diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..586c5fb7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,270 @@ +root = true + +[*] +roslynator_accessibility_modifiers = explicit +roslynator_use_anonymous_function_or_method_group = anonymous_function|method_group +roslynator_enum_has_flag_style = method +roslynator_object_creation_type_style = explicit|implicit|implicit_when_type_is_obvious + +indent_style = space + +trim_trailing_whitespace = true + +insert_final_newline = false + +[*.md] +trim_trailing_whitespace = false + +[*.json] +indent_size = 2 + +[*.cs] +dotnet_sort_system_directives_first = true:warning + +csharp_style_namespace_declarations = file_scoped:warning + +csharp_style_var_for_built_in_types = false:warning + +csharp_style_var_when_type_is_apparent = true:warning + +csharp_style_var_elsewhere = true:warning + +csharp_new_line_before_members_in_anonymous_types = true:warning + +# SA1623: Property summary documentation should match accessors +dotnet_diagnostic.SA1623.severity = none + +# SA1101: Prefix local calls with this +dotnet_diagnostic.SA1101.severity = none + +# SA1642: Constructor summary documentation should begin with standard text +dotnet_diagnostic.SA1642.severity = none + +# SA1309: Field names should not begin with underscore +dotnet_diagnostic.SA1309.severity = none + +# RCS1194: Implement exception constructors. +dotnet_diagnostic.RCS1194.severity = none + +# SA1000: Keywords should be spaced correctly +dotnet_diagnostic.SA1000.severity = none + +# SA1124: Do not use regions +dotnet_diagnostic.SA1124.severity = none + +# SA1413: Use trailing comma in multi-line initializers +dotnet_diagnostic.SA1413.severity = none + +# SA1201: Elements should appear in the correct order +dotnet_diagnostic.SA1201.severity = suggestion + +# SA1638: File header file name documentation should match file name +dotnet_diagnostic.SA1638.severity = warning + +# SA1633: File should have header +dotnet_diagnostic.SA1633.severity = none + +# SA1404: Code analysis suppression should have justification +dotnet_diagnostic.SA1404.severity = none + +# SA1206: Declaration keywords should follow order +dotnet_diagnostic.SA1206.severity = none + +# CA1040: Avoid empty interfaces +dotnet_diagnostic.CA1040.severity = none + +# RCS1012: Use explicit type instead of 'var' +dotnet_diagnostic.RCS1012.severity = none + +# RCS1008: Use explicit type instead of 'var' +dotnet_diagnostic.RCS1008.severity = none + +# CA1725 +dotnet_diagnostic.CA1725.severity = none + +# RCS1009: Use explicit type instead of 'var' +dotnet_diagnostic.RCS1009.severity = none + +# SA1402: File may only contain a single type +dotnet_diagnostic.SA1402.severity = suggestion + +# CA1711 +dotnet_diagnostic.CA1711.severity = none + +# CA1720: Identifier contains type name +dotnet_diagnostic.CA1720.severity = none + +# IDE0022: Use block body for methods +dotnet_diagnostic.IDE0022.severity = none + +# SA1011: Closing square brackets should be spaced correctly +dotnet_diagnostic.SA1011.severity = none + +# CA1721 +dotnet_diagnostic.CA1721.severity = none + +# SA1313: Parameter names should begin with lower-case letter +dotnet_diagnostic.SA1313.severity = none + +# SecurityIntelliSenseCS: MS Security rules violation +dotnet_diagnostic.SecurityIntelliSenseCS.severity = suggestion + +# SA1123: Do not place regions within elements +dotnet_diagnostic.SA1123.severity = none + +# RCS1046: Add suffix 'Async' to asynchronous method name +dotnet_diagnostic.RCS1046.severity = warning + +# SA1625: Element documentation should not be copied and pasted +dotnet_diagnostic.SA1625.severity = none + +# SCS9999 +dotnet_diagnostic.SCS9999.severity = none + +# RCS1090 Add call to 'ConfigureAwait' +dotnet_diagnostic.RCS1090.severity = none + +# RCS1170 Use read-only auto-implemented property +dotnet_diagnostic.RCS1170.severity = none + +# SA1649 +dotnet_diagnostic.SA1649.severity = none + +# RCS1021 Use expression-bodied lambda. +dotnet_diagnostic.RCS1021.severity = none + +# RCS1047 Remove suffix 'Async' from non-asynchronous method name. +# dotnet_diagnostic.RCS1047.severity = silent + +# SA1600 Elements should be documented +dotnet_diagnostic.SA1600.severity = silent + +# CS1591 Missing XML comment for publicly visible type or member +dotnet_diagnostic.CS1591.severity = none + +# SA1602: Enumeration items should be documented +dotnet_diagnostic.SA1602.severity = none + +# CA1720 Identifier 'Decimal' contains type name +dotnet_diagnostic.CA1720.severity = none + +# SA1601: Partial elements should be documented +dotnet_diagnostic.SA1601.severity = silent + +# CA1711 Rename type name UserEventHandler so that it does not end in 'EventHandler' +dotnet_diagnostic.CA1711.severity = none + +# CA1307: Specify StringComparison for clarity +dotnet_diagnostic.CA1307.severity = none +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_indent_labels = one_less_than_current +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_style_prefer_readonly_struct = true:suggestion +csharp_prefer_static_local_function = true:suggestion +csharp_style_prefer_readonly_struct_member = true:suggestion +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent +csharp_style_conditional_delegate_call = true:suggestion +csharp_space_around_binary_operators = before_and_after +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_prefer_collection_expression = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_readonly_field = true:suggestion +dotnet_style_predefined_type_for_member_access = true:silent +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_allow_statement_immediately_after_block_experimental = true:silent +dotnet_style_allow_multiple_blank_lines_experimental = true:silent +dotnet_code_quality_unused_parameters = all:suggestion diff --git a/templates/template/.env b/.env similarity index 93% rename from templates/template/.env rename to .env index a76ec7d7..d6d68734 100644 --- a/templates/template/.env +++ b/.env @@ -5,3 +5,6 @@ # The IP below should be swapped to your real IP or DNS name, like 192.168.88.248, etc. if testing from remote browsers or mobile devices PROJECT_EXTERNAL_DNS_NAME_OR_IP=localhost + +# The docker image version +DOCKER_IMAGE_TAG=6.0.0 diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..7ab2dabb --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms +open_collective: genocs +custom: ["https://www.buymeacoffee.com/genocs"] diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..c982b5fe --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "nuget" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" # How often to check for updates (can be "daily", "weekly" or "monthly") diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index ce398d56..f18640fa 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -2,10 +2,9 @@ name: Build test and pack on: push: - branches: ["master"] - + branches: [main, develop] pull_request: - branches: ["master"] + branches: [main, develop] jobs: build: @@ -14,21 +13,21 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup .NET - uses: actions/setup-dotnet@v2 + uses: actions/setup-dotnet@v4 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Restore dependencies run: dotnet restore - name: Build - run: dotnet build --no-restore + run: dotnet build -c Debug --no-restore - name: Test - run: dotnet test --no-build --verbosity normal + run: dotnet test -c Debug --no-build --verbosity normal - name: Pack - run: dotnet pack --no-build --verbosity normal + run: dotnet pack -c Debug --no-build --verbosity normal diff --git a/.github/workflows/dockerhub-publish.yml b/.github/workflows/dockerhub-publish.yml new file mode 100644 index 00000000..e8d987f2 --- /dev/null +++ b/.github/workflows/dockerhub-publish.yml @@ -0,0 +1,57 @@ +# This is a manually triggered dockerhub build and publish + +name: Publish to Dockerhub + +# Controls when the action will run. Workflow runs when manually triggered using the UI +# or API. +on: + workflow_dispatch: + # Inputs the workflow accepts. + inputs: + version: + # Friendly description to be shown in the UI instead of 'name' + description: "Image Version" + + # Default value if no value is explicitly provided + default: "6.2.0" + + # Input has to be provided for the workflow to run + required: true + +jobs: + build: + name: Publish Docker Image + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build -c Debug --no-restore + + - name: Test + run: dotnet test --no-build --verbosity normal + + - name: Pack + run: dotnet pack --no-build --verbosity normal + + - name: Docker Hub Log in + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and push image to Dockerhub + run: | + docker build -f webapi.dockerfile -t genocs/demo-webapi:${{ github.event.inputs.version }} -t genocs/demo-webapi:latest . + docker push genocs/demo-webapi:${{ github.event.inputs.version }} + docker push genocs/demo-webapi:latest diff --git a/.github/workflows/nuget-publish.yml b/.github/workflows/nuget-publish.yml index 89c68139..7e70f358 100644 --- a/.github/workflows/nuget-publish.yml +++ b/.github/workflows/nuget-publish.yml @@ -13,24 +13,24 @@ on: description: "Packages Version" # Default value if no value is explicitly provided - default: "5.0.0-preview.4.0" + default: "6.2.0" # Input has to be provided for the workflow to run required: true - jobs: build: name: Update NuGet packages runs-on: ubuntu-latest + steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup .NET - uses: actions/setup-dotnet@v2 + uses: actions/setup-dotnet@v4 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Restore dependencies run: dotnet restore @@ -42,7 +42,7 @@ jobs: run: dotnet test --no-build --verbosity normal - name: Pack - run: dotnet pack -c Debug -o out -p:Version=${{ github.event.inputs.version }} --no-build --verbosity normal + run: dotnet pack -c Debug -o out -p:PackageVersion=${{ github.event.inputs.version }} --no-build --verbosity normal - name: Push packages to Nuget - run: dotnet nuget push ./out/*.nupkg -k ${{secrets.NUGET_API_KEY}} -s https://api.nuget.org/v3/index.json --skip-duplicate --no-symbols + run: dotnet nuget push ./out/*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate --no-symbols diff --git a/.gitignore b/.gitignore index c1aafb51..65f178a7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files +*.rsuser *.suo *.user *.userosscache @@ -12,6 +13,9 @@ # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs +# Mono auto generated files +mono_crash.* + # Build results [Dd]ebug/ [Dd]ebugPublic/ @@ -19,10 +23,14 @@ [Rr]eleases/ x64/ x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ +[Ll]ogs/ # Visual Studio 2015/2017 cache/options directory .vs/ @@ -36,9 +44,10 @@ Generated\ Files/ [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* -# NUNIT +# NUnit *.VisualState.xml TestResult.xml +nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ @@ -54,6 +63,12 @@ project.fragment.lock.json artifacts/ # **/Properties/launchSettings.json +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + # StyleCop StyleCopReport.xml @@ -61,6 +76,7 @@ StyleCopReport.xml *_i.c *_p.c *_i.h +*_h.h *.ilk *.meta *.obj @@ -77,6 +93,7 @@ StyleCopReport.xml *.tlh *.tmp *.tmp_proj +*_wpftmp.csproj *.log *.vspscc *.vssscc @@ -132,6 +149,11 @@ _TeamCity* .axoCover/* !.axoCover/settings.json +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + # Visual Studio code coverage results *.coverage *.coveragexml @@ -179,6 +201,8 @@ PublishScripts/ # NuGet Packages *.nupkg +# NuGet Symbol Packages +*.snupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. @@ -203,12 +227,14 @@ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx +*.appxbundle +*.appxupload # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache -!*.[Cc]ache/ +!?*.[Cc]ache/ # Others ClientBin/ @@ -217,10 +243,11 @@ ClientBin/ *.dbmdl *.dbproj.schemaview *.jfm +*.pfx *.publishsettings orleans.codegen.cs -# Including strong name files can present a security risk +# Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk @@ -251,6 +278,9 @@ ServiceFabricBackup/ *.bim.layout *.bim_*.settings *.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ @@ -293,6 +323,9 @@ paket-files/ # CodeRush .cr/ +# CodeRush personal settings +.cr/personal + # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc @@ -316,7 +349,7 @@ __pycache__/ # OpenCover UI analysis results OpenCover/ -# Azure Stream Analytics local run output +# Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log @@ -325,7 +358,111 @@ ASALocalRun/ # NVidia Nsight GPU debugger configuration file *.nvuser -# MFractors (Xamarin productivity tool) working folder +# MFractors (Xamarin productivity tool) working folder .mfractor/ -logs/ \ No newline at end of file +logs/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# JetBrains Rider +.idea/ +*.sln.iml + +## +## Visual Studio Code +## +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json diff --git a/.travis.yml b/.travis.yml index 6df0ef1a..8cc7e1c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,10 +2,10 @@ language: csharp mono: none sudo: required dist: xenial -dotnet: 7.0.100 +dotnet: 8.0.0 branches: only: - - master + - main #- develop before_script: - chmod -R a+x scripts diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..3c87ede3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,377 @@ +### Changelog + +All notable changes to this project will be documented in this file. Dates are displayed in UTC. + +Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). + +#### [v6.4.0](https://github.com/Genocs/genocs-library/compare/v6.3.0...v6.4.0) + +> 10 November 2024 + +- Refactor MongoDB repository structure and base classes [`98e24b1`](https://github.com/Genocs/genocs-library/commit/98e24b1aab96ca4276924a822e0b5ffec50c3e9b) +- Update package references to version 6.4.0 across multiple projects [`6fdf470`](https://github.com/Genocs/genocs-library/commit/6fdf470b2234bf8a08e70deb251bef4f2cbd4edc) +- Refactor: Replace IIdentifiable with IEntity [`a21e330`](https://github.com/Genocs/genocs-library/commit/a21e33004ff115b383556835096ee02bd94c9f0f) +- Update packages, refactor code, and improve readability [`0a2d5be`](https://github.com/Genocs/genocs-library/commit/0a2d5be49e6e3d28e56da01aea2b72c21fa83568) +- Remove project refs and add using directive [`a5f189b`](https://github.com/Genocs/genocs-library/commit/a5f189be8c47d0294ca93dea5804a11b67bcc84d) +- Update to version 6.3.0 and refactor codebase [`713fb84`](https://github.com/Genocs/genocs-library/commit/713fb847400afccc205061d854db1ee22159d62d) +- Standardize property names and update audit fields [`c10e995`](https://github.com/Genocs/genocs-library/commit/c10e995b229fa8958e7f0585679562c711a56c22) +- Refactor MongoDB repository structure and update usings [`c3a8bac`](https://github.com/Genocs/genocs-library/commit/c3a8baca5d49ee7999be1e59384fea5c8b2d36d2) +- Refactor AzureInitializer and update MongoDB.Driver [`42dfe53`](https://github.com/Genocs/genocs-library/commit/42dfe53d120dbd53fe8018bdd418f1d2d9208c0f) +- Refactor null checks and exception handling [`7da092d`](https://github.com/Genocs/genocs-library/commit/7da092d0fecec393297dac85cf0a11e6fe6df95c) +- Update CHANGELOG for v6.3.0 release [`2e5eb47`](https://github.com/Genocs/genocs-library/commit/2e5eb47693439d55f9974402bab3f923993f117c) +- Update package versions in multiple project files [`106fc31`](https://github.com/Genocs/genocs-library/commit/106fc319c0411d90a6499bb4477b6f80cbe585f2) +- Refactor Entity and IAggregateRoot for simplicity [`3ccf665`](https://github.com/Genocs/genocs-library/commit/3ccf665db0c7e8eaceb50fa13d14c1ff4faa22e6) + +#### [v6.3.0](https://github.com/Genocs/genocs-library/compare/v6.2.0...v6.3.0) + +> 3 November 2024 + +- Update project files and package references [`34415fa`](https://github.com/Genocs/genocs-library/commit/34415fa90e252c2654ddeebf5a8f84789d29be20) +- Update Genocs packages from 6.2.0 to 6.3.0 [`950efdb`](https://github.com/Genocs/genocs-library/commit/950efdb79522da4661f8fba43d020d07e4a6d927) +- Update package versions to 6.2.0 in multiple projects [`328f67e`](https://github.com/Genocs/genocs-library/commit/328f67e3a0f1ceb1be16be1d7eff0c679c5e5a8e) +- Update project references and improve domain entities [`24589b4`](https://github.com/Genocs/genocs-library/commit/24589b4a5cc52bb99609c3089b2e366343f5688e) +- Update Roslynator.Analyzers and adjust framework references [`6e9af26`](https://github.com/Genocs/genocs-library/commit/6e9af26c48bad5298f20430eca65f06b0a2c99f4) +- Update to 6.2.0, replace Jaeger with OpenTelemetry [`ee78547`](https://github.com/Genocs/genocs-library/commit/ee785472f89c00bb5e53ad066b9d7b8b2588fffa) +- Update packages, enhance JWT options, and refactor configs [`ded3efb`](https://github.com/Genocs/genocs-library/commit/ded3efb09cdb4b9b42b5d36102cf45bea608e84c) + +#### [v6.2.0](https://github.com/Genocs/genocs-library/compare/v6.1.0...v6.2.0) + +> 22 October 2024 + +- Refactor and enhance OpenAPI and middleware handling [`4bbbe88`](https://github.com/Genocs/genocs-library/commit/4bbbe8811151bcefa73254f6f2fd4eba21409c2d) +- Update package versions in workflows and projects [`5909e0b`](https://github.com/Genocs/genocs-library/commit/5909e0b1a03d51817816cf4c1944e5fc93af32f9) +- Update package versions to 6.2.0 in Genocs.HTTP, Genocs.Core, Genocs.Metrics, Genocs.WebApi, Genocs.Security, Genocs.Logging, Genocs.Secrets.Vault, Genocs.MessageBrokers, Genocs.WebApi.Swagger, Genocs.ServiceBusAzure, Genocs.Persistence.Redis, Genocs.Core.Demo.Contracts, Genocs.Persistence.MongoDb, Genocs.WebApi.Security, Genocs.Secrets.AzureKeyVault, Genocs.MessageBrokers.Outbox, Genocs.MessageBrokers.RabbitMQ, Genocs.Auth, Genocs.WebApi.CQRS, Genocs.Discovery.Consul, Genocs.Persistence.MongoDb.UnitTests, and Genocs.Core.Demo.Domain projects [`7a08e96`](https://github.com/Genocs/genocs-library/commit/7a08e96d7625a3aea775a325e20bc117fbb74420) +- Add detailed Swagger configuration and options [`3ea5a05`](https://github.com/Genocs/genocs-library/commit/3ea5a05522ec98715802cd24ed1c660acd89588a) +- Enhance Azure setup, null safety, and code quality [`21f2798`](https://github.com/Genocs/genocs-library/commit/21f27983546f3757d957c03dfef8394df95388d3) +- Refactor: Replace Jaeger with OpenTelemetry [`4e1a3ba`](https://github.com/Genocs/genocs-library/commit/4e1a3ba54d5872421849773efe6aecfed5a1c241) +- Update Roslynator, improve type clarity, and reformat code [`52b5851`](https://github.com/Genocs/genocs-library/commit/52b5851706ca1a0c14849f6ffc398def10b58c3b) + +#### [v6.1.0](https://github.com/Genocs/genocs-library/compare/v6.0.0...v6.1.0) + +> 11 October 2024 + +- Refactor interfaces, update Azure Key Vault options, and improve code readability [`485db2c`](https://github.com/Genocs/genocs-library/commit/485db2ca1dd13e87a785a2242248671858fb7e45) +- Refactor: Replace Jaeger with OpenTelemetry [`1eed44b`](https://github.com/Genocs/genocs-library/commit/1eed44bd91c9b187bcc1a2d3983ebe52b20e0664) +- Refactor interfaces and update Azure Key Vault options [`87edca1`](https://github.com/Genocs/genocs-library/commit/87edca186850db3cbe17398229335012308fb814) +- **List of code changes:** [`01adb18`](https://github.com/Genocs/genocs-library/commit/01adb188466e53010932b58c2475cef69afeecc1) +- Update package versions from 6.0.* to 6.1.* [`088a59a`](https://github.com/Genocs/genocs-library/commit/088a59adc0da7fcb3676a9e08f43a3187ba4d9df) +- Make properties nullable and improve code readability [`6e091e5`](https://github.com/Genocs/genocs-library/commit/6e091e5fa20f0a2d1dda34468675d6fbe787792c) +- Refactor interfaces, update Azure Key Vault options, and improve code readability [`866463f`](https://github.com/Genocs/genocs-library/commit/866463f0f4abf764b667774553301a417733474d) +- Update packages and enhance Azure Key Vault integration [`3b45172`](https://github.com/Genocs/genocs-library/commit/3b45172b945ad7df08a11021c4ef738bad715f33) +- Remove CardToken, update configs, and add Oracle setup [`c1095a7`](https://github.com/Genocs/genocs-library/commit/c1095a76c9755976f829e77ea4051e2b84ef948a) +- Update various NuGet packages across multiple projects [`a1e01bb`](https://github.com/Genocs/genocs-library/commit/a1e01bb0634de8d38a869fb0b75ed530cd42f1db) +- Refactor and update dependencies, fix typos, and improve code formatting [`318ca61`](https://github.com/Genocs/genocs-library/commit/318ca61f46bb69fd29b13346576373d4d0cedd2c) +- Refactor README.md and remove unnecessary lines [`ab02bd7`](https://github.com/Genocs/genocs-library/commit/ab02bd7d529538eee6cf60a2926b6b889853487d) + +### [v6.0.0](https://github.com/Genocs/genocs-library/compare/6.0.0-preview.2.0...v6.0.0) + +> 31 July 2024 + +- Refactor and update dependencies and logging level [`63533a2`](https://github.com/Genocs/genocs-library/commit/63533a2ccfb64106dd81e296ca1a1de55c556f48) +- Refactor auth and update docs [`a63d816`](https://github.com/Genocs/genocs-library/commit/a63d816a4b617246ec99b3400d019a1b46ff40b0) +- Update packages, fix typos, and improve code formatting [`d634694`](https://github.com/Genocs/genocs-library/commit/d6346942c88195b831bd66f4ff8a1e039695e0b2) +- Update various NuGet packages across multiple projects [`c61eab2`](https://github.com/Genocs/genocs-library/commit/c61eab254c53920f94c575503c41c4e623fd1b36) + +#### [6.0.0-preview.2.0](https://github.com/Genocs/genocs-library/compare/v5.0.0-preview.4.0...6.0.0-preview.2.0) + +> 19 July 2024 + +- Bump prometheus-net from 5.0.2 to 8.2.1 [`#54`](https://github.com/Genocs/genocs-library/pull/54) +- Bump System.IdentityModel.Tokens.Jwt from 7.0.3 to 7.1.2 in /src/Genocs.Auth [`#56`](https://github.com/Genocs/genocs-library/pull/56) +- Bump Polly from 8.2.0 to 8.2.1 [`#55`](https://github.com/Genocs/genocs-library/pull/55) +- Bump Microsoft.VisualStudio.Azure.Containers.Tools.Targets from 1.19.5 to 1.19.6 [`#53`](https://github.com/Genocs/genocs-library/pull/53) +- Bump Moq from 4.20.69 to 4.20.70 [`#52`](https://github.com/Genocs/genocs-library/pull/52) +- Net8 [`#35`](https://github.com/Genocs/genocs-library/pull/35) +- Bump Serilog.Sinks.Seq from 5.1.0 to 5.2.3 [`#21`](https://github.com/Genocs/genocs-library/pull/21) +- Bump Serilog.Sinks.ElasticSearch from 8.4.1 to 9.0.3 [`#18`](https://github.com/Genocs/genocs-library/pull/18) +- Added documentation and more control over types [`3d4c17e`](https://github.com/Genocs/genocs-library/commit/3d4c17e0e501d9174160f01569d2523472085a04) +- Refactor configs and update docs across projects [`504fc40`](https://github.com/Genocs/genocs-library/commit/504fc40ee05a1ed6bc838a5b08195cb285feb8c1) +- Merge issues [`cc6c097`](https://github.com/Genocs/genocs-library/commit/cc6c097d28854b584d3df34f9dc3ea6c4be78db9) +- Updated Nuget Packages [`9ff8abb`](https://github.com/Genocs/genocs-library/commit/9ff8abbd28df412a5e8e34b4d6e5fc26bda2046b) +- Sincronize naming conventions [`7bae2b7`](https://github.com/Genocs/genocs-library/commit/7bae2b7496200b36a9f0f0c4f0dadf60b74c3421) +- Updated packages [`9f04ab5`](https://github.com/Genocs/genocs-library/commit/9f04ab59052556b3f27aa3b377b08ed2b3e5e4b8) +- Documentation, updated packages [`3d49180`](https://github.com/Genocs/genocs-library/commit/3d49180fd5bfa4d07f7297b852bee83f2b000849) +- Refactor config naming and add new options [`5977b8c`](https://github.com/Genocs/genocs-library/commit/5977b8c49236a78a209519de00deb6e9865d7b57) +- Updated comments, attributes, methods, and dependencies across multiple files [`ccaf4a5`](https://github.com/Genocs/genocs-library/commit/ccaf4a583373155be0186994deea15dcdabfbade) +- Updated project dependencies and refactored security settings [`cc6bd4d`](https://github.com/Genocs/genocs-library/commit/cc6bd4df16be8fc76e4f7f68359cb1e6c1ae430e) +- Cleanup data after uniform naming conventions [`34b35b2`](https://github.com/Genocs/genocs-library/commit/34b35b2c4979c95b957f144cf7e680d16fca450d) +- Refactor auth/config handling and enhance security [`bb5ea59`](https://github.com/Genocs/genocs-library/commit/bb5ea5989bdbe0bfa756e3b00b1a15a3e12f3942) +- Refactor and enhance JWT handling and security [`b864cc9`](https://github.com/Genocs/genocs-library/commit/b864cc9693111b9d60881a6643e5b16638c15894) +- Refactor and enhance JWT handling and security [`736964d`](https://github.com/Genocs/genocs-library/commit/736964d22cb1715c3c1b0455647077a5797c99ad) +- Integrate Azure Key Vault & Update Packages [`bd2a025`](https://github.com/Genocs/genocs-library/commit/bd2a025418302d032c93219388bc542c9c4b9409) +- Updated multiple packages and improved code readability [`8b9b1b5`](https://github.com/Genocs/genocs-library/commit/8b9b1b58fe6d618f0621e2412934fa94e412bdf5) +- Refactor and enhance codebase for clarity [`227e968`](https://github.com/Genocs/genocs-library/commit/227e9686af3c2f0c036c1546202e3a9d5b998a96) +- Add verification service client and models [`3b27e46`](https://github.com/Genocs/genocs-library/commit/3b27e463fa91d72fe87473c7d6918114896433cd) +- Updated project structure [`9ffe861`](https://github.com/Genocs/genocs-library/commit/9ffe8612bf90808a2584c3a62feb1128be2d65de) +- Fix [`0af67b8`](https://github.com/Genocs/genocs-library/commit/0af67b813a1c7d70d5a7b5f0d3fc8fdf5e7071da) +- Refactor code for improved readability and update variable types [`762c51f`](https://github.com/Genocs/genocs-library/commit/762c51f5b8d38ede287938fde6f0f7638eb33f75) +- Refactor auth and update docs [`06bb32f`](https://github.com/Genocs/genocs-library/commit/06bb32ff452e1e32e94dc5554aa38c7bf7ce5ec8) +- Minor fix [`9ad6741`](https://github.com/Genocs/genocs-library/commit/9ad67419409742bcad1f383a1f9f5219f9ccfb7a) +- Updated nuget packages to prepare for removing legacy behavior [`3887c17`](https://github.com/Genocs/genocs-library/commit/3887c171b97543ee47e8e182e63964e6b5f8667e) +- Reverted reference framework [`99ac637`](https://github.com/Genocs/genocs-library/commit/99ac6373f8774c73de507419425b9840928f5fde) +- Refactored code for readability and updated variable types [`cc0d478`](https://github.com/Genocs/genocs-library/commit/cc0d47898ac8f705e9cb17837eb5f272d6a77bc2) +- Update app config for logging, security, and more [`1058ea1`](https://github.com/Genocs/genocs-library/commit/1058ea157068e99bb9e08dbe71350d574d9b8d7f) +- Enhanced app config and documentation [`bf42a88`](https://github.com/Genocs/genocs-library/commit/bf42a88da74ce68b2aaef9c3463ac698cc01a145) +- Setup configuration [`02c2be7`](https://github.com/Genocs/genocs-library/commit/02c2be7978110755596ff81f95f108b6b8c61f0b) +- Update dependencies and refactor namespaces [`0cde64f`](https://github.com/Genocs/genocs-library/commit/0cde64fba30a987d5be4062d6ab167945629b8bc) +- updates for 2024 [`c58e4a2`](https://github.com/Genocs/genocs-library/commit/c58e4a2be076f22bec1dcad16a4f4a05a8865203) +- Update package versions across projects [`bb92cdb`](https://github.com/Genocs/genocs-library/commit/bb92cdbb7688fa5f33c4660729b6b589c41f3e06) +- Refactor auth and update docs [`85198fb`](https://github.com/Genocs/genocs-library/commit/85198fbd504da2e98499783f0d85eb6fcccab91d) +- Refactor configuration naming conventions and add new options [`ede9ad3`](https://github.com/Genocs/genocs-library/commit/ede9ad3256c1ad948b89dddc789d6734ae2eed1a) +- Merge issues [`94db485`](https://github.com/Genocs/genocs-library/commit/94db485eaf5a5732aeb300f9b1ab701835b3a256) +- removed templates and updated packages [`aa54284`](https://github.com/Genocs/genocs-library/commit/aa542848efc453e7f298f5dd02e466d5a42c7791) +- File updates [`73afdc0`](https://github.com/Genocs/genocs-library/commit/73afdc09e3412194d31a462ce3d63d7116ebbd68) +- Minor fix [`2dde44c`](https://github.com/Genocs/genocs-library/commit/2dde44c057ee340bb9aebceb367f2d9cd3b084d7) +- Version pre5.x [`7ba35a0`](https://github.com/Genocs/genocs-library/commit/7ba35a0d172d3699e58da473f41c408ee0267376) +- Clean warnings [`7c66fc3`](https://github.com/Genocs/genocs-library/commit/7c66fc3f57bc60bf44f35e391f80a5855baba357) +- Cleanup config file [`5a3ab6d`](https://github.com/Genocs/genocs-library/commit/5a3ab6d535046b6f35d40f682c6e3fa371905db4) +- work in progress setup docker [`19e285e`](https://github.com/Genocs/genocs-library/commit/19e285e52b3a59cb9e6ea50414edc5fb33326df2) +- Added comments [`7661260`](https://github.com/Genocs/genocs-library/commit/7661260ebc31ee35c5cd600b238422c281fd7e1e) +- Updates [`eba8b20`](https://github.com/Genocs/genocs-library/commit/eba8b208039220656cb3d4cf32ac285dda13fb79) +- Updates change log [`08f7ea0`](https://github.com/Genocs/genocs-library/commit/08f7ea0ef2212d92769c1aad6fd0af05c673e168) +- Fix error on Console log [`f6da253`](https://github.com/Genocs/genocs-library/commit/f6da2539bd7249928d3221f0a3af95ab5dd513ef) +- Updated Release note [`acf52b8`](https://github.com/Genocs/genocs-library/commit/acf52b8b636de43ebb91bb2ab1b4a234d2e51092) +- Added wildcard [`bb303ea`](https://github.com/Genocs/genocs-library/commit/bb303eae19f13d330e0bc14c864e6e2fca4e001b) +- Implemented Mongo functionality [`3bb4589`](https://github.com/Genocs/genocs-library/commit/3bb4589e3596ca7cb1aed17129a3aab2ef5b9dd5) +- Updated packages [`9f2cedc`](https://github.com/Genocs/genocs-library/commit/9f2cedca131300bcc01183f936ef7d0a77599cb8) +- Refactory open telemetry [`a3170e5`](https://github.com/Genocs/genocs-library/commit/a3170e5c1d1b5fd56cc0b65e3663dfaa3cb1e7b9) +- Changed the docker compose config file [`1444175`](https://github.com/Genocs/genocs-library/commit/1444175abb79ab834bd69dcbac9395829332c3ff) +- Fix configuration on docker without Fabio and Consul [`363ec41`](https://github.com/Genocs/genocs-library/commit/363ec41ebdc5d0f7c17a935226504c37d53b50cd) +- Before big refactory [`b21a55e`](https://github.com/Genocs/genocs-library/commit/b21a55ec751c8186b73e7da84bd1a38256ff7984) +- Fix link [`56b18a2`](https://github.com/Genocs/genocs-library/commit/56b18a25652e30f383b6c137a168487a3827cd5e) +- Fix auth and authorization [`5b7fc8c`](https://github.com/Genocs/genocs-library/commit/5b7fc8cebf25b33ee7f7f676a74698aee328ca7d) +- Using Open telemetry [`09a5783`](https://github.com/Genocs/genocs-library/commit/09a57836398b28ba07afbbadcf211181e1b86829) +- Pagages updated wip [`dd10e1a`](https://github.com/Genocs/genocs-library/commit/dd10e1afa7a1cf6f5c7b1780413fff9ae9cd0775) +- Second round [`8d92e64`](https://github.com/Genocs/genocs-library/commit/8d92e641cd7f3ca2cc658c36739d1253e9bc5525) +- Default mongodb entity with ObjectId [`296abe7`](https://github.com/Genocs/genocs-library/commit/296abe7757f025b2d75313e423d9f380f758a160) +- updated docker files and github actions [`a7dd458`](https://github.com/Genocs/genocs-library/commit/a7dd4583ac302f1841d2b1c152849deb761f3cc2) +- updated readme [`ccb4614`](https://github.com/Genocs/genocs-library/commit/ccb46140e499cb1cac8ab129e710a9bffe0034e6) +- Updated clients to net8 [`8f32845`](https://github.com/Genocs/genocs-library/commit/8f328458cfdc28303e78f1d87d11e98a9f348280) +- Fix workflow [`6f527c7`](https://github.com/Genocs/genocs-library/commit/6f527c7324c6097ca287a3c1a1bcff592af081ea) +- tmp [`7688a62`](https://github.com/Genocs/genocs-library/commit/7688a627e67555c0a5e5578e489b3ad45810e9bb) +- Added default assembly name [`8293914`](https://github.com/Genocs/genocs-library/commit/8293914cac430f3b15921f6aa8de64619b1aac19) +- Commit after the check with Products and Oerders by Postman [`5f37139`](https://github.com/Genocs/genocs-library/commit/5f37139b8d8eb4f5e5ce7cb6c4d8d2b147ce106e) +- minor fix [`b5a32dd`](https://github.com/Genocs/genocs-library/commit/b5a32dd6c5256892c73f086b9686f953e6ced46a) +- refactory [`b94aa66`](https://github.com/Genocs/genocs-library/commit/b94aa66fc60fc0d5b90fa799b95ea193bb74fe42) +- Added auth features and AuthorizedController [`3a28fd1`](https://github.com/Genocs/genocs-library/commit/3a28fd1bf3f42c3de94cdd58ae002eb91b14fe2c) +- fix prometheus [`3ba5662`](https://github.com/Genocs/genocs-library/commit/3ba56621b9ff9882902d8908c3e69d23cb840fa7) +- Fix for pack [`19f2e02`](https://github.com/Genocs/genocs-library/commit/19f2e027fd0dbab7871171e6af05be8d5e10b88e) +- Updated github action [`7b8bf5d`](https://github.com/Genocs/genocs-library/commit/7b8bf5dfb9d5bdb0c8244a52239af7707c290862) +- Updated references [`28920e4`](https://github.com/Genocs/genocs-library/commit/28920e4c8411a90d6eb57c24f3c655715b2160c3) +- Disabled Monitoring [`a8bad21`](https://github.com/Genocs/genocs-library/commit/a8bad21f6c199b726de89167f740bac1c85fcd6f) +- Fix build [`8c0bdf3`](https://github.com/Genocs/genocs-library/commit/8c0bdf374e38b543bc6398d0742ad3dab66c95b7) +- Updated log plus fix url service [`24aff9b`](https://github.com/Genocs/genocs-library/commit/24aff9b130c36c90d73a13f70e3047f42a128a76) +- Bump System.IdentityModel.Tokens.Jwt in /src/Genocs.Auth [`cd58c28`](https://github.com/Genocs/genocs-library/commit/cd58c28d68e059fb6fb83db7d34debcdee5ee1b4) +- Bump Microsoft.VisualStudio.Azure.Containers.Tools.Targets [`d620e5d`](https://github.com/Genocs/genocs-library/commit/d620e5d6d9a9d4a5abac0dde8ad3aeef3e2c42a6) +- Fix path [`31a62e9`](https://github.com/Genocs/genocs-library/commit/31a62e9f428866042f6191bcf99c411dda0368b6) + +#### [v5.0.0-preview.4.0](https://github.com/Genocs/genocs-library/compare/v5.0.0-preview.3.0...v5.0.0-preview.4.0) + +> 15 July 2023 + +- April 2023 [`#17`](https://github.com/Genocs/genocs-library/pull/17) +- Create ml.yml [`#14`](https://github.com/Genocs/genocs-library/pull/14) +- April 2023 [`#12`](https://github.com/Genocs/genocs-library/pull/12) +- April 2023 [`#11`](https://github.com/Genocs/genocs-library/pull/11) +- Preview 4.x [`d619840`](https://github.com/Genocs/genocs-library/commit/d619840033c146a60bdb5fe6f37ea7e6e7ae5de7) +- Start removing legacy MongoDB legacy database [`f4ca960`](https://github.com/Genocs/genocs-library/commit/f4ca9609ef11ad550c789e05dbfd6c0984fa312b) +- Refactory to remove legacy Mongodb [`7accdcb`](https://github.com/Genocs/genocs-library/commit/7accdcba26edb9e38ebf0583966348f80a2d31b1) +- refactory template [`78a2731`](https://github.com/Genocs/genocs-library/commit/78a27316ce9a4341af3c1c21b2e9543bf8cf0af3) +- Added Mongo Encriptation [`a6e2157`](https://github.com/Genocs/genocs-library/commit/a6e2157a9e60c6da11623e70f41657b8caa78cf3) +- Refactory Persistance [`d27adce`](https://github.com/Genocs/genocs-library/commit/d27adce74254630bca2d0da46fec17c94372848b) +- Fix application [`8c10982`](https://github.com/Genocs/genocs-library/commit/8c10982c42a0d176b045ea6ce22a37ff95f8abda) +- Fix templates [`55c8d85`](https://github.com/Genocs/genocs-library/commit/55c8d8505fa33f06cfb02814734464adf384eb22) +- Removed legacy references [`a32d92b`](https://github.com/Genocs/genocs-library/commit/a32d92b6f68c50a0638912b52a250338db59de7c) +- Fix error on unit test [`b2f5fdd`](https://github.com/Genocs/genocs-library/commit/b2f5fdd6069bb6276fc29af312616704ac48f27a) +- Paginated query [`ddb108e`](https://github.com/Genocs/genocs-library/commit/ddb108e6ca4d391f5a7c4b515831cddf083ec82f) +- Updated packages [`f030660`](https://github.com/Genocs/genocs-library/commit/f03066034868d3cebb38667425e63b6d8f55b3da) +- updates [`48b67fb`](https://github.com/Genocs/genocs-library/commit/48b67fba3bc2e2ee72d008734629b31547275d7b) +- Updated github workflow [`8b689df`](https://github.com/Genocs/genocs-library/commit/8b689dfe7adb50f6378ed9a970ec6b9412e0a7f0) + +#### v5.0.0-preview.3.0 + +> 13 May 2023 + +- March 2023 [`#9`](https://github.com/Genocs/genocs-library/pull/9) +- Create docker-image.yml [`#6`](https://github.com/Genocs/genocs-library/pull/6) +- Create nuget-deploy.yml [`#5`](https://github.com/Genocs/genocs-library/pull/5) +- Added explicitly build [`#3`](https://github.com/Genocs/genocs-library/pull/3) +- Develop [`#2`](https://github.com/Genocs/genocs-library/pull/2) +- Bump Microsoft.AspNetCore.Authentication.JwtBearer from 5.0.6 to 5.0.9 in /src/Genocs.Core.Demo.WebApi [`#1`](https://github.com/Genocs/genocs-library/pull/1) +- Added SignalR service [`8a6faee`](https://github.com/Genocs/genocs-library/commit/8a6faeef4c6c2dec473abb1e86fe55a9e24f87c5) +- Added template [`98884b0`](https://github.com/Genocs/genocs-library/commit/98884b034910bc8d4c912344fe2ce0f54b4e7aaa) +- Upated to net7 [`2cbc3f8`](https://github.com/Genocs/genocs-library/commit/2cbc3f8b6cefb5cfe153b3aba8a28d5f425804dc) +- First commit [`4d9b150`](https://github.com/Genocs/genocs-library/commit/4d9b1503a8295f21392886f4a149a67851472d47) +- Users Implementation - WIP [`1b80c65`](https://github.com/Genocs/genocs-library/commit/1b80c65e26fcbe16ae15aaf42fa7b93350aca70e) +- Added Consul, Fabio and RestEasy [`0d7e96b`](https://github.com/Genocs/genocs-library/commit/0d7e96bcdc372044b8a7b7d3705b594aa5c39b97) +- Added WebApi [`9e28196`](https://github.com/Genocs/genocs-library/commit/9e28196b0db9a3a30d4ba8094c448020f09529c7) +- Added Query builder [`6e1318b`](https://github.com/Genocs/genocs-library/commit/6e1318b83b09299bce1fe0d1683b67e6aa200fa7) +- File file structure completed [`af6664e`](https://github.com/Genocs/genocs-library/commit/af6664ea893388c8acf938c3317c8d76c90c3c87) +- Review for CQRS [`e7ec4aa`](https://github.com/Genocs/genocs-library/commit/e7ec4aa85a81b47af4c4ae3bd4ab2ce4e6adb852) +- Added Vault [`172acbd`](https://github.com/Genocs/genocs-library/commit/172acbdc7aded226a49b642168443ca63f39241d) +- Updated naming conventions [`a81a5de`](https://github.com/Genocs/genocs-library/commit/a81a5dec2f8fc1cda111b0451d3da310b3b410ef) +- Genocs.Core Version 3.1 [`89675d3`](https://github.com/Genocs/genocs-library/commit/89675d3bc6b68e03628865c20ff52bee94888086) +- Fix read options [`831e1f4`](https://github.com/Genocs/genocs-library/commit/831e1f4631a3fa244dcf3bc25ad258d8db27eb9e) +- Added API Gateway [`78d0f16`](https://github.com/Genocs/genocs-library/commit/78d0f16b96b2900486c585f267ab7716679e320c) +- added Azure Service bus [`6d79d68`](https://github.com/Genocs/genocs-library/commit/6d79d68ad7a4348841cc100e84c57ec63c97bef2) +- Added WebApi.CORS and WebAPI.Security [`612b2d2`](https://github.com/Genocs/genocs-library/commit/612b2d2696894ef9f12e0410750a86069c1b8e0d) +- Removed fix [`80e943c`](https://github.com/Genocs/genocs-library/commit/80e943c9283570a3816dcff418bb683d2e401314) +- Build it's OK - Runting crash [`044f029`](https://github.com/Genocs/genocs-library/commit/044f0297c4d24b17da97a48196c260f987b20dee) +- Added complete demo [`ec6427c`](https://github.com/Genocs/genocs-library/commit/ec6427c3f0ba73fb429292025f26d21598a1cf6a) +- Added auth [`6703204`](https://github.com/Genocs/genocs-library/commit/670320401a6198b5ebb09464a2fd10cfe49d1765) +- update version [`3973f32`](https://github.com/Genocs/genocs-library/commit/3973f3236625dad6d1c11b19b56a71fb1c24c076) +- Refactory [`d88206b`](https://github.com/Genocs/genocs-library/commit/d88206b55a4825202ad7d2140c97087a634ca14b) +- Implemented CQRS pattern in Core [`d2adfb8`](https://github.com/Genocs/genocs-library/commit/d2adfb8a7385bd1d6312fbb69ff8568058b6ab07) +- Added products [`1a9fcdd`](https://github.com/Genocs/genocs-library/commit/1a9fcdd53f4aa68807b0ae7fbe5da4217ebf501f) +- Updated Nuget plus push on nuget [`eb001d8`](https://github.com/Genocs/genocs-library/commit/eb001d899a25f212591c195cf9fd6f8cc2b00265) +- Added web demo [`77dc809`](https://github.com/Genocs/genocs-library/commit/77dc809c3d661f08011dabf8f52fce15481ab9d7) +- Configuration files updated for deployments [`86317d3`](https://github.com/Genocs/genocs-library/commit/86317d370287b75e4f5114ae24c9018ee47df1fd) +- Committed after refactory [`908dee7`](https://github.com/Genocs/genocs-library/commit/908dee79e5818875f1b366d509cd9231ea20011f) +- Clean up data [`5378f6b`](https://github.com/Genocs/genocs-library/commit/5378f6bfc60b91e7ff01578744a71896f67f787e) +- Projects settings [`669f10f`](https://github.com/Genocs/genocs-library/commit/669f10fab3bb4f9ae99682f65697ce2285e8ebaa) +- Split monitoring and telemetry [`8afc9bd`](https://github.com/Genocs/genocs-library/commit/8afc9bdef6ccce499ff519156099807e1545bd77) +- Added docker compose [`415b416`](https://github.com/Genocs/genocs-library/commit/415b41621b1a0ffb3018d767a71df59e7348ee3c) +- Start Setup dependencies [`76b2389`](https://github.com/Genocs/genocs-library/commit/76b238943e65ce2fe4f1d02a05fcfaecfdc57b4a) +- Splitted common from core [`e58627c`](https://github.com/Genocs/genocs-library/commit/e58627c46f86933b3dc41a231882521a45b8bf0c) +- Addes swagger [`1b90eae`](https://github.com/Genocs/genocs-library/commit/1b90eae248e267e8ac8df4de51def8f2892cbbd4) +- Fix open telementry [`310d251`](https://github.com/Genocs/genocs-library/commit/310d25172d3f1ebc9de262cf46710186f1c7d2a9) +- Fix naming [`f964d5e`](https://github.com/Genocs/genocs-library/commit/f964d5e61cf4b648c5386a3db6d2d7910a25c46f) +- Removed compile warning [`82480de`](https://github.com/Genocs/genocs-library/commit/82480de6aa6368afa375244d49c6e54562512a63) +- Refactory naming conventions [`f340235`](https://github.com/Genocs/genocs-library/commit/f34023508c5301f9a1b197caccb35094295bd06f) +- Added security [`09a7102`](https://github.com/Genocs/genocs-library/commit/09a7102c1cad10a67e24a86ddca20452a6cda5ef) +- Added Mongodb [`dc0b989`](https://github.com/Genocs/genocs-library/commit/dc0b989f9d9ed23b68cee3023327228075c435e5) +- Fix Runtime error on outbox, there is an issue on get AppSettings [`371d754`](https://github.com/Genocs/genocs-library/commit/371d7545d468a057a5c7fdb52fc5f06671434d1c) +- added api service to service [`b8b00b9`](https://github.com/Genocs/genocs-library/commit/b8b00b9b66096175509689c3683b7214b6097295) +- Added kubernes deplyments [`d9325b2`](https://github.com/Genocs/genocs-library/commit/d9325b216e8de992ae0fa85a413628e3d1633f8e) +- Completed the docker container applicaion tests [`33581e4`](https://github.com/Genocs/genocs-library/commit/33581e44202235fb424d15d28fbe210ca0147030) +- Released on Nuget [`74b71d3`](https://github.com/Genocs/genocs-library/commit/74b71d3cf70fc1251398681fffc7e63aab421185) +- Added support for NET6 as well [`1035cd2`](https://github.com/Genocs/genocs-library/commit/1035cd2580405fe25fb2b5e8fa37eb10fde4e361) +- Fix namespaces [`df48cc0`](https://github.com/Genocs/genocs-library/commit/df48cc014ea75907ed03b986cc553faeaac9db45) +- Sync libraries [`1b8a059`](https://github.com/Genocs/genocs-library/commit/1b8a059a159a63abd9122fec4fbf36efd7cde98d) +- Added bus worker [`ee5627a`](https://github.com/Genocs/genocs-library/commit/ee5627a3c79e6534f8757ebbc87fe64e79ac777a) +- foolish updates [`e8f4bb8`](https://github.com/Genocs/genocs-library/commit/e8f4bb86b4fbaf42483c3705d685d7af1e572f27) +- Build release [`d6cd11b`](https://github.com/Genocs/genocs-library/commit/d6cd11b36d33d21b7a0fcc69b63673ea080041da) +- Added masstransit hosted service [`71d1f40`](https://github.com/Genocs/genocs-library/commit/71d1f40e7d9009bc7a30feecca60a4a34ac808f7) +- Fix run SignalR [`1d5b669`](https://github.com/Genocs/genocs-library/commit/1d5b6690cab6d53099ee35b7eba1d52ff37eac12) +- Persistence MongoDB 3.0.1 released [`a44893c`](https://github.com/Genocs/genocs-library/commit/a44893c95af0413a961e9a3a1e7cc2b5fe7e0a80) +- Updated library [`99502ff`](https://github.com/Genocs/genocs-library/commit/99502ffb74ee1d828ad10649a4b25f7a9d85f2a6) +- minor fix [`ca3b995`](https://github.com/Genocs/genocs-library/commit/ca3b99552a202dc79041207848dea69800c4856b) +- Fix monitoring and tracing [`5ca3d46`](https://github.com/Genocs/genocs-library/commit/5ca3d4638788a3564f94fb156000fde6ae79572a) +- Added AzureService Bus handler [`df5f7e5`](https://github.com/Genocs/genocs-library/commit/df5f7e5a97c038d5a659bc524214109b1a586c50) +- Document Update [`974ab14`](https://github.com/Genocs/genocs-library/commit/974ab144d3de2fae312301448ae76145652a4d99) +- Fixed signalR [`c03535b`](https://github.com/Genocs/genocs-library/commit/c03535b12a9eff2f31a3f10976af149c4d5e89e0) +- Refactory for docker build [`35c06cc`](https://github.com/Genocs/genocs-library/commit/35c06cc104b47aa380473b5de2fe7a98be9f1aa4) +- New Release on Nuget [`766a5d8`](https://github.com/Genocs/genocs-library/commit/766a5d804c9c1fb0d2a5c4d43d97e967bebab486) +- updated nuget packages [`c3eb63e`](https://github.com/Genocs/genocs-library/commit/c3eb63e72fa31223830cdde3778b73a90ad181f1) +- Updates [`333fbeb`](https://github.com/Genocs/genocs-library/commit/333fbeb71deb1b2a46ec9e1d521de6428fdcbb4e) +- Code cleanup [`d780d09`](https://github.com/Genocs/genocs-library/commit/d780d09eafedd42b55599509ae3bdb0ae877391d) +- Add jwt [`594b8f8`](https://github.com/Genocs/genocs-library/commit/594b8f8dcee2414fe569751a3b128c38227b03b0) +- Updated nuget services [`cf8ff75`](https://github.com/Genocs/genocs-library/commit/cf8ff75dfd5c7b27a7d7e8dcad4d302d6403594b) +- Added command [`81d0f09`](https://github.com/Genocs/genocs-library/commit/81d0f090f49036c7063e181627583fd410bb89b1) +- Changed readme filename [`7689dca`](https://github.com/Genocs/genocs-library/commit/7689dca4d7e29c2e13ef586fcd6a850c94264880) +- Cleanup data [`d8e270e`](https://github.com/Genocs/genocs-library/commit/d8e270eed46c5f7726b62d8bfc0c86a1747fb4c0) +- fix signalR [`a35dc64`](https://github.com/Genocs/genocs-library/commit/a35dc643b1c8f64a2521ec6b30518d83d3e5b75f) +- Removed launch [`0430d66`](https://github.com/Genocs/genocs-library/commit/0430d6632d5d50f06c23ad3f42992b12c56bf835) +- WIP [`ffec644`](https://github.com/Genocs/genocs-library/commit/ffec64431484fb3854317bf176bd5f38ab96e46c) +- Refactory naming conventions [`227b8c3`](https://github.com/Genocs/genocs-library/commit/227b8c344e8f7dc889e710de9959f05fc8ba9c26) +- Fix copy and paste typos [`d29576d`](https://github.com/Genocs/genocs-library/commit/d29576dfd7002412f3bc6f25ad7b26be24e780d4) +- Updated Readme [`3d52b4e`](https://github.com/Genocs/genocs-library/commit/3d52b4eb1368e737c40b95a735bf17931e38ad95) +- Minor updates [`1d5b26c`](https://github.com/Genocs/genocs-library/commit/1d5b26c3ce70434a94007db8038974fa19231cfa) +- Enable communication between ApiGateway and service [`17e85f3`](https://github.com/Genocs/genocs-library/commit/17e85f344fdeca17eea225a0389f097fb4aabebc) +- Template fix [`108afb7`](https://github.com/Genocs/genocs-library/commit/108afb7a1950804d4fefd322bbf03b12a796e070) +- tmp [`5436cf9`](https://github.com/Genocs/genocs-library/commit/5436cf92e5d15febf1dc9dc0e4c195f44c8caec7) +- Added localhost certificates [`71e14b6`](https://github.com/Genocs/genocs-library/commit/71e14b6c471fbc45c91a3d7377825119feba96ca) +- Namespace updates [`e832ec2`](https://github.com/Genocs/genocs-library/commit/e832ec20865c711732cf6e530a7a2c83ea2a1bc2) +- Extensions plus placeholder for documentation [`db01f6e`](https://github.com/Genocs/genocs-library/commit/db01f6ebb777e07c350fd3dcadbfcdd18c738e21) +- Fix unit test [`28bf234`](https://github.com/Genocs/genocs-library/commit/28bf2345e57ca7a1b785fc561f820cd17b5788b0) +- Refactory and Standardization [`198a4ef`](https://github.com/Genocs/genocs-library/commit/198a4ef419517dc15b50ecebbba89830e6f76d86) +- Fixed project to build in Release [`b60f331`](https://github.com/Genocs/genocs-library/commit/b60f331469ec70ff37920baec5657dfb24b9b6f9) +- fix for build and up [`ec855c7`](https://github.com/Genocs/genocs-library/commit/ec855c7c36a7aacaa918180111706fc30ea42d83) +- Fix build docker file [`4edfb28`](https://github.com/Genocs/genocs-library/commit/4edfb2874a1e592e66688d475b93141ce9149514) +- Cleanup [`c564e0a`](https://github.com/Genocs/genocs-library/commit/c564e0a14d4ce4971cb55abc2e3bb459f2c2c1e5) +- Updates 2023 [`65a783a`](https://github.com/Genocs/genocs-library/commit/65a783a4c683bb647c70c1f5c7bedf8eab2635cf) +- updated packages [`fd847a3`](https://github.com/Genocs/genocs-library/commit/fd847a3b46a813a47daebe55849fb7f48d78aa58) +- Fix nuget packages [`41fc96c`](https://github.com/Genocs/genocs-library/commit/41fc96c675586f82d91af62c28738e6521bf02e7) +- updated readme [`928bb26`](https://github.com/Genocs/genocs-library/commit/928bb261c4e458ec8351cebf7d1cc8231f30f84f) +- added nuspec [`5cc6a99`](https://github.com/Genocs/genocs-library/commit/5cc6a9994bdccbba3ad8a116fc1599c081daafb3) +- Initial commit [`d678e72`](https://github.com/Genocs/genocs-library/commit/d678e722365d1c5a6591dd776dbb7112accc0d1b) +- added files [`5a6dd39`](https://github.com/Genocs/genocs-library/commit/5a6dd3979e76199fa329cb0e5ae7793ab0974007) +- removed duplicated [`aea2e5e`](https://github.com/Genocs/genocs-library/commit/aea2e5e060b3ac4dba64061a04f29c9da2676d33) +- Readme updates [`f971283`](https://github.com/Genocs/genocs-library/commit/f971283d32aca19fd9268b8a5465e1060c9130eb) +- Added support for console hosted service plus rebuild to rc1.1 [`2fcf79a`](https://github.com/Genocs/genocs-library/commit/2fcf79a33a42fae1622c9086222fcb48a775e63a) +- SignalR Setup completed [`57f8694`](https://github.com/Genocs/genocs-library/commit/57f869470812a3c2c1686e221887c62290da1756) +- Added preview on package [`20a72cb`](https://github.com/Genocs/genocs-library/commit/20a72cbadda98a8244c4a1ca56e8287fe6029ba0) +- New architecture [`5b693db`](https://github.com/Genocs/genocs-library/commit/5b693dbe7a66662bf47b064ba4147e5b4274745a) +- minor fix on documentation [`9e08077`](https://github.com/Genocs/genocs-library/commit/9e08077f50321b8ff903a3b8c04ae958dc085942) +- updated packages [`1961ed1`](https://github.com/Genocs/genocs-library/commit/1961ed1d9d4cc4c791e4be03d4ee3b3ccc1237c1) +- minor changes [`f69c864`](https://github.com/Genocs/genocs-library/commit/f69c8641983e94461a999f45fefd330bd2fef68b) +- Convey Latest version [`f381cc0`](https://github.com/Genocs/genocs-library/commit/f381cc08a8421c2e8f6eb683581cb167baf67c74) +- Create nuget-publish.yml [`c880398`](https://github.com/Genocs/genocs-library/commit/c880398feb9bf65fea01a0becfa0b4d3e9d558c2) +- Extended pagination [`7a4f64b`](https://github.com/Genocs/genocs-library/commit/7a4f64b30e29a8b51a6046a45616dcf9b9f7d5a4) +- Updated packages [`c974b2f`](https://github.com/Genocs/genocs-library/commit/c974b2f93d44486f1e5c61141c576265fbe4b2e9) +- bug fix [`23e55e2`](https://github.com/Genocs/genocs-library/commit/23e55e2dbe8fe1f16ace837e81122bd19ca6e1ab) +- Create manual.yml [`08a5bf4`](https://github.com/Genocs/genocs-library/commit/08a5bf4b1ac0ad43ead33a7d78aca3e00da9ccf6) +- Updated github actions [`610ae7a`](https://github.com/Genocs/genocs-library/commit/610ae7af5ef35f77361353168c8290f14e106e17) +- Skip pack [`5f0e1a6`](https://github.com/Genocs/genocs-library/commit/5f0e1a609ebffaa806f87c1de033e3e1158ffc87) +- Updated DDSettings to MongoDbSettings [`78867ef`](https://github.com/Genocs/genocs-library/commit/78867ef90b247a5c31b40b101771876e93cf0f60) +- Create build_and_test.yml [`b5d6356`](https://github.com/Genocs/genocs-library/commit/b5d63563be406b3e33b0bfe953db8301b8907f3b) +- case [`1410f79`](https://github.com/Genocs/genocs-library/commit/1410f799761a1ca552cc290a38ccfdbcff4ea069) +- Fix product settings file [`1c9bfb7`](https://github.com/Genocs/genocs-library/commit/1c9bfb77ac9f270ece594b737bb299b96da94c11) +- Fix nuget [`49ff439`](https://github.com/Genocs/genocs-library/commit/49ff439e01e9822a528995cb07df3a79c166cfd2) +- updated pack [`7fda8e3`](https://github.com/Genocs/genocs-library/commit/7fda8e359aa701d729b1e361f8aca6cae77276ea) +- Another fix [`5945ba0`](https://github.com/Genocs/genocs-library/commit/5945ba0df066de1413b818380733751d3f9b20f4) +- Added MongoDbSettings validation [`fdd53a4`](https://github.com/Genocs/genocs-library/commit/fdd53a4507acb9ac4198ce1ebbd89118219cca6b) +- Removed service discovery [`1fa0490`](https://github.com/Genocs/genocs-library/commit/1fa0490c7cf83daf912da12c894a8ae0868390f0) +- Fix naming case [`5140f99`](https://github.com/Genocs/genocs-library/commit/5140f99bd6125e38ba3f93eb49d1834a5d288c00) +- updated the readme file [`9d08b32`](https://github.com/Genocs/genocs-library/commit/9d08b3274253a68020d9c2717bdc5befde17058d) +- Updated namespace [`460fa3a`](https://github.com/Genocs/genocs-library/commit/460fa3a7508c93e0807d0ae039046efdc6db7ada) +- Restored package creation [`aba219a`](https://github.com/Genocs/genocs-library/commit/aba219a4c34fac787d100949e4aa010766a26c15) +- Enabled metrics [`9323ce3`](https://github.com/Genocs/genocs-library/commit/9323ce30c4df515c8fef902472034ce46bc8f270) +- another [`d75abd6`](https://github.com/Genocs/genocs-library/commit/d75abd63e45f249a40e8dcd07f6d28bbf00c7ae5) +- updated Git Actions [`ab039cb`](https://github.com/Genocs/genocs-library/commit/ab039cb3fe2f1d9ab3e7b8b68b8d7634fcec9673) +- Update README.md [`0968315`](https://github.com/Genocs/genocs-library/commit/0968315e31a156617be2fb248b8694d0003c6cf1) +- Persistence MongoDB Release 3.1.1 [`1096f4b`](https://github.com/Genocs/genocs-library/commit/1096f4bc44d4ef443584fa33e174666f3a5c4dc8) +- Update README.md [`db02cea`](https://github.com/Genocs/genocs-library/commit/db02cea5ea6b58eba1590ee4302364654abe09b4) +- Fix git action [`edcc0eb`](https://github.com/Genocs/genocs-library/commit/edcc0eb2d19910b148c37f638e7bbb9eda5b28e3) +- Minor fix [`c3e46d0`](https://github.com/Genocs/genocs-library/commit/c3e46d013c5248694996ad19cdc0bfb1234baccd) +- updated and push on NuGet [`f6b5c65`](https://github.com/Genocs/genocs-library/commit/f6b5c65afd7f0739ca448d2ee390e38c5ad74bcc) +- Fix missing namespaces [`11582e2`](https://github.com/Genocs/genocs-library/commit/11582e2b6ef9e507f9cf3712e23307024a009086) +- minor updates [`3a6cff2`](https://github.com/Genocs/genocs-library/commit/3a6cff26590274f44dab563d16328be3c6a68561) +- Updated to net 7 [`09f258c`](https://github.com/Genocs/genocs-library/commit/09f258cc977d63e2592bdd7f0ad8a3de94b14d24) +- updated versions [`1e82df3`](https://github.com/Genocs/genocs-library/commit/1e82df394f3def006ad6146ed4edc808c58ece28) +- Updated copyright [`38abc87`](https://github.com/Genocs/genocs-library/commit/38abc8781162853519f402b46e958dee1b56bcd8) +- another check [`3daed28`](https://github.com/Genocs/genocs-library/commit/3daed28b821c4a33797947878b01c228b4495942) +- Cleanup [`a80ff56`](https://github.com/Genocs/genocs-library/commit/a80ff568a5768af5ffec176b36092be30331e633) +- updated actions [`c44d885`](https://github.com/Genocs/genocs-library/commit/c44d8857f6e5dc6bdf7f5257b659ac018577a554) +- Fix out folder [`b3d51aa`](https://github.com/Genocs/genocs-library/commit/b3d51aa76066b400768356d5d485af26db525abf) +- Minor fix [`2b6d56a`](https://github.com/Genocs/genocs-library/commit/2b6d56ae702a9c1cf8184e2ccedb37a42a066136) +- Updates packages [`7e9ea75`](https://github.com/Genocs/genocs-library/commit/7e9ea7588dfa3fdd35798740e2ca5654e018b73a) +- another fix [`b341ea9`](https://github.com/Genocs/genocs-library/commit/b341ea9eb3c3579c15782e67df1f769fd30ccd34) +- prerelease version [`10ecc45`](https://github.com/Genocs/genocs-library/commit/10ecc45f534e5460309a39bc6b6b8c98225fbecd) +- Update manual.yml [`dd83b4f`](https://github.com/Genocs/genocs-library/commit/dd83b4fe355bdcad206b00d88ef972027353d101) +- enabled build nuget [`79ddbaa`](https://github.com/Genocs/genocs-library/commit/79ddbaa242c9692850fcf3f2aed7132921563732) +- Update README.md [`dbb5ad1`](https://github.com/Genocs/genocs-library/commit/dbb5ad1ec63e5f98c2b0dfc1a56e08479b18cdd8) +- another fix [`fc4a5e3`](https://github.com/Genocs/genocs-library/commit/fc4a5e31b159b02677ccb162da36365ca8088718) +- Update genocs.sln [`f9a8a44`](https://github.com/Genocs/genocs-library/commit/f9a8a4412c69334876ef7b448d7a859ed6952feb) +- Rename Genocs.Persistence.MongoDb.UnitTests.csproj to Genocs.Persistence.MongoDB.UnitTests.csproj [`c01d12a`](https://github.com/Genocs/genocs-library/commit/c01d12ad403bdea18df049af1f766c251bbc08aa) +- --no-symbols true [`c796cec`](https://github.com/Genocs/genocs-library/commit/c796cec8f8744e4cca00bc5794724fece0cba787) +- Fix Typo [`154f1f2`](https://github.com/Genocs/genocs-library/commit/154f1f2131c5c66d961a9cdf9f8c868f24989866) +- updated push nuget [`6f3038d`](https://github.com/Genocs/genocs-library/commit/6f3038d0b848795456f0baa517799c0b03b84071) +- updated nuget package [`dca2bd3`](https://github.com/Genocs/genocs-library/commit/dca2bd340733b670b9a4ee6134878fda59cf4982) +- Fix secret [`88749b3`](https://github.com/Genocs/genocs-library/commit/88749b3edeac981bfd4edff32df16d15de4bde60) +- Modify settings [`fca6a4e`](https://github.com/Genocs/genocs-library/commit/fca6a4ec1283eeea3890daa9a05086ab95651da2) +- minor fix [`dfa6a0d`](https://github.com/Genocs/genocs-library/commit/dfa6a0d366b2a2f9700a3026b49f800e7a592ff7) +- Fix version [`050265a`](https://github.com/Genocs/genocs-library/commit/050265ad33ea0cb0a3d8dfae4083aabd74260136) +- Update README.md [`4519f74`](https://github.com/Genocs/genocs-library/commit/4519f742a570e61463a48318b0cf91a2ce96e224) +- Update manual.yml [`cac36ca`](https://github.com/Genocs/genocs-library/commit/cac36cae9243c6fd4e56bd35c4775bb586243ba9) +- Update manual.yml [`8d4b4b4`](https://github.com/Genocs/genocs-library/commit/8d4b4b4c7db071be08e11a1408119f69139688f5) +- Update build_and_test.yml [`0d3eba5`](https://github.com/Genocs/genocs-library/commit/0d3eba579054975364bd26b7289bc7463cd28438) +- Bump Microsoft.AspNetCore.Authentication.JwtBearer [`0a3c697`](https://github.com/Genocs/genocs-library/commit/0a3c6970fc1630ec5eded4692c148bbb208a61af) +- pack [`4dfae77`](https://github.com/Genocs/genocs-library/commit/4dfae7789e1285ba60a2aea3b99186182adfcdff) +- updated pack [`3b66046`](https://github.com/Genocs/genocs-library/commit/3b660468260fbb05a61f7676e3ab6a7bc500d8c9) +- commentd test [`b785f88`](https://github.com/Genocs/genocs-library/commit/b785f88944eeaa931fd1edbca827cdb9b669aa33) +- updated sdk [`08a07f5`](https://github.com/Genocs/genocs-library/commit/08a07f5093b766dc043f6e29c5546b41852686fc) +- updated dotnet version [`e869b6f`](https://github.com/Genocs/genocs-library/commit/e869b6f05723a30e2f30ae5762970203b971fc19) +- Fix travis [`87659ed`](https://github.com/Genocs/genocs-library/commit/87659ed017917485c4ab7f7f929aa6c650b6b1ad) +- changed build path [`2e5d97b`](https://github.com/Genocs/genocs-library/commit/2e5d97b2035b7e8f252243062b7bf010bed5fa36) +- Added file [`b8e3ca2`](https://github.com/Genocs/genocs-library/commit/b8e3ca2bf24ef1db66f90451023b3981feff65f4) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..5d2f3da5 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +info@genocs.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 00000000..49772f3a --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,77 @@ + + + + + enable + enable + true + false + false + $(MSBuildThisFileDirectory)dotnet.ruleset + True + True + 6.3.0 + 10.0 + Genocs + Genocs 2024 + LICENSE + https://github.com/Genocs/genocs-library + https://github.com/Genocs/genocs-library.git + icon.png + git + True + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + True + \ + + + True + \ + + + True + \ + + + + + + + + + + + + true + + + true + + + \ No newline at end of file diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 00000000..fd9797bc --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,5 @@ + + + $(OutputPath)$(AssemblyName).xml + + \ No newline at end of file diff --git a/LICENSE b/LICENSE index b5d2cb0b..e9e6d445 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Genocs Services +Copyright (c) 2024 Genocs Services Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..d01ff741 --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +build: + dotnet build +start: + dotnet run --project ./src/Genocs.TelegramIntegration.WebApi +nuget: + nuget pack -NoDefaultExcludes -OutputDirectory nupkgs +publish: + dotnet publish --os linux --arch x64 -c Release --self-contained +publish-to-hub: + dotnet publish --os linux --arch x64 -c Release -p:ContainerRegistry=docker.io -p:ContainerImageName=genocs/telegram_integration-webapi --self-contained +tp: # terraform plan + cd terraform/environments/staging && terraform plan +ta: # terraform apply + cd terraform/environments/staging && terraform apply +td: # terraform destroy + cd terraform/environments/staging && terraform destroy +dcu: # docker-compose up : webapi + postgresql + cd docker-compose/ && docker-compose -f docker-compose.postgresql.yml up -d +dcd: # docker-compose down : webapi + postgresql + cd docker-compose/ && docker-compose -f docker-compose.postgresql.yml down +fds: # force rededeploy aws ecs service + aws ecs update-service --force-new-deployment --service dotnet-webapi --cluster genocs +gw: # git docker workflow to push docker image to the repository based on the main branch + @echo triggering github workflow to push docker image to container + @echo ensure that you have the gh-cli installed and authenticated. + gh workflow run dockerhub-publish -f push_to_docker=true \ No newline at end of file diff --git a/NuGet.config b/NuGet.config index 4dfe59d3..a552551f 100644 --- a/NuGet.config +++ b/NuGet.config @@ -1,7 +1,6 @@ - diff --git a/README.md b/README.md index 7bb7c68f..5faf79c1 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,51 @@ -# Genocs .NET library + +[![License][license-shield]][license-url] +[![Build][build-shield]][build-url] +[![Downloads][downloads-shield]][downloads-url] +[![Contributors][contributors-shield]][contributors-url] +[![Forks][forks-shield]][forks-url] +[![Stargazers][stars-shield]][stars-url] +[![Issues][issues-shield]][issues-url] +[![Discord][discord-shield]][discord-url] +[![Gitter][gitter-shield]][gitter-url] +[![Twitter][twitter-shield]][twitter-url] +[![Twitterx][twitterx-shield]][twitterx-url] +[![LinkedIn][linkedin-shield]][linkedin-url] + +[license-shield]: https://img.shields.io/github/license/Genocs/genocs-library?color=2da44e&style=flat-square +[license-url]: https://github.com/Genocs/genocs-library/blob/main/LICENSE +[build-shield]: https://github.com/Genocs/genocs-library/actions/workflows/build_and_test.yml/badge.svg?branch=main +[build-url]: https://github.com/Genocs/genocs-library/actions/workflows/build_and_test.yml +[downloads-shield]: https://img.shields.io/nuget/dt/Genocs.Microservice.Template.svg?color=2da44e&label=downloads&logo=nuget +[downloads-url]: https://www.nuget.org/packages/Genocs.Microservice.Template +[contributors-shield]: https://img.shields.io/github/contributors/Genocs/genocs-library.svg?style=flat-square +[contributors-url]: https://github.com/Genocs/genocs-library/graphs/contributors +[forks-shield]: https://img.shields.io/github/forks/Genocs/genocs-library?style=flat-square +[forks-url]: https://github.com/Genocs/genocs-library/network/members +[stars-shield]: https://img.shields.io/github/stars/Genocs/genocs-library.svg?style=flat-square +[stars-url]: https://img.shields.io/github/stars/Genocs/genocs-library?style=flat-square +[issues-shield]: https://img.shields.io/github/issues/Genocs/genocs-library?style=flat-square +[issues-url]: https://github.com/Genocs/genocs-library/issues +[discord-shield]: https://img.shields.io/discord/1106846706512953385?color=%237289da&label=Discord&logo=discord&logoColor=%237289da&style=flat-square +[discord-url]: https://discord.com/invite/fWwArnkV +[gitter-shield]: https://img.shields.io/badge/chat-on%20gitter-blue.svg +[gitter-url]: https://gitter.im/genocs/ +[twitter-shield]: https://img.shields.io/twitter/follow/genocs?color=1DA1F2&label=Twitter&logo=Twitter&style=flat-square +[twitter-url]: https://twitter.com/genocs +[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=flat-square&logo=linkedin&colorB=555 +[linkedin-url]: https://www.linkedin.com/in/giovanni-emanuele-nocco-b31a5169/ +[twitterx-shield]: https://img.shields.io/twitter/url/https/twitter.com/genocs.svg?style=social +[twitterx-url]: https://twitter.com/genocs + + +

+ icon +

-[![GitHub](https://img.shields.io/github/license/Genocs/genocs-library?color=2da44e&style=flat-square)](https://github.com/Genocs/genocs-library/blob/main/LICENSE) -[![.NET build and test](https://github.com/Genocs/genocs-library/actions/workflows/build_and_test.yml/badge.svg)](https://github.com/Genocs/genocs-library/actions/workflows/build_and_test.yml) -[![NuGet](https://img.shields.io/badge/nuget-v.5.0.0-blue)](https://www.nuget.org/packages/Genocs.Core) -[![NuGet Downloads](https://img.shields.io/nuget/dt/Genocs.Core.svg)](https://www.nuget.org/packages/Genocs.Core) -[![Discord](https://img.shields.io/discord/1106846706512953385?color=%237289da&label=Discord&logo=discord&logoColor=%237289da&style=flat-square)](https://discord.com/invite/fWwArnkV) -[![Twitter](https://img.shields.io/twitter/follow/genocs?color=1DA1F2&label=Twitter&logo=Twitter&style=flat-square)](https://twitter.com/genocs) +# Genocs .NET library ---- -This repo contains a set of libraries designed by Genocs. The libraries are built using .NET standard 2.1, .NET6 and .NET7. +This repo contains a set of libraries to build LOB (Line Of Business) applications. The library is open source and built to be PRODUCTION READY. The library is built on top of .NET8, it is designed and maintained by Genocs. Packages are available on [NuGet Genocs](https://www.nuget.org/profiles/gioema_nocco). @@ -23,60 +59,82 @@ Building a software library to be cloud agnostic has several advantages. First, The advantages of using containers are numerous. Containers provide a lightweight, portable, and isolated environment for applications to run in, allowing them to be easily moved between different systems. This makes it easier to deploy applications quickly and reliably across different environments. Additionally, containers can help reduce resource consumption by running multiple applications on the same host, as each container is isolated from the others. This helps to improve efficiency and scalability. Finally, containers provide an additional layer of security, as they are isolated from the underlying operating system and other applications. +## Documentation + +You can find a useful documentation about how to use the library. The documentation contains the complete set of libraries, template, CLI that altogether make the *genocs ecosystem* a comprensive set of tools to build enterprise solutions. + +Documentation available at [Genocs Blog](https://genocs-blog.netlify.app/library/) + ## Infrastructure -In this section you can find the infrastructure components to setup the environment. -You will use ***Docker compose*** to setup the infrastructure components. +In this section you can find the infrastructure components you need to execute the solution. Infrastucture components are the database, the enterprice servise bus, the distributed logging, monitoring, tracing systems along with database and many more. +You can use **Docker compose** to setup the infrastructure components just by running few commands. ``` bash +cd ./containers # Setup the infrastructure -docker-compose -f ./containers/infrastructure-bare.yml --project-name genocs-infrastructure up -d -docker-compose -f ./containers/infrastructure-monitoring.yml --project-name genocs-infrastructure up -d -docker-compose -f ./containers/infrastructure-scaling.yml --project-name genocs-infrastructure up -d -docker-compose -f ./containers/infrastructure-security.yml --project-name genocs-infrastructure up -d +docker compose -f ./infrastructure-bare.yml --env-file ./.env --project-name genocs up -d +docker compose -f ./infrastructure-monitoring.yml --env-file ./.env --project-name genocs up -d +docker compose -f ./infrastructure-scaling.yml --env-file ./.env --project-name genocs up -d +docker compose -f ./infrastructure-security.yml --env-file ./.env --project-name genocs up -d # Use this file only in case you want to setup sqlserver database (no need if you use postgres) -docker-compose -f ./containers/infrastructure-sqlserver.yml --project-name genocs-infrastructure up -d +docker compose -f ./infrastructure-sqlserver.yml --env-file ./.env --project-name genocs up -d + +# Use this file only in case you want to setup mySql database (no need if you use postgres) +docker compose -f ./infrastructure-mysql.yml --env-file ./.env --project-name genocs up -d + +# Use this file only in case you want to setup oracle database (no need if you use postgres) +docker compose -f ./infrastructure-oracle.yml --env-file ./.env --project-name genocs up -d # Use this file only in case you want to setup elk stack -docker-compose -f ./containers/infrastructure-elk.yml --project-name genocs-infrastructure up -d +docker compose -f ./infrastructure-elk.yml --env-file ./.env --project-name genocs up -d # Use this file only in case you want to setup AI ML components prepared by Genocs -docker-compose -f ./containers/infrastructure-ml.yml --project-name genocs-infrastructure up -d +docker compose -f ./infrastructure-ml.yml --env-file ./.env --project-name genocs up -d ``` -`infrastructure-bare.yml` allows to install the basic infrastructure components. Basic componens are the [RabbitMQ](https://rabbitmq.com), [Redis](https://redis.io), [Mongo](https://mongodb.com), [Postgres](https://www.postgresql.org/). +`infrastructure-bare.yml` allows to install the basic infrastructure components. They are: +- [RabbitMQ](https://rabbitmq.com) +- [Redis](https://redis.io) +- [MongoDB](https://mongodb.com) +- [Postgres](https://www.postgresql.org/). +You can run them locally: -- [rabbitmq](http://localhost:15672/) -- redis -- mongo -- postgresql +- [RabbitMQ](http://localhost:15672): `localhost:15672` +- Redis: `localhost:6379` +- MongoDB: `localhost:27017` +- Postgres: `localhost:5432` -`infrastructure-monitoring.yml` allows to install the monitoring infrastructure components. +`infrastructure-monitoring.yml` allows to install the monitoring infrastructure components. They are: +- [Prometheus](https://prometheus.io/) +- [Grafana](https://grafana.com/) +- [InfluxDB](https://www.influxdata.com/) +- [Jaeger](https://www.jaegertracing.io/) +- [Seq](https://datalust.co/seq) -Inside the file you can find: -- prometheus -- grafana -- influxdb -- jaeager -- seq +You can run them locally: -`infrastructure-scaling.yml` allows to install the scaling infrastructure components. +- [Prometheus](localhost:9090): `localhost:9090` +- [Grafana](localhost:3000): `localhost:3000` +- [InfluxDB](localhost:8086): `localhost:8086` +- [Jaeger](localhost:16686): `localhost:16686` +- [Seq](localhost:5341): `localhost:5341` -Inside the file you can find: -- fabio -- consul +`infrastructure-scaling.yml` allows to install the scaling infrastructure components. They are: +- Fabio +- Consul `infrastructure-security.yml` allows to install the security infrastructure components. Inside the file you can find: -- vault (hashicorp) +- vault (Hashicorp) The script below allows to setup the infrastructure components. This means that you can find all the containers inside the same network. @@ -91,32 +149,16 @@ networks: Remember to add the network configuration inside your docker compose file to setup the network, before running the containers. -``` yml -networks: - genocs: - name: genocs-network - external: true - driver: bridge -``` ## ***Kubernetes cluster*** You can setup the application inside a Kubernetes cluster. -Check the repo [enterprise-containers](https://github.com/Genocs/enterprise-containers) to setup a Kubernetes cluster. - -Inside the repo you can find scripts, configuration files and documentation to setup a cluster from scratch. - -## **Libraries** -You can find a full documentation on: -[**Documentation**](https://genocs-blog.netlify.app/library/) - - +Check the repo [enterprise-containers](https://github.com/Genocs/enterprise-containers) to setup a Kubernetes cluster. +There you can find scripts, configuration files and documentation to setup a cluster from scratch. ## Support - - Use [**api-workbench**](./api-workbench.rest) inside Visual Studio code with [REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) plugin ## Configuration @@ -197,13 +239,15 @@ Use [**api-workbench**](./api-workbench.rest) inside Visual Studio code with [RE "tags": {} }, "jaeger": { - "enabled": false, - "serviceName": "users", - "udpHost": "localhost", - "udpPort": 6831, - "maxPacketSize": 65000, - "sampler": "const", - "excludePaths": [ "/", "/ping", "/metrics" ] + "enabled": true, + "serviceName": "orders", + "endpoint": "http://localhost:4317", + "protocol": "Grpc", + "processorType": "Batch", + "maxQueueSize": 2048, + "scheduledDelayMilliseconds": 5000, + "exporterTimeoutMilliseconds": 30000, + "maxExportBatchSize": 512 }, "jwt": { "certificate": { @@ -231,7 +275,7 @@ Use [**api-workbench**](./api-workbench.rest) inside Visual Studio code with [RE "enabled": false, "endpoint": "/metrics" }, - "mongo": { + "mongodb": { "connectionString": "mongodb://localhost:27017", "database": "genocs-users-service", "seed": false @@ -306,6 +350,11 @@ Use [**api-workbench**](./api-workbench.rest) inside Visual Studio code with [RE "header": "Certificate" } }, + "azureKeyVault": { + "enabled": false, + "name": "gnx-keyvault", + "managedIdentityId": "secret", + }, "vault": { "enabled": false, "url": "http://localhost:8200", @@ -342,7 +391,8 @@ Use [**api-workbench**](./api-workbench.rest) inside Visual Studio code with [RE ## Demo Application Inside the library there is a simple demo application you can use to test the library. -Some commands +Following are the commands to build and run the demo application. + ``` bash # Build the solution dotnet build @@ -382,13 +432,11 @@ docker build -t genocs/demo-worker:2.0.0 -t genocs/demo-worker:latest -f ./demo- docker push genocs/demo-worker:2.0.0 docker push genocs/demo-worker:latest ``` - - --- +--- ## Enterprise Application - -Take a look inside **./src/apps** folder. There you can find a full-fledged application composed by: +Inside **./src/apps** folder you can find a full-fledged application composed by: - ApiGateway - Identity Service - Order Service @@ -397,7 +445,7 @@ Take a look inside **./src/apps** folder. There you can find a full-fledged appl In that way you can test the entire flow. - +**TODO**: Add a architecture diagram to show the components and how they interact with each other. ### How to BUILD & RUN the application @@ -463,6 +511,13 @@ You can deploy Demo Application with one click in Heroku, Microsoft Azure, or Go [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fheartexlabs%2Flabel-studio%2Fmaster%2Fazuredeploy.json) [](https://deploy.cloud.run) +## License + +This project is licensed with the [MIT license](LICENSE). + +## Changelog + +View Complete [Changelog](https://github.com/Genocs/microservice-template/blob/main/CHANGELOG.md). ## Community @@ -470,33 +525,33 @@ You can deploy Demo Application with one click in Heroku, Microsoft Azure, or Go - Facebook Page [@genocs](https://facebook.com/Genocs) - Youtube Channel [@genocs](https://youtube.com/c/genocs) -## Contributors -Submit your PR and join the list! +## Support + +Has this Project helped you learn something New? or Helped you at work? +Here are a few ways by which you can support. + +- ⭐ Leave a star! +- 🥇 Recommend this project to your colleagues. +- 🦸 Do consider endorsing me on LinkedIn for ASP.NET Core - [Connect via LinkedIn](https://www.linkedin.com/in/giovanni-emanuele-nocco-b31a5169/) +- ☕ If you want to support this project in the long run, [consider buying me a coffee](https://www.buymeacoffee.com/genocs)! + - - - +[![buy-me-a-coffee](https://raw.githubusercontent.com/Genocs/blazor-template/main/assets/buy-me-a-coffee.png "buy-me-a-coffee")](https://www.buymeacoffee.com/genocs) -## License +## Code Contributors -This project is licensed with the [MIT license](LICENSE). +This project exists thanks to all the people who contribute. [Submit your PR and join the team!](CONTRIBUTING.md) -## Support :star: +[![genocs contributors](https://contrib.rocks/image?repo=Genocs/blazor-template "genocs contributors")](https://github.com/genocs/blazor-template/graphs/contributors) -Has this project helped you learn something New? or Helped you at work? -Here are a few ways by which you can support. +## Financial Contributors -- Leave a star! :star: -- Recommend this awesome project to your colleagues. 🥇 -- Do consider endorsing me on LinkedIn for ASP.NET Core - [Connect via LinkedIn](https://www.linkedin.com/in/giovanni-emanuele-nocco-b31a5169/) 🦸 -- Or, If you want to support this project in the long run, [consider buying me a coffee](https://www.buymeacoffee.com/genocs)! ☕ +Become a financial contributor and help me sustain the project. [Support the Project!](https://opencollective.com/genocs/contribute) -
- -black-button + -## **acknowledgments** +## Acknowledgements - [devmentors](https://github.com/devmentors) -- [abp](https://github.com/abpframework) \ No newline at end of file +- [abp](https://github.com/abpframework) diff --git a/assets/buy-me-a-coffee.png b/assets/buy-me-a-coffee.png new file mode 100644 index 00000000..02687279 Binary files /dev/null and b/assets/buy-me-a-coffee.png differ diff --git a/assets/genocs-library-logo.png b/assets/genocs-library-logo.png new file mode 100644 index 00000000..133cb562 Binary files /dev/null and b/assets/genocs-library-logo.png differ diff --git a/azure-pipeline.yml b/azure/azure-pipeline.yml similarity index 100% rename from azure-pipeline.yml rename to azure/azure-pipeline.yml diff --git a/azure/infrastructure/helloworld.bicep b/azure/infrastructure/helloworld.bicep new file mode 100644 index 00000000..3feb8d34 --- /dev/null +++ b/azure/infrastructure/helloworld.bicep @@ -0,0 +1,50 @@ +// Get the resource group location +param location string = 'East US' + +@description('The name of you Web Site.') +param webSiteName string = 'gnx-website' + +param uniqueString string = '{uniqueString(resourceGroup().id)}' + +// Generate a unique storage account name +param storageAccountName string = 'helloworldstorage${uniqueString}' + +// Create a storage account +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: { + accessTier: 'Hot' + } +} + +// Create an App Service Plan +resource appServicePlan 'Microsoft.Web/serverfarms@2023-12-01' = { + name: 'asp-${uniqueString}' + location: location + sku: { + name: 'F1' + tier: 'Free' + } +} + +// Create a web app +resource webApp 'Microsoft.Web/sites@2023-12-01' = { + name: webSiteName + location: location + properties: { + serverFarmId: appServicePlan.id + siteConfig: { + appSettings: [ + { + name: 'STORAGE_ACCOUNT_NAME' + value: storageAccount.name + } + ] + } + } +} diff --git a/containers/.env b/containers/.env new file mode 100644 index 00000000..e69de29b diff --git a/containers/infrastructure-bare.yml b/containers/infrastructure-bare.yml index ba8e929a..31e236da 100644 --- a/containers/infrastructure-bare.yml +++ b/containers/infrastructure-bare.yml @@ -1,5 +1,3 @@ -version: "3.9" - services: rabbitmq: image: masstransit/rabbitmq diff --git a/containers/infrastructure-elk.yml b/containers/infrastructure-elk.yml index d87f8929..3d03dfe6 100644 --- a/containers/infrastructure-elk.yml +++ b/containers/infrastructure-elk.yml @@ -1,5 +1,3 @@ -version: "3.9" - services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:7.3.2 diff --git a/containers/infrastructure-ml.yml b/containers/infrastructure-ml.yml index eba6aa7f..e3f0e21d 100644 --- a/containers/infrastructure-ml.yml +++ b/containers/infrastructure-ml.yml @@ -1,5 +1,3 @@ -version: "3.9" - services: image_labeling: image: heartexlabs/label-studio:latest diff --git a/containers/infrastructure-monitoring.yml b/containers/infrastructure-monitoring.yml index 6c6b3ab6..6eeedab9 100644 --- a/containers/infrastructure-monitoring.yml +++ b/containers/infrastructure-monitoring.yml @@ -1,5 +1,3 @@ -version: "3.9" - services: grafana: image: grafana/grafana @@ -37,6 +35,7 @@ services: - 9411:9411 - 14268:14268 - 16686:16686 + - 4317:4317 # network_mode: host networks: - genocs diff --git a/containers/infrastructure-mysql.yml b/containers/infrastructure-mysql.yml new file mode 100644 index 00000000..e4dbc650 --- /dev/null +++ b/containers/infrastructure-mysql.yml @@ -0,0 +1,23 @@ +services: + mysqldb: + image: mysql + hostname: mysqldb + container_name: mysqldb + ports: + - 3306:3306 + + environment: + MYSQL_ROOT_PASSWORD: MySect3tPassw! + volumes: + - mysqlsystem:/var/lib/mysql + networks: + - genocs + +networks: + genocs: + name: genocs-network + external: true + +volumes: + mysqlsystem: + driver: local diff --git a/containers/infrastructure-oracle.yml b/containers/infrastructure-oracle.yml new file mode 100644 index 00000000..2ce16a6f --- /dev/null +++ b/containers/infrastructure-oracle.yml @@ -0,0 +1,35 @@ +services: + oracledb: + image: container-registry.oracle.com/database/express:21.3.0-xe + hostname: oracledb + container_name: oracledb + ports: + - 1521:1521 + - 5500:5500 + + environment: + ORACLE_PWD: MySect3tPassw! + ORACLE_CHARACTERSET: AL32UTF8 + ENABLE_ARCHIVELOG: true + ENABLE_FORCE_LOGGING: true + + volumes: + - oracle_data:/opt/oracle/oradata + - oracle_startup:/opt/oracle/scripts/startup + - oracle_setup:/opt/oracle/scripts/setup + + networks: + - genocs + +networks: + genocs: + name: genocs-network + external: true + +volumes: + oracle_data: + driver: local + oracle_startup: + driver: local + oracle_setup: + driver: local diff --git a/containers/infrastructure-scaling.yml b/containers/infrastructure-scaling.yml index a7247e96..99ecf424 100644 --- a/containers/infrastructure-scaling.yml +++ b/containers/infrastructure-scaling.yml @@ -1,5 +1,3 @@ -version: "3.9" - services: consul: image: hashicorp/consul diff --git a/containers/infrastructure-security.yml b/containers/infrastructure-security.yml index 07f089bf..3ecfd2c5 100644 --- a/containers/infrastructure-security.yml +++ b/containers/infrastructure-security.yml @@ -1,5 +1,3 @@ -version: "3.9" - services: vault: image: hashicorp/vault diff --git a/containers/infrastructure-sqlserver.yml b/containers/infrastructure-sqlserver.yml index c88354d2..be0d0e94 100644 --- a/containers/infrastructure-sqlserver.yml +++ b/containers/infrastructure-sqlserver.yml @@ -1,5 +1,3 @@ -version: "3.9" - services: sqlserver: diff --git a/demo-docker-compose.yml b/demo-docker-compose.yml index 2cccfe6b..fb63a465 100644 --- a/demo-docker-compose.yml +++ b/demo-docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.9' - services: application.webapi: image: genocs/demo-webapi:2.0.0 diff --git a/demo-webapi.dockerfile b/demo-webapi.dockerfile index dea4fa6c..963017d9 100644 --- a/demo-webapi.dockerfile +++ b/demo-webapi.dockerfile @@ -1,20 +1,11 @@ #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. - -#FROM mcr.microsoft.com/dotnet/core/aspnet:3.0 -#FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim -#FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim -#FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine -FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 -#FROM mcr.microsoft.com/dotnet/core/sdk:3.0 AS build-env -#FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build-env -# FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build-env -#FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build-env -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env WORKDIR /src COPY ["src/Genocs.Core", "src/Genocs.Core/"] COPY ["src/Genocs.Common", "src/Genocs.Common/"] diff --git a/demo-worker.dockerfile b/demo-worker.dockerfile index fa4bd415..dc372871 100644 --- a/demo-worker.dockerfile +++ b/demo-worker.dockerfile @@ -1,20 +1,11 @@ #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. - -#FROM mcr.microsoft.com/dotnet/core/aspnet:3.0 -#FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim -#FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim -#FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine -FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 -#FROM mcr.microsoft.com/dotnet/core/sdk:3.0 AS build-env -#FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build-env -# FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build-env -#FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build-env -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env WORKDIR /src COPY ["src/Genocs.Core.Demo.Worker", "src/Genocs.Core.Demo.Worker/"] COPY ["src/Genocs.Core.Demo.Contracts", "src/Genocs.Core.Demo.Contracts/"] diff --git a/dotnet.ruleset b/dotnet.ruleset new file mode 100644 index 00000000..3010e47e --- /dev/null +++ b/dotnet.ruleset @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/genocs.sln b/genocs.sln index 38feed0d..16581cec 100644 --- a/genocs.sln +++ b/genocs.sln @@ -29,8 +29,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Genocs.Core.Demo.Domain", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Genocs.Core.Demo.Infrastructure", "src\Genocs.Core.Demo.Infrastructure\Genocs.Core.Demo.Infrastructure.csproj", "{FA01671E-9778-4581-9CD3-4A0651DBC7D7}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Genocs.Monitoring", "src\Genocs.Monitoring\Genocs.Monitoring.csproj", "{14F46B52-6C3E-40CC-A18F-1A1E8FA9E6F3}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Genocs.QueryBuilder", "src\Genocs.QueryBuilder\Genocs.QueryBuilder.csproj", "{2F9F9EF1-031D-433A-816D-49372AAAAC66}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Genocs.QueryBuilder.UnitTests", "src\Genocs.QueryBuilder.UnitTests\Genocs.QueryBuilder.UnitTests.csproj", "{23B5DC61-F78E-42A5-9AA8-AB96DB7C89EC}" @@ -83,10 +81,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Genocs.Common", "src\Genocs EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Solution Items", "_Solution Items", "{51A2E158-4686-4764-91D5-3CDDD06280D4}" ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig demo-docker-compose.yml = demo-docker-compose.yml demo-webapi.dockerfile = demo-webapi.dockerfile demo-worker.dockerfile = demo-worker.dockerfile + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + dotnet.ruleset = dotnet.ruleset + global.json = global.json + launchSettings.json = launchSettings.json + Makefile = Makefile README.md = README.md + stylecop.json = stylecop.json EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OrdersService", "OrdersService", "{80862789-8B42-4878-AC10-9CFF06A7313C}" @@ -121,6 +127,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Application", "Application" src\apps\signalr-webapi.dockerfile = src\apps\signalr-webapi.dockerfile EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Genocs.Secrets.AzureKeyVault", "src\Genocs.Secrets.AzureKeyVault\Genocs.Secrets.AzureKeyVault.csproj", "{ECBF1AEE-AE3A-4F81-9943-5675169DEFA7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -171,10 +179,6 @@ Global {FA01671E-9778-4581-9CD3-4A0651DBC7D7}.Debug|Any CPU.Build.0 = Debug|Any CPU {FA01671E-9778-4581-9CD3-4A0651DBC7D7}.Release|Any CPU.ActiveCfg = Release|Any CPU {FA01671E-9778-4581-9CD3-4A0651DBC7D7}.Release|Any CPU.Build.0 = Release|Any CPU - {14F46B52-6C3E-40CC-A18F-1A1E8FA9E6F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {14F46B52-6C3E-40CC-A18F-1A1E8FA9E6F3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {14F46B52-6C3E-40CC-A18F-1A1E8FA9E6F3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {14F46B52-6C3E-40CC-A18F-1A1E8FA9E6F3}.Release|Any CPU.Build.0 = Release|Any CPU {2F9F9EF1-031D-433A-816D-49372AAAAC66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2F9F9EF1-031D-433A-816D-49372AAAAC66}.Debug|Any CPU.Build.0 = Debug|Any CPU {2F9F9EF1-031D-433A-816D-49372AAAAC66}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -287,6 +291,10 @@ Global {6CE8740F-8561-481B-AC9F-D1E73C449235}.Debug|Any CPU.Build.0 = Debug|Any CPU {6CE8740F-8561-481B-AC9F-D1E73C449235}.Release|Any CPU.ActiveCfg = Release|Any CPU {6CE8740F-8561-481B-AC9F-D1E73C449235}.Release|Any CPU.Build.0 = Release|Any CPU + {ECBF1AEE-AE3A-4F81-9943-5675169DEFA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ECBF1AEE-AE3A-4F81-9943-5675169DEFA7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ECBF1AEE-AE3A-4F81-9943-5675169DEFA7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ECBF1AEE-AE3A-4F81-9943-5675169DEFA7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/global.json b/global.json new file mode 100644 index 00000000..f6d787e3 --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "8.0.10", + "rollForward": "latestMajor", + "allowPrerelease": true + } +} \ No newline at end of file diff --git a/icon.png b/icon.png index 66a5cbe2..3f859c6e 100644 Binary files a/icon.png and b/icon.png differ diff --git a/launchSettings.json b/launchSettings.json new file mode 100644 index 00000000..c0eaff1c --- /dev/null +++ b/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "Docker Compose": { + "commandName": "DockerCompose", + "commandVersion": "1.0", + "serviceActions": { + "genocs.demo.webapi": "StartDebugging" + } + } + } +} \ No newline at end of file diff --git a/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs b/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs index 94c54a7b..b0de9341 100644 --- a/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs +++ b/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs @@ -1,11 +1,11 @@ +using Genocs.Auth.Configurations; using Microsoft.AspNetCore.Http; using System.Net; namespace Genocs.Auth; - /// -/// The access token validator middleware +/// The access token validator middleware. /// public class AccessTokenValidatorMiddleware : IMiddleware { @@ -13,10 +13,10 @@ public class AccessTokenValidatorMiddleware : IMiddleware private readonly IEnumerable _endpoints; /// - /// The AccessTokenValidatorMiddleware constructor + /// The AccessTokenValidatorMiddleware constructor. /// - /// The access token service - /// The options + /// The access token service. + /// The options. public AccessTokenValidatorMiddleware(IAccessTokenService accessTokenService, JwtOptions options) { _accessTokenService = accessTokenService; @@ -24,14 +24,15 @@ public AccessTokenValidatorMiddleware(IAccessTokenService accessTokenService, Jw } /// - /// The InvokeAsync method + /// The InvokeAsync method. /// - /// The http context - /// The request delegate + /// The http context. + /// The request delegate. /// public async Task InvokeAsync(HttpContext context, RequestDelegate next) { - var path = context.Request.Path.HasValue ? context.Request.Path.Value : string.Empty; + string path = context.Request.Path.HasValue ? context.Request.Path.Value : string.Empty; + if (_endpoints.Contains(path)) { await next(context); diff --git a/src/Genocs.Auth/AuthAttribute.cs b/src/Genocs.Auth/AuthAttribute.cs index 4188f8bd..31c613c5 100644 --- a/src/Genocs.Auth/AuthAttribute.cs +++ b/src/Genocs.Auth/AuthAttribute.cs @@ -3,16 +3,17 @@ namespace Genocs.Auth; /// -/// The authorization Attribute +/// The authorization Attribute. /// public class AuthAttribute : AuthorizeAttribute { /// - /// The AuthAttribute constructor + /// The AuthAttribute constructor. /// - /// The authorization schema - /// The authorization policy - public AuthAttribute(string scheme, string policy = "") : base(policy) + /// The authorization schema. + /// The authorization policy. + public AuthAttribute(string scheme, string policy = "") + : base(policy) { AuthenticationSchemes = scheme; } diff --git a/src/Genocs.Auth/Builders/JwtOptionsBuilder.cs b/src/Genocs.Auth/Builders/JwtOptionsBuilder.cs index 0ab9ce28..c81ab262 100644 --- a/src/Genocs.Auth/Builders/JwtOptionsBuilder.cs +++ b/src/Genocs.Auth/Builders/JwtOptionsBuilder.cs @@ -1,3 +1,5 @@ +using Genocs.Auth.Configurations; + namespace Genocs.Auth.Builders; internal sealed class JwtOptionsBuilder : IJwtOptionsBuilder diff --git a/src/Genocs.Auth/IJwtOptionsBuilder.cs b/src/Genocs.Auth/Configurations/IJwtOptionsBuilder.cs similarity index 92% rename from src/Genocs.Auth/IJwtOptionsBuilder.cs rename to src/Genocs.Auth/Configurations/IJwtOptionsBuilder.cs index 49e358f5..9675c881 100644 --- a/src/Genocs.Auth/IJwtOptionsBuilder.cs +++ b/src/Genocs.Auth/Configurations/IJwtOptionsBuilder.cs @@ -1,4 +1,4 @@ -namespace Genocs.Auth; +namespace Genocs.Auth.Configurations; public interface IJwtOptionsBuilder { diff --git a/src/Genocs.Auth/Configurations/JwtOptions.cs b/src/Genocs.Auth/Configurations/JwtOptions.cs new file mode 100644 index 00000000..79566a5c --- /dev/null +++ b/src/Genocs.Auth/Configurations/JwtOptions.cs @@ -0,0 +1,83 @@ +namespace Genocs.Auth.Configurations; + +public class JwtOptions +{ + /// + /// Default section name. + /// + public const string Position = "jwt"; + + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + public IEnumerable? AllowAnonymousEndpoints { get; set; } + public CertificateOptions? Certificate { get; set; } + public string? Algorithm { get; set; } + public string? Issuer { get; set; } + public string? IssuerSigningKey { get; set; } + public string? Authority { get; set; } + public string? Audience { get; set; } + + /// + /// This is the Authentication Scheme name. + /// + public string Challenge { get; set; } = "Bearer"; + public string MetadataAddress { get; set; } = "/.well-known/openid-configuration"; + public bool SaveToken { get; set; } = true; + public bool SaveSigninToken { get; set; } + public bool RequireAudience { get; set; } = true; + public bool RequireHttpsMetadata { get; set; } + public bool RequireExpirationTime { get; set; } = true; + public bool RequireSignedTokens { get; set; } = true; + public int ExpiryMinutes { get; set; } + public TimeSpan? Expiry { get; set; } + public string? ValidAudience { get; set; } + public IEnumerable? ValidAudiences { get; set; } + public string? ValidIssuer { get; set; } + public IEnumerable? ValidIssuers { get; set; } + public bool ValidateActor { get; set; } + + /// + /// It defines whether the audience should be validated. + /// Defaults to true. + /// + public bool ValidateAudience { get; set; } = true; + + /// + /// It defines whether the issuer should be validated. + /// Defaults to true. + /// + public bool ValidateIssuer { get; set; } = true; + public bool ValidateLifetime { get; set; } = true; + public bool ValidateTokenReplay { get; set; } + public bool ValidateIssuerSigningKey { get; set; } + + /// + /// It defines whether the token should be refreshed when the issuer key is not found. + /// Defaults to true. + /// + public bool RefreshOnIssuerKeyNotFound { get; set; } = true; + + /// + /// It defines whether the error details should be included in the response. + /// Defaults to true. + /// + public bool IncludeErrorDetails { get; set; } = true; + public string? AuthenticationType { get; set; } + public string? NameClaimType { get; set; } + + /// + /// The claim type that will be used to determine the user's roles. + /// Defaults to "Role". + /// + public string RoleClaimType { get; set; } = "Role"; + + public class CertificateOptions + { + public string? Location { get; set; } + public string? RawData { get; set; } + public string? Password { get; set; } + } +} \ No newline at end of file diff --git a/src/Genocs.Auth/DisabledAuthenticationPolicyEvaluator.cs b/src/Genocs.Auth/DisabledAuthenticationPolicyEvaluator.cs index 9723b386..85cc1ab8 100644 --- a/src/Genocs.Auth/DisabledAuthenticationPolicyEvaluator.cs +++ b/src/Genocs.Auth/DisabledAuthenticationPolicyEvaluator.cs @@ -17,8 +17,11 @@ internal sealed class DisabledAuthenticationPolicyEvaluator : IPolicyEvaluator /// public Task AuthenticateAsync(AuthorizationPolicy policy, HttpContext context) { - var authenticationTicket = new AuthenticationTicket(new ClaimsPrincipal(), - new AuthenticationProperties(), JwtBearerDefaults.AuthenticationScheme); + var authenticationTicket = new AuthenticationTicket( + new ClaimsPrincipal(), + new AuthenticationProperties(), + JwtBearerDefaults.AuthenticationScheme); + return Task.FromResult(AuthenticateResult.Success(authenticationTicket)); } @@ -30,8 +33,11 @@ public Task AuthenticateAsync(AuthorizationPolicy policy, Ht /// /// /// - public Task AuthorizeAsync(AuthorizationPolicy policy, - AuthenticateResult authenticationResult, HttpContext context, object resource) + public Task AuthorizeAsync( + AuthorizationPolicy policy, + AuthenticateResult authenticationResult, + HttpContext context, + object resource) { return Task.FromResult(PolicyAuthorizationResult.Success()); } diff --git a/src/Genocs.Auth/Extensions.cs b/src/Genocs.Auth/Extensions.cs index eaeb7a6e..05e93b9f 100644 --- a/src/Genocs.Auth/Extensions.cs +++ b/src/Genocs.Auth/Extensions.cs @@ -1,11 +1,15 @@ +using Genocs.Auth.Configurations; using Genocs.Auth.Handlers; using Genocs.Auth.Services; using Genocs.Core.Builders; +using Genocs.Security.Services; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization.Policy; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; +using Microsoft.IdentityModel.Protocols; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Tokens; using System.Security.Cryptography.X509Certificates; using System.Text; @@ -14,23 +18,26 @@ namespace Genocs.Auth; public static class Extensions { - private const string SectionName = "jwt"; private const string RegistryName = "auth"; - public static IGenocsBuilder AddJwt(this IGenocsBuilder builder, string sectionName = SectionName, - Action? optionsFactory = null) + public static IGenocsBuilder AddJwt( + this IGenocsBuilder builder, + string sectionName = JwtOptions.Position, + Action? optionsFactory = null) { if (string.IsNullOrWhiteSpace(sectionName)) { - sectionName = SectionName; + sectionName = JwtOptions.Position; } var options = builder.GetOptions(sectionName); return builder.AddJwt(options, optionsFactory); } - private static IGenocsBuilder AddJwt(this IGenocsBuilder builder, JwtOptions options, - Action? optionsFactory = null) + private static IGenocsBuilder AddJwt( + this IGenocsBuilder builder, + JwtOptions options, + Action? optionsFactory = null) { if (!builder.TryRegister(RegistryName)) { @@ -42,7 +49,7 @@ private static IGenocsBuilder AddJwt(this IGenocsBuilder builder, JwtOptions opt builder.Services.AddSingleton(); builder.Services.AddTransient(); - if (options.AuthenticationDisabled) + if (!options.Enabled) { builder.Services.AddSingleton(); } @@ -63,6 +70,7 @@ private static IGenocsBuilder AddJwt(this IGenocsBuilder builder, JwtOptions opt SaveSigninToken = options.SaveSigninToken, RequireExpirationTime = options.RequireExpirationTime, RequireSignedTokens = options.RequireSignedTokens, + RoleClaimType = options.RoleClaimType, ClockSkew = TimeSpan.Zero }; @@ -71,28 +79,28 @@ private static IGenocsBuilder AddJwt(this IGenocsBuilder builder, JwtOptions opt tokenValidationParameters.AuthenticationType = options.AuthenticationType; } - var hasCertificate = false; + bool hasCertificate = false; if (options.Certificate is not null) { - X509Certificate2 certificate = null; - var password = options.Certificate.Password; - var hasPassword = !string.IsNullOrWhiteSpace(password); + X509Certificate2? certificate = null; + string? password = options.Certificate.Password; + bool hasPassword = !string.IsNullOrWhiteSpace(password); if (!string.IsNullOrWhiteSpace(options.Certificate.Location)) { certificate = hasPassword ? new X509Certificate2(options.Certificate.Location, password) : new X509Certificate2(options.Certificate.Location); - var keyType = certificate.HasPrivateKey ? "with private key" : "with public key only"; + string keyType = certificate.HasPrivateKey ? "with private key" : "with public key only"; Console.WriteLine($"Loaded X.509 certificate from location: '{options.Certificate.Location}' {keyType}."); } if (!string.IsNullOrWhiteSpace(options.Certificate.RawData)) { - var rawData = Convert.FromBase64String(options.Certificate.RawData); + byte[] rawData = Convert.FromBase64String(options.Certificate.RawData); certificate = hasPassword ? new X509Certificate2(rawData, password) : new X509Certificate2(rawData); - var keyType = certificate.HasPrivateKey ? "with private key" : "with public key only"; + string keyType = certificate.HasPrivateKey ? "with private key" : "with public key only"; Console.WriteLine($"Loaded X.509 certificate from raw data {keyType}."); } @@ -105,11 +113,12 @@ private static IGenocsBuilder AddJwt(this IGenocsBuilder builder, JwtOptions opt hasCertificate = true; tokenValidationParameters.IssuerSigningKey = new X509SecurityKey(certificate); - var actionType = certificate.HasPrivateKey ? "issuing" : "validating"; + string actionType = certificate.HasPrivateKey ? "issuing" : "validating"; Console.WriteLine($"Using X.509 certificate for {actionType} tokens."); } } + // If no certificate is provided, use symmetric encryption. if (!string.IsNullOrWhiteSpace(options.IssuerSigningKey) && !hasCertificate) { if (string.IsNullOrWhiteSpace(options.Algorithm) || hasCertificate) @@ -117,7 +126,7 @@ private static IGenocsBuilder AddJwt(this IGenocsBuilder builder, JwtOptions opt options.Algorithm = SecurityAlgorithms.HmacSha256; } - var rawKey = Encoding.UTF8.GetBytes(options.IssuerSigningKey); + byte[] rawKey = Encoding.UTF8.GetBytes(options.IssuerSigningKey); tokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(rawKey); Console.WriteLine("Using symmetric encryption for issuing tokens."); } @@ -135,8 +144,9 @@ private static IGenocsBuilder AddJwt(this IGenocsBuilder builder, JwtOptions opt builder.Services .AddAuthentication(o => { - o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + o.DefaultAuthenticateScheme = options.Challenge; + o.DefaultChallengeScheme = options.Challenge; + o.DefaultScheme = options.Challenge; }) .AddJwtBearer(o => { @@ -162,17 +172,102 @@ private static IGenocsBuilder AddJwt(this IGenocsBuilder builder, JwtOptions opt return builder; } + /// + /// Enable OpenId Connect Authentication. + /// It can be used with Firebase Authentication. + /// + /// The Genocs builder. + /// The configuration section name. + /// The Genocs builder you can use for chain. + public static IGenocsBuilder AddOpenIdJwt( + this IGenocsBuilder builder, + string sectionName = JwtOptions.Position) + { + + JwtOptions options = builder.Configuration.GetOptions(sectionName); + + string metadataAddress = $"{options.Issuer}{options.MetadataAddress}"; + var configurationManager = new ConfigurationManager(metadataAddress, new OpenIdConnectConfigurationRetriever()); + + builder.Services + .AddAuthentication(o => + { + o.DefaultAuthenticateScheme = options.Challenge; + o.DefaultChallengeScheme = options.Challenge; + o.DefaultScheme = options.Challenge; + }) + .AddJwtBearer(o => + { + o.IncludeErrorDetails = options.IncludeErrorDetails; + o.RefreshOnIssuerKeyNotFound = options.RefreshOnIssuerKeyNotFound; + o.MetadataAddress = metadataAddress; + o.ConfigurationManager = configurationManager; + o.Audience = options.Audience; + }); + + return builder; + } + + /// + /// It adds the private key JWT authentication. + /// + /// The Genocs builder. + /// The optional section name. Default name: 'jwt'. + /// The Genocs builder you can use for chaining. + /// Whenever mandatory data like 'IssuerSigningKey' is missing. + public static IGenocsBuilder AddPrivateKeyJwt( + this IGenocsBuilder builder, + string sectionName = JwtOptions.Position) + { + if (string.IsNullOrWhiteSpace(sectionName)) + { + sectionName = JwtOptions.Position; + } + + JwtOptions options = builder.Configuration.GetOptions(sectionName); + + if (string.IsNullOrWhiteSpace(options.IssuerSigningKey)) + { + throw new InvalidOperationException("Issuer signing key is missing."); + } + + SecurityKey signingKey = SecurityKeyBuilder.CreateRsaSecurityKey(options.IssuerSigningKey); + + builder.Services + .AddAuthentication(o => + { + o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddJwtBearer(o => + { + o.SaveToken = options.SaveToken; + o.RequireHttpsMetadata = options.RequireHttpsMetadata; + o.TokenValidationParameters = new TokenValidationParameters() + { + IssuerSigningKey = signingKey, + ValidateAudience = options.ValidateAudience, + ValidAudience = options.ValidAudience, + ValidateIssuer = options.ValidateIssuer, + ValidIssuer = options.ValidIssuer, + ValidateLifetime = options.ValidateLifetime, + ValidateIssuerSigningKey = options.ValidateIssuerSigningKey + }; + }); + + return builder; + } + public static IApplicationBuilder UseAccessTokenValidator(this IApplicationBuilder app) => app.UseMiddleware(); } /// -/// DateExtensions extension method +/// DateExtensions extension method. /// internal static class DateExtensions { /// - /// ToTimestamp support function + /// ToTimestamp support function. /// /// /// diff --git a/src/Genocs.Auth/Genocs.Auth.csproj b/src/Genocs.Auth/Genocs.Auth.csproj index 8b9b60a8..924792ef 100644 --- a/src/Genocs.Auth/Genocs.Auth.csproj +++ b/src/Genocs.Auth/Genocs.Auth.csproj @@ -1,67 +1,45 @@  - - net6.0;net7.0 - enable - enable - Genocs.Auth - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The authorization library useful to build .NET Core projects. - The authorization library useful to build .NET Core projects. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - - - - - True - \ - - - True - \ - - - True - \ - - - - - - - - - - - - - - - - - - - - - - - - - + + net8.0;net7.0;net6.0 + Genocs.Auth + Genocs.Auth + Genocs.Auth + The authorization library useful to build .NET Core projects. + The authorization library useful to build .NET Core projects. + true + 5.0.0 + Nocco Giovanni Emanuele + microservice microservices solid solid-principles authentication genocs + README_NUGET.md + Aligned to the ecosystem + True + latest + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Genocs.Auth/Handlers/JwtHandler.cs b/src/Genocs.Auth/Handlers/JwtHandler.cs index 2af2b6bc..36e197e0 100644 --- a/src/Genocs.Auth/Handlers/JwtHandler.cs +++ b/src/Genocs.Auth/Handlers/JwtHandler.cs @@ -1,3 +1,4 @@ +using Genocs.Auth.Configurations; using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; @@ -22,7 +23,7 @@ internal sealed class JwtHandler : IJwtHandler private readonly JwtOptions _options; private readonly TokenValidationParameters _tokenValidationParameters; private readonly SigningCredentials _signingCredentials; - private readonly string _issuer; + private readonly string? _issuer; public JwtHandler(JwtOptions options, TokenValidationParameters tokenValidationParameters) { @@ -43,7 +44,17 @@ public JwtHandler(JwtOptions options, TokenValidationParameters tokenValidationP _issuer = options.Issuer; } - public JsonWebToken CreateToken(string userId, + /// + /// Creates a new token. + /// + /// + /// + /// + /// + /// + /// It is thrown when mandatory data is empty. + public JsonWebToken CreateToken( + string userId, string? role = null, string? audience = null, IDictionary>? claims = null) @@ -61,6 +72,7 @@ public JsonWebToken CreateToken(string userId, new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new(JwtRegisteredClaimNames.Iat, now.ToTimestamp().ToString()), }; + if (!string.IsNullOrWhiteSpace(role)) { jwtClaims.Add(new Claim(ClaimTypes.Role, role)); @@ -91,10 +103,9 @@ public JsonWebToken CreateToken(string userId, claims: jwtClaims, notBefore: now, expires: expires, - signingCredentials: _signingCredentials - ); + signingCredentials: _signingCredentials); - var token = new JwtSecurityTokenHandler().WriteToken(jwt); + string token = new JwtSecurityTokenHandler().WriteToken(jwt); return new JsonWebToken { @@ -114,8 +125,11 @@ public JsonWebToken CreateToken(string userId, /// public JsonWebTokenPayload? GetTokenPayload(string accessToken) { - _jwtSecurityTokenHandler.ValidateToken(accessToken, _tokenValidationParameters, - out var validatedSecurityToken); + _jwtSecurityTokenHandler.ValidateToken( + accessToken, + _tokenValidationParameters, + out var validatedSecurityToken); + if (validatedSecurityToken is not JwtSecurityToken jwt) { return null; diff --git a/src/Genocs.Auth/IJwtHandler.cs b/src/Genocs.Auth/IJwtHandler.cs index 45f3706a..5720e6fa 100644 --- a/src/Genocs.Auth/IJwtHandler.cs +++ b/src/Genocs.Auth/IJwtHandler.cs @@ -1,27 +1,28 @@ namespace Genocs.Auth; /// -/// IJwtHandler interface definition +/// IJwtHandler interface definition. /// public interface IJwtHandler { /// - /// It allows to create a new JsonWebToken + /// It allows to create a new JsonWebToken. /// - /// The userId - /// The role - /// The audience - /// The claims - /// The JsonWebToken just created - JsonWebToken CreateToken(string userId, - string? role = null, - string? audience = null, - IDictionary>? claims = null); + /// The userId. + /// The role. + /// The audience. + /// The claims. + /// The JsonWebToken just created. + JsonWebToken CreateToken( + string userId, + string? role = null, + string? audience = null, + IDictionary>? claims = null); /// - /// Get the JsonWebTokenPayload from the accessToken + /// Get the JsonWebTokenPayload from the accessToken. /// - /// The access token string value - /// The JsonWebTokenPayload + /// The access token string value. + /// The JsonWebTokenPayload. JsonWebTokenPayload? GetTokenPayload(string accessToken); } \ No newline at end of file diff --git a/src/Genocs.Auth/JsonWebToken.cs b/src/Genocs.Auth/JsonWebToken.cs index 6a3a6b66..bdf2eefc 100644 --- a/src/Genocs.Auth/JsonWebToken.cs +++ b/src/Genocs.Auth/JsonWebToken.cs @@ -1,6 +1,5 @@ namespace Genocs.Auth; - /// /// The JSON Web Token definition. /// @@ -15,7 +14,7 @@ public class JsonWebToken /// Gets or sets the refresh token. /// public string? RefreshToken { get; set; } - + /// /// Gets or sets the access token expiration. /// @@ -34,5 +33,5 @@ public class JsonWebToken /// /// The claims. /// - public IDictionary>? Claims { get; set; } + public IDictionary>? Claims { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Auth/JsonWebTokenPayload.cs b/src/Genocs.Auth/JsonWebTokenPayload.cs index 68dfe0f4..b3dcfdc1 100644 --- a/src/Genocs.Auth/JsonWebTokenPayload.cs +++ b/src/Genocs.Auth/JsonWebTokenPayload.cs @@ -1,27 +1,27 @@ namespace Genocs.Auth; /// -/// The JsonWebToken payload +/// The JsonWebToken payload. /// public class JsonWebTokenPayload { /// - /// The subject + /// The subject. /// public string? Subject { get; set; } /// - /// The Identity Role + /// The Identity Role. /// public string? Role { get; set; } /// - /// The expiration ticks + /// The expiration ticks. /// public long Expires { get; set; } /// - /// List of claims + /// List of claims. /// public IDictionary>? Claims { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Auth/JwtAuthAttribute.cs b/src/Genocs.Auth/JwtAuthAttribute.cs index c3b4715e..96439a83 100644 --- a/src/Genocs.Auth/JwtAuthAttribute.cs +++ b/src/Genocs.Auth/JwtAuthAttribute.cs @@ -4,7 +4,8 @@ public class JwtAuthAttribute : AuthAttribute { public const string AuthenticationScheme = "Bearer"; - public JwtAuthAttribute(string policy = "") : base(AuthenticationScheme, policy) + public JwtAuthAttribute(string policy = "") + : base(AuthenticationScheme, policy) { } } \ No newline at end of file diff --git a/src/Genocs.Auth/JwtOptions.cs b/src/Genocs.Auth/JwtOptions.cs deleted file mode 100644 index b3f2aaf4..00000000 --- a/src/Genocs.Auth/JwtOptions.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace Genocs.Auth; - -public class JwtOptions -{ - public bool AuthenticationDisabled { get; set; } - public IEnumerable AllowAnonymousEndpoints { get; set; } - public CertificateOptions Certificate { get; set; } - public string Algorithm { get; set; } - public string Issuer { get; set; } - public string IssuerSigningKey { get; set; } - public string Authority { get; set; } - public string Audience { get; set; } - public string Challenge { get; set; } = "Bearer"; - public string MetadataAddress { get; set; } - public bool SaveToken { get; set; } = true; - public bool SaveSigninToken { get; set; } - public bool RequireAudience { get; set; } = true; - public bool RequireHttpsMetadata { get; set; } = true; - public bool RequireExpirationTime { get; set; } = true; - public bool RequireSignedTokens { get; set; } = true; - public int ExpiryMinutes { get; set; } - public TimeSpan? Expiry { get; set; } - public string ValidAudience { get; set; } - public IEnumerable ValidAudiences { get; set; } - public string ValidIssuer { get; set; } - public IEnumerable ValidIssuers { get; set; } - public bool ValidateActor { get; set; } - public bool ValidateAudience { get; set; } = true; - public bool ValidateIssuer { get; set; } = true; - public bool ValidateLifetime { get; set; } = true; - public bool ValidateTokenReplay { get; set; } - public bool ValidateIssuerSigningKey { get; set; } - public bool RefreshOnIssuerKeyNotFound { get; set; } = true; - public bool IncludeErrorDetails { get; set; } = true; - public string AuthenticationType { get; set; } - public string NameClaimType { get; set; } - public string RoleClaimType { get; set; } - - public class CertificateOptions - { - public string Location { get; set; } - public string RawData { get; set; } - public string Password { get; set; } - } -} \ No newline at end of file diff --git a/src/Genocs.Auth/README.md b/src/Genocs.Auth/README_NUGET.md similarity index 65% rename from src/Genocs.Auth/README.md rename to src/Genocs.Auth/README_NUGET.md index a840446a..86096c0e 100644 --- a/src/Genocs.Auth/README.md +++ b/src/Genocs.Auth/README_NUGET.md @@ -3,7 +3,7 @@ This package contains a set of functionalities to handling authorization logic as JWT. First of all I have to say thanks to devmentors. -The libraries are built using .NET7. +The libraries are built using net8, net7, net6. ## Description @@ -37,7 +37,39 @@ Following are the project settings needed to enable monitoring ## Release notes -### [2023-03-12] 5.0.0-preview.5.0 +### [2024-06-15] 6.0.0 +- Added support for Azure Key Vault +- Unified settings + +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 - Implemented MongoDB repository interfaces ### [2023-03-24] 5.0.0 diff --git a/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs b/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs index 5540ba21..8ce4fb58 100644 --- a/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs +++ b/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs @@ -1,3 +1,4 @@ +using Genocs.Auth.Configurations; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Primitives; @@ -10,9 +11,10 @@ internal sealed class InMemoryAccessTokenService : IAccessTokenService private readonly IHttpContextAccessor _httpContextAccessor; private readonly TimeSpan _expires; - public InMemoryAccessTokenService(IMemoryCache cache, - IHttpContextAccessor httpContextAccessor, - JwtOptions jwtOptions) + public InMemoryAccessTokenService( + IMemoryCache cache, + IHttpContextAccessor httpContextAccessor, + JwtOptions jwtOptions) { _cache = cache; _httpContextAccessor = httpContextAccessor; diff --git a/src/Genocs.Common/Options/AppSettings.cs b/src/Genocs.Common/Configurations/AppOptions.cs similarity index 58% rename from src/Genocs.Common/Options/AppSettings.cs rename to src/Genocs.Common/Configurations/AppOptions.cs index 014b578f..79dd7851 100644 --- a/src/Genocs.Common/Options/AppSettings.cs +++ b/src/Genocs.Common/Configurations/AppOptions.cs @@ -1,42 +1,47 @@ -namespace Genocs.Common.Options; +namespace Genocs.Common.Configurations; /// -/// The application settings +/// The application settings. /// -public class AppSettings +public class AppOptions { /// - /// Default section name + /// Default section name. /// - public const string Position = "App"; + public const string Position = "app"; /// - /// Application name + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + /// + /// Application name. /// public string? Name { get; set; } /// - /// Service name + /// Service name. /// public string? Service { get; set; } /// - /// The instance of the service + /// The instance of the service. /// public string? Instance { get; set; } /// - /// The application version + /// The application version. /// public string? Version { get; set; } /// - /// It defines whether the banner is shown into the console at startup time or not + /// It defines whether the banner is shown into the console at startup time or not. /// public bool DisplayBanner { get; set; } /// - /// It defines whether the application version is shown into the console at startup time or not + /// It defines whether the application version is shown into the console at startup time or not. /// public bool DisplayVersion { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Common/Genocs.Common.csproj b/src/Genocs.Common/Genocs.Common.csproj index a118ec23..2ade0ec7 100644 --- a/src/Genocs.Common/Genocs.Common.csproj +++ b/src/Genocs.Common/Genocs.Common.csproj @@ -1,45 +1,20 @@ - + - - netstandard2.1 - enable - enable - Genocs.Common - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The commons components library useful to build .NET Core projects. - The commons components library useful to build .NET Core projects. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - - - - - True - \ - - - True - \ - - - True - \ - - + + net8.0;net7.0;net6.0 + Genocs.Common + Genocs.Common + Genocs.Common + The Genocs Library - Common components. + The common components to build .NET Core projects along with Genocs Library. + true + 5.0.0 + Nocco Giovanni Emanuele + microservice microservices solid solid-principles genocs + README_NUGET.md + Aligned to the ecosystem + True + latest + diff --git a/src/Genocs.Common/README.md b/src/Genocs.Common/README_NUGET.md similarity index 56% rename from src/Genocs.Common/README.md rename to src/Genocs.Common/README_NUGET.md index 36aac719..ddab0eb4 100644 --- a/src/Genocs.Common/README.md +++ b/src/Genocs.Common/README_NUGET.md @@ -15,17 +15,5 @@ Please check the GitHub repository getting more info. ## Release notes -### [2023-03-12] 5.0.0-preview.5.0 -- Implemented MongoDB repository interfaces +Release notes can be found in the [CHANGELOG](https://github.com/Genocs/genocs-library/blob/b98629b4001aad7a2123190cfd4cc63ae7b6f292/CHANGELOG.md) file. -### [2023-03-24] 5.0.0 -- New Architecture - -### [2023-03-12] 3.1.0 -- Added Builders - -### [2023-03-12] 3.0.0 -- Refactory to implement CQRS pattern - -### [2023-03-04] 2.4.1 -- Updated System.Text.Json \ No newline at end of file diff --git a/src/Genocs.Common/Types/DecoratorAttribute.cs b/src/Genocs.Common/Types/DecoratorAttribute.cs index 07794b3e..fdfba28b 100644 --- a/src/Genocs.Common/Types/DecoratorAttribute.cs +++ b/src/Genocs.Common/Types/DecoratorAttribute.cs @@ -1,8 +1,9 @@ namespace Genocs.Common.Types; /// -/// DecoratorAttribute placeholder +/// DecoratorAttribute placeholder. /// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] public class DecoratorAttribute : Attribute { } \ No newline at end of file diff --git a/src/Genocs.Common/Types/Extensions.cs b/src/Genocs.Common/Types/Extensions.cs index ba2548ed..485790dd 100644 --- a/src/Genocs.Common/Types/Extensions.cs +++ b/src/Genocs.Common/Types/Extensions.cs @@ -5,12 +5,12 @@ namespace Genocs.Common.Types; /// -/// Type extensions +/// Type extensions. /// public static class Extensions { /// - /// GetDefaultInstance + /// GetDefaultInstance. /// /// /// @@ -21,9 +21,9 @@ public static class Extensions return string.Empty; } - var defaultValueCache = new Dictionary(); + var defaultValueCache = new Dictionary(); - if (TryGetDefaultValue(type, out var instance, defaultValueCache)) + if (TryGetDefaultValue(type, out object? instance, defaultValueCache)) { return instance; } @@ -32,17 +32,17 @@ public static class Extensions } public static object SetDefaultInstanceProperties(this object instance) - => SetDefaultInstanceProperties(instance, new Dictionary()); + => SetDefaultInstanceProperties(instance, new Dictionary()); - private static object SetDefaultInstanceProperties(object instance, Dictionary defaultValueCache) + private static object SetDefaultInstanceProperties(object instance, Dictionary defaultValueCache) { - defaultValueCache ??= new Dictionary(); + defaultValueCache ??= new Dictionary(); var type = instance.GetType(); foreach (var propertyInfo in type.GetProperties(BindingFlags.Instance)) { - if (TryGetDefaultValue(propertyInfo.PropertyType, out var defaultValue, defaultValueCache)) + if (TryGetDefaultValue(propertyInfo.PropertyType, out object? defaultValue, defaultValueCache)) { SetValue(propertyInfo, instance, defaultValue); } @@ -51,7 +51,7 @@ private static object SetDefaultInstanceProperties(object instance, Dictionary defaultValueCache) + private static bool TryGetDefaultValue(Type type, out object? defaultValue, Dictionary defaultValueCache) { if (defaultValueCache.TryGetValue(type, out defaultValue)) { @@ -116,6 +116,7 @@ private static bool TryGetDefaultValue(Type type, out object? defaultValue, Dict return false; } + // TODO: Refactor this to remove the use of FormatterServices defaultValue = FormatterServices.GetUninitializedObject(type); defaultValueCache[type] = defaultValue; @@ -158,7 +159,7 @@ private static bool TryGetCollectionDefaultValue(Type type, out object? defaultV return true; } - private static void SetValue(PropertyInfo propertyInfo, object instance, object value) + private static void SetValue(PropertyInfo propertyInfo, object instance, object? value) { if (propertyInfo.CanWrite) { diff --git a/src/Genocs.Common/Types/HiddenAttribute.cs b/src/Genocs.Common/Types/HiddenAttribute.cs index 70d846fb..69480b4f 100644 --- a/src/Genocs.Common/Types/HiddenAttribute.cs +++ b/src/Genocs.Common/Types/HiddenAttribute.cs @@ -1,8 +1,7 @@ namespace Genocs.Common.Types; - /// -/// It defines whether the attribute is hidden or not +/// It defines whether the attribute is hidden or not. /// [AttributeUsage(AttributeTargets.Property)] public class HiddenAttribute : Attribute diff --git a/src/Genocs.Common/Types/IIdentifiable.cs b/src/Genocs.Common/Types/IIdentifiable.cs index 0da19004..e7ff2efb 100644 --- a/src/Genocs.Common/Types/IIdentifiable.cs +++ b/src/Genocs.Common/Types/IIdentifiable.cs @@ -1,19 +1,13 @@ namespace Genocs.Common.Types; /// -/// Identifiable interface definition +/// Identifiable interface definition. /// -/// +/// The Identifiable type. public interface IIdentifiable { /// - /// The Id getter + /// The Id getter. /// T Id { get; } - - /// - /// Checks if this entity is transient (not persisted to database and it has not an ). - /// - /// True, if this entity is transient - bool IsTransient(); } \ No newline at end of file diff --git a/src/Genocs.Common/Types/IInitializer.cs b/src/Genocs.Common/Types/IInitializer.cs index a0390f2b..6c9ff8f9 100644 --- a/src/Genocs.Common/Types/IInitializer.cs +++ b/src/Genocs.Common/Types/IInitializer.cs @@ -1,13 +1,13 @@ namespace Genocs.Common.Types; /// -/// Initializer interface definition +/// Initializer interface definition. /// public interface IInitializer { /// - /// Standard initializer + /// Standard initializer. /// - /// + /// The Task. Task InitializeAsync(); } \ No newline at end of file diff --git a/src/Genocs.Common/Types/IStartupInitializer.cs b/src/Genocs.Common/Types/IStartupInitializer.cs index b78123b4..c6061dcb 100644 --- a/src/Genocs.Common/Types/IStartupInitializer.cs +++ b/src/Genocs.Common/Types/IStartupInitializer.cs @@ -1,14 +1,13 @@ namespace Genocs.Common.Types; - /// -/// Startup initializer interface definition +/// Startup initializer interface definition. /// public interface IStartupInitializer : IInitializer { /// - /// It allows to add an initializer + /// It allows to add an initializer. /// - /// + /// The initializer. void AddInitializer(IInitializer initializer); } \ No newline at end of file diff --git a/src/Genocs.Common/Types/MessageAttribute.cs b/src/Genocs.Common/Types/MessageAttribute.cs index 5dbc8107..224aa048 100644 --- a/src/Genocs.Common/Types/MessageAttribute.cs +++ b/src/Genocs.Common/Types/MessageAttribute.cs @@ -1,43 +1,43 @@ namespace Genocs.Common.Types; /// -/// MessageAttribute class +/// MessageAttribute class. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] public class MessageAttribute : Attribute { /// - /// The Exchange used by the system + /// The Exchange used by the system. /// public string Exchange { get; } /// - /// The Topic used to send/receive message + /// The Topic used to send/receive message. /// public string Topic { get; } /// - /// The queue + /// The queue. /// public string Queue { get; } /// - /// The type of the queue + /// The type of the queue. /// public string QueueType { get; } /// - /// The error + /// The error. /// public string ErrorQueue { get; } /// - /// The subscriptionId + /// The subscriptionId. /// public string SubscriptionId { get; } /// - /// Standard constructor + /// Standard constructor. /// /// /// @@ -45,8 +45,13 @@ public class MessageAttribute : Attribute /// /// /// - public MessageAttribute(string? exchange = null, string? topic = null, string? queue = null, - string? queueType = null, string? errorQueue = null, string? subscriptionId = null) + public MessageAttribute( + string? exchange = null, + string? topic = null, + string? queue = null, + string? queueType = null, + string? errorQueue = null, + string? subscriptionId = null) { Exchange = exchange ?? string.Empty; Topic = topic ?? string.Empty; diff --git a/src/Genocs.Common/Types/PublicContractAttribute.cs b/src/Genocs.Common/Types/PublicContractAttribute.cs index 9f669695..e3ab8e01 100644 --- a/src/Genocs.Common/Types/PublicContractAttribute.cs +++ b/src/Genocs.Common/Types/PublicContractAttribute.cs @@ -1,7 +1,7 @@ namespace Genocs.Common.Types; /// -/// PublicContractAttribute class +/// PublicContractAttribute class. /// [AttributeUsage(AttributeTargets.Class)] public class PublicContractAttribute : Attribute diff --git a/src/Genocs.Core.Demo.Contracts/Genocs.Core.Demo.Contracts.csproj b/src/Genocs.Core.Demo.Contracts/Genocs.Core.Demo.Contracts.csproj index ab4014c3..69c454cc 100644 --- a/src/Genocs.Core.Demo.Contracts/Genocs.Core.Demo.Contracts.csproj +++ b/src/Genocs.Core.Demo.Contracts/Genocs.Core.Demo.Contracts.csproj @@ -1,21 +1,17 @@  - - net7.0 - enable - enable - false - false - + + net8.0 + false + false + + + + - - - - - - - - + + + diff --git a/src/Genocs.Core.Demo.Contracts/OrderRequestEvent.cs b/src/Genocs.Core.Demo.Contracts/OrderRequestEvent.cs index fe8bbb30..76c37cf5 100644 --- a/src/Genocs.Core.Demo.Contracts/OrderRequestEvent.cs +++ b/src/Genocs.Core.Demo.Contracts/OrderRequestEvent.cs @@ -7,7 +7,6 @@ public class OrderRequest : IEvent public string OrderId { get; set; } = Guid.NewGuid().ToString(); public string UserId { get; set; } = default!; public DateTime TimeStamp { get; set; } = DateTime.UtcNow; - public string CardToken { get; set; } = default!; public decimal Amount { get; set; } public string Currency { get; set; } = default!; diff --git a/src/Genocs.Core.Demo.Domain/Aggregates/BaseAggregate.cs b/src/Genocs.Core.Demo.Domain/Aggregates/BaseAggregate.cs new file mode 100644 index 00000000..f0eede68 --- /dev/null +++ b/src/Genocs.Core.Demo.Domain/Aggregates/BaseAggregate.cs @@ -0,0 +1,24 @@ +using Genocs.Core.Domain.Entities; +using Genocs.Core.Domain.Entities.Auditing; +using Genocs.Persistence.MongoDb.Domain.Entities; +using MongoDB.Bson; + +namespace Genocs.Core.Demo.Domain.Aggregates; + +/// +/// Base aggregate class used for all entities. +/// This class is used to define some common properties for all entities. +/// +public class BaseAggregate : AggregateRoot, IMongoDbEntity, IHasCreationTime +{ + public BaseAggregate() + { + // Set the unique identifier for the entity generates a new ObjectId. + Id = ObjectId.GenerateNewId(); + } + + /// + /// Creation time of this entity. + /// + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; +} \ No newline at end of file diff --git a/src/Genocs.Core.Demo.Domain/Aggregates/Order.cs b/src/Genocs.Core.Demo.Domain/Aggregates/Order.cs index c7b7d5f6..0a8c3080 100644 --- a/src/Genocs.Core.Demo.Domain/Aggregates/Order.cs +++ b/src/Genocs.Core.Demo.Domain/Aggregates/Order.cs @@ -1,26 +1,21 @@ -using Genocs.Core.Domain.Entities; -using Genocs.Core.Domain.Entities.Auditing; -using Genocs.Core.Domain.Repositories; -using Genocs.Persistence.MongoDb.Repositories; +using Genocs.Core.Domain.Repositories; +using MongoDB.Bson; namespace Genocs.Core.Demo.Domain.Aggregates; [TableMapping("Orders")] -public class Order : AggregateRoot, IMongoDbEntity, IHasCreationTime +public class Order : BaseAggregate { - public Order(string orderId, string userId, string cardToken, decimal amount, string currency) + public Order(string orderId, string userId, decimal amount, string currency) { OrderId = orderId; UserId = userId; - CardToken = cardToken; Amount = amount; Currency = currency; } - public string OrderId { get; set; } = Guid.NewGuid().ToString(); + public string OrderId { get; set; } = ObjectId.GenerateNewId().ToString(); public string UserId { get; set; } = default!; - public DateTime CreationTime { get; set; } = DateTime.UtcNow; - public string CardToken { get; set; } = default!; public decimal Amount { get; set; } public string Currency { get; set; } = default!; } diff --git a/src/Genocs.Core.Demo.Domain/Aggregates/User.cs b/src/Genocs.Core.Demo.Domain/Aggregates/User.cs index 2b241a42..94f13764 100644 --- a/src/Genocs.Core.Demo.Domain/Aggregates/User.cs +++ b/src/Genocs.Core.Demo.Domain/Aggregates/User.cs @@ -1,31 +1,20 @@ -using Genocs.Common.Types; -using Genocs.Core.Domain.Entities.Auditing; -using Genocs.Core.Domain.Repositories; +using Genocs.Core.Domain.Repositories; namespace Genocs.Core.Demo.Domain.Aggregates; [TableMapping("Users")] -public class User : IIdentifiable, IHasCreationTime +public class User : BaseAggregate { - - public Guid Id { get; set; } public string UserId { get; set; } = default!; - public DateTime CreationTime { get; set; } = DateTime.UtcNow; public string Username { get; set; } = default!; public decimal Age { get; set; } public string Country { get; set; } = default!; public User(string userId, string username, decimal age, string country) { - Id = Guid.NewGuid(); UserId = userId; Username = username; Age = age; Country = country; } - - public bool IsTransient() - { - return true; - } } diff --git a/src/Genocs.Core.Demo.Domain/Genocs.Core.Demo.Domain.csproj b/src/Genocs.Core.Demo.Domain/Genocs.Core.Demo.Domain.csproj index ae4a0a79..39f3fc59 100644 --- a/src/Genocs.Core.Demo.Domain/Genocs.Core.Demo.Domain.csproj +++ b/src/Genocs.Core.Demo.Domain/Genocs.Core.Demo.Domain.csproj @@ -1,21 +1,19 @@  - - net7.0 - enable - enable - false - false - + + net8.0 + false + false + - - - - + + + + - - - - + + + + diff --git a/src/Genocs.Core.Demo.Infrastructure/Genocs.Core.Demo.Infrastructure.csproj b/src/Genocs.Core.Demo.Infrastructure/Genocs.Core.Demo.Infrastructure.csproj index d32391db..dc1d6876 100644 --- a/src/Genocs.Core.Demo.Infrastructure/Genocs.Core.Demo.Infrastructure.csproj +++ b/src/Genocs.Core.Demo.Infrastructure/Genocs.Core.Demo.Infrastructure.csproj @@ -1,11 +1,9 @@  - - net7.0 - enable - enable - false - false - + + net8.0 + false + false + diff --git a/src/Genocs.Core.Demo.WebApi/Configurations/ExternalServiceOptions.cs b/src/Genocs.Core.Demo.WebApi/Configurations/ExternalServiceOptions.cs new file mode 100644 index 00000000..ef8b1db6 --- /dev/null +++ b/src/Genocs.Core.Demo.WebApi/Configurations/ExternalServiceOptions.cs @@ -0,0 +1,41 @@ +using System.ComponentModel.DataAnnotations; + +namespace Genocs.Core.Demo.WebApi.Configurations; + +/// +/// This class implements Options pattern with Validation. +/// +public class ExternalServiceOptions : IValidatableObject +{ + /// + /// Default section name. + /// + public const string Position = "externalService"; + + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + public string Caller { get; set; } = default!; + public string Private { get; set; } = default!; + public string Public { get; set; } = default!; + + public IEnumerable Validate(ValidationContext validationContext) + { + if (string.IsNullOrWhiteSpace(Caller)) + { + yield return new ValidationResult("No Caller defined in ExternalService config", new[] { nameof(Caller) }); + } + + if (string.IsNullOrWhiteSpace(Private)) + { + yield return new ValidationResult("No Private defined in ExternalService config", new[] { nameof(Private) }); + } + + if (string.IsNullOrWhiteSpace(Public)) + { + yield return new ValidationResult("No Public defined in ExternalService config", new[] { nameof(Public) }); + } + } +} diff --git a/src/Genocs.Core.Demo.WebApi/Configurations/RabbitMQOptions.cs b/src/Genocs.Core.Demo.WebApi/Configurations/RabbitMQOptions.cs new file mode 100644 index 00000000..029daa12 --- /dev/null +++ b/src/Genocs.Core.Demo.WebApi/Configurations/RabbitMQOptions.cs @@ -0,0 +1,22 @@ +namespace Genocs.Core.Demo.WebApi.Configurations; + +public class RabbitMQOptions +{ + /// + /// Default section name. + /// + public const string Position = "rabbitMQ"; + + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + public string HostName { get; set; } = default!; + public string VirtualHost { get; set; } = default!; + public string UserName { get; set; } = default!; + public string Password { get; set; } = default!; + public int Port { get; set; } = default!; + public bool UseSSL { get; set; } + +} diff --git a/src/Genocs.Core.Demo.WebApi/Configurations/SecretOptions.cs b/src/Genocs.Core.Demo.WebApi/Configurations/SecretOptions.cs new file mode 100644 index 00000000..367033fc --- /dev/null +++ b/src/Genocs.Core.Demo.WebApi/Configurations/SecretOptions.cs @@ -0,0 +1,20 @@ +namespace Genocs.Core.Demo.WebApi.Configurations; + +public class SecretOptions +{ + /// + /// Default section name. + /// + public const string Position = "secrets"; + + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + /// + /// This is an example of a secret. That should be stored in a secure way. + /// + public string? Secret { get; set; } + +} diff --git a/src/Genocs.Core.Demo.WebApi/Configurations/VerificationServiceOptions.cs b/src/Genocs.Core.Demo.WebApi/Configurations/VerificationServiceOptions.cs new file mode 100644 index 00000000..d33ab6a4 --- /dev/null +++ b/src/Genocs.Core.Demo.WebApi/Configurations/VerificationServiceOptions.cs @@ -0,0 +1,16 @@ +namespace Genocs.Core.Demo.WebApi.Configurations; + +public class VerificationServiceOptions +{ + /// + /// Default section name. + /// + public const string Position = "verificationService"; + + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + public string ApiKey { get; set; } = default!; +} diff --git a/src/Genocs.Core.Demo.WebApi/Controllers/AuthorizedController.cs b/src/Genocs.Core.Demo.WebApi/Controllers/AuthorizedController.cs new file mode 100644 index 00000000..8d496c13 --- /dev/null +++ b/src/Genocs.Core.Demo.WebApi/Controllers/AuthorizedController.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Net.Mime; + +namespace Genocs.Core.Demo.WebApi.Controllers; + +[ApiController] +[Route("[controller]")] +[Authorize] +public class AuthorizedController : ControllerBase +{ + private readonly ILogger _logger; + + public AuthorizedController(ILogger logger) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + [HttpGet("GetAuthorized")] + [Consumes(MediaTypeNames.Application.Json)] + [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] + public async Task PostSubmitDemoCommand() + { + return await Task.Run(() => Ok($"Done! Authorization is: {HttpContext.Request.Headers["Authorization"]}")); + } +} diff --git a/src/Genocs.Core.Demo.WebApi/Controllers/ExternalApiController.cs b/src/Genocs.Core.Demo.WebApi/Controllers/ExternalApiController.cs new file mode 100644 index 00000000..6d6b975a --- /dev/null +++ b/src/Genocs.Core.Demo.WebApi/Controllers/ExternalApiController.cs @@ -0,0 +1,34 @@ +using Genocs.Core.Demo.WebApi.Infrastructure.Services; +using Microsoft.AspNetCore.Mvc; +using System.Net.Mime; + +namespace Genocs.Core.Demo.WebApi.Controllers; + +[ApiController] +[Route("[controller]")] +public class ExternalApiController : ControllerBase +{ + private readonly ILogger _logger; + private readonly IExternalServiceClient _externalServiceClient; + + public ExternalApiController( + ILogger logger, + IExternalServiceClient externalServiceClient) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _externalServiceClient = externalServiceClient ?? throw new ArgumentNullException(nameof(externalServiceClient)); + } + + [HttpGet("")] + [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] + [Consumes(MediaTypeNames.Application.Json)] + public async Task HomeAsync() + => await Task.Run(() => Ok("done")); + + [HttpPost("")] + [Produces("application/json")] + [ProducesResponseType(typeof(IssuingResponse), StatusCodes.Status200OK)] + [Consumes(MediaTypeNames.Application.Json)] + public async Task PostIssueAsync(IssuingRequest request) + => Ok(await _externalServiceClient.IssueAsync(request)); +} diff --git a/src/Genocs.Core.Demo.WebApi/Controllers/HomeController.cs b/src/Genocs.Core.Demo.WebApi/Controllers/HomeController.cs index 823bf70f..d9a29280 100644 --- a/src/Genocs.Core.Demo.WebApi/Controllers/HomeController.cs +++ b/src/Genocs.Core.Demo.WebApi/Controllers/HomeController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Genocs.Core.Demo.WebApi.Configurations; +using Microsoft.AspNetCore.Mvc; namespace Genocs.Core.Demo.WebApi.Controllers; @@ -6,6 +7,13 @@ namespace Genocs.Core.Demo.WebApi.Controllers; [Route("")] public class HomeController : ControllerBase { + public readonly SecretOptions _secretSettings; + + public HomeController(SecretOptions secretSettings) + { + _secretSettings = secretSettings ?? throw new ArgumentNullException(nameof(secretSettings)); + } + [HttpGet] [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] public IActionResult Get() @@ -15,4 +23,9 @@ public IActionResult Get() [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] public IActionResult Ping() => Ok("pong"); + + [HttpGet("secret")] + [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] + public IActionResult GetSecret() + => Ok($"Read: {_secretSettings.Secret}"); } diff --git a/src/Genocs.Core.Demo.WebApi/Controllers/MongoDbController.cs b/src/Genocs.Core.Demo.WebApi/Controllers/MongoDbController.cs index 69e79b7b..5fbd6f2d 100644 --- a/src/Genocs.Core.Demo.WebApi/Controllers/MongoDbController.cs +++ b/src/Genocs.Core.Demo.WebApi/Controllers/MongoDbController.cs @@ -1,8 +1,6 @@ using Genocs.Core.Demo.Domain.Aggregates; -using Genocs.Core.Domain.Repositories; -using Genocs.Persistence.MongoDb.Repositories; +using Genocs.Persistence.MongoDb.Domain.Repositories; using Microsoft.AspNetCore.Mvc; -using MongoDB.Bson; namespace Genocs.Core.Demo.WebApi.Controllers; diff --git a/src/Genocs.Core.Demo.WebApi/Controllers/ServiceBusController.cs b/src/Genocs.Core.Demo.WebApi/Controllers/ServiceBusController.cs index 2b4d2d63..f8f874a6 100644 --- a/src/Genocs.Core.Demo.WebApi/Controllers/ServiceBusController.cs +++ b/src/Genocs.Core.Demo.WebApi/Controllers/ServiceBusController.cs @@ -22,7 +22,6 @@ public ServiceBusController(ILogger logger, IAzureServiceB _azureServiceBusTopic = azureServiceBusTopic; } - [HttpPost("SendToQueueAzureServiceBusQueue")] [Consumes(MediaTypeNames.Application.Json)] [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] @@ -61,7 +60,6 @@ public async Task PostSubmitOrder() { OrderId = Guid.NewGuid().ToString(), UserId = Guid.NewGuid().ToString(), - CardToken = "6500-1254-2548", Amount = 10.0M, Currency = "EUR", Basket = new List() diff --git a/src/Genocs.Core.Demo.WebApi/Genocs.Core.Demo.WebApi.csproj b/src/Genocs.Core.Demo.WebApi/Genocs.Core.Demo.WebApi.csproj index 41790cf1..8b857be6 100644 --- a/src/Genocs.Core.Demo.WebApi/Genocs.Core.Demo.WebApi.csproj +++ b/src/Genocs.Core.Demo.WebApi/Genocs.Core.Demo.WebApi.csproj @@ -1,29 +1,46 @@  - - net7.0 - enable - enable - false - false - __genocs - Linux - ..\.. - + + net8.0 + false + false + genocs + Linux + ..\.. + - - - - - - + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Genocs.Core.Demo.WebApi/Infrastructure/Extensions/BuilderExtensions.cs b/src/Genocs.Core.Demo.WebApi/Infrastructure/Extensions/BuilderExtensions.cs new file mode 100644 index 00000000..3d955bc0 --- /dev/null +++ b/src/Genocs.Core.Demo.WebApi/Infrastructure/Extensions/BuilderExtensions.cs @@ -0,0 +1,51 @@ +using Genocs.Core.Builders; +using Genocs.Core.Demo.WebApi.Configurations; +using Genocs.Core.Demo.WebApi.Infrastructure.Services; +using Genocs.HTTP; +using Genocs.Security; +using Genocs.WebApi.Security; + +namespace Genocs.Core.Demo.WebApi.Infrastructure.Extensions; + +public static class BuilderExtensions +{ + /// + /// Extension method to add application services to the DI container. + /// + /// The Genocs builder. + /// The Genocs builder to be used for chain. + public static IGenocsBuilder AddApplicationServices(this IGenocsBuilder builder) + { + // Add the Security services + builder.AddSecurity(); + + // Add the Certification Authentication + builder.AddCertificateAuthentication(); + + // Add the Genocs Http client + builder.AddHttpClient(); + + // Add the External Service settings + // Option 1: In this way ExternalServiceSettings is available for dependency injection. + // var settings = new ExternalServiceSettings(); + // builder.Configuration.GetSection(ExternalServiceSettings.Position).Bind(settings); + // builder.Services.AddSingleton(settings); + + // Option 2: In this way ExternalServiceSettings is available for dependency injection. By using IOptions you can access the settings. + builder.Services.AddOptions() + .BindConfiguration(ExternalServiceOptions.Position) + .ValidateDataAnnotations() + .ValidateOnStart(); + + // builder.Services.AddSingleton, ConfigureWebApiSettings>(); + ExternalServiceOptions settings = builder.Configuration.GetOptions(ExternalServiceOptions.Position); + builder.Services.AddSingleton(settings); + + // Add the External Service http Client + builder.Services.AddTransient(); + + // builder.Services.Configure(builder.Configuration.GetSection(ExternalServiceSettings.Position)); + + return builder; + } +} diff --git a/src/Genocs.Core.Demo.WebApi/Infrastructure/Extensions/ServiceCollectionExtensions.cs b/src/Genocs.Core.Demo.WebApi/Infrastructure/Extensions/ServiceCollectionExtensions.cs index ef682b8b..de255e2f 100644 --- a/src/Genocs.Core.Demo.WebApi/Infrastructure/Extensions/ServiceCollectionExtensions.cs +++ b/src/Genocs.Core.Demo.WebApi/Infrastructure/Extensions/ServiceCollectionExtensions.cs @@ -1,5 +1,5 @@ -using Genocs.Core.Demo.WebApi.Options; -using Genocs.ServiceBusAzure.Options; +using Genocs.Core.Demo.WebApi.Configurations; +using Genocs.ServiceBusAzure.Configurations; using Genocs.ServiceBusAzure.Queues; using Genocs.ServiceBusAzure.Queues.Interfaces; using Genocs.ServiceBusAzure.Topics; @@ -21,7 +21,7 @@ public static IServiceCollection AddAzureServiceBus(this IServiceCollection serv public static IServiceCollection AddAzureServiceBusTopic(this IServiceCollection services, IConfiguration configuration) { // Register IOptions - services.Configure(configuration.GetSection(AzureServiceBusTopicSettings.Position)); + services.Configure(configuration.GetSection(AzureServiceBusTopicOptions.Position)); // HOW to Register TopicSettings instead of IOptions ////var topicSetting = new TopicOptions(); @@ -36,7 +36,7 @@ public static IServiceCollection AddAzureServiceBusTopic(this IServiceCollection public static IServiceCollection AddAzureServiceBusQueue(this IServiceCollection services, IConfiguration configuration) { // Register IOptions - services.Configure(configuration.GetSection(AzureServiceBusQueueSettings.Position)); + services.Configure(configuration.GetSection(AzureServiceBusQueueOptions.Position)); // HOW to Register QueueSettings instead of IOptions ////var queueSetting = new QueueSettings(); @@ -50,8 +50,11 @@ public static IServiceCollection AddAzureServiceBusQueue(this IServiceCollection public static IServiceCollection AddCustomMassTransit(this IServiceCollection services, IConfiguration configuration) { - var rabbitMQSettings = new RabbitMQSettings(); - configuration.GetSection(RabbitMQSettings.Position).Bind(rabbitMQSettings); + RabbitMQOptions rabbitMQSettings = new RabbitMQOptions(); + configuration.GetSection(RabbitMQOptions.Position).Bind(rabbitMQSettings); + + // This is another way to get the RabbitMQOptions + // RabbitMQOptions? rabbitMQSettingsV2 = configuration.GetSection(RabbitMQOptions.Position).Get(); services.AddSingleton(rabbitMQSettings); @@ -62,17 +65,24 @@ public static IServiceCollection AddCustomMassTransit(this IServiceCollection se x.UsingRabbitMq((context, cfg) => { cfg.ConfigureEndpoints(context); - //cfg.UseHealthCheck(context); - cfg.Host(rabbitMQSettings.HostName, rabbitMQSettings.VirtualHost, - h => - { - h.Username(rabbitMQSettings.UserName); - h.Password(rabbitMQSettings.Password); - } - ); + + // cfg.UseHealthCheck(context); + cfg.Host( + rabbitMQSettings.HostName, + rabbitMQSettings.VirtualHost, + h => + { + h.Username(rabbitMQSettings.UserName); + h.Password(rabbitMQSettings.Password); + }); }); }); return services; } + + public static IApplicationBuilder UseFirebaseAuthentication(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } } diff --git a/src/Genocs.Core.Demo.WebApi/Infrastructure/FirebaseAuthenticationMiddleware.cs b/src/Genocs.Core.Demo.WebApi/Infrastructure/FirebaseAuthenticationMiddleware.cs new file mode 100644 index 00000000..5d0e953a --- /dev/null +++ b/src/Genocs.Core.Demo.WebApi/Infrastructure/FirebaseAuthenticationMiddleware.cs @@ -0,0 +1,55 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; + +namespace Genocs.Core.Demo.WebApi.Infrastructure; + +/// +/// Middleware authentication. Used to implement OpenId JWT implementation. +/// +public class FirebaseAuthenticationMiddleware +{ + private readonly RequestDelegate _next; + + public FirebaseAuthenticationMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task Invoke(HttpContext context) + { + string authHeader = context.Request.Headers["Authorization"].ToString(); + + if (authHeader != null && authHeader.StartsWith("Bearer ")) + { + string token = authHeader.Substring("Bearer ".Length).Trim(); + + try + { + var handler = new JwtSecurityTokenHandler(); + var jsonToken = handler.ReadToken(token) as JwtSecurityToken; + + var jwtToken = new JwtSecurityToken(token); + var payload = jwtToken.Payload; + var claims = new[] + { + new Claim(ClaimTypes.NameIdentifier, payload["user_id"].ToString()), + new Claim(ClaimTypes.Name, payload["name"].ToString()), + + // Add more claims as needed + }; + + var identity = new ClaimsIdentity(claims, "Firebase"); + context.User = new ClaimsPrincipal(identity); + } + catch (Exception ex) + { + // Token validation failed + context.Response.StatusCode = 401; + await context.Response.WriteAsync("Unauthorized"); + return; + } + } + + await _next(context); + } +} \ No newline at end of file diff --git a/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/ExternalServiceClient.cs b/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/ExternalServiceClient.cs new file mode 100644 index 00000000..6ca25116 --- /dev/null +++ b/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/ExternalServiceClient.cs @@ -0,0 +1,84 @@ +using Genocs.Core.Demo.WebApi.Configurations; +using Genocs.HTTP; +using Genocs.HTTP.Configurations; +using Genocs.Security; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; + +namespace Genocs.Core.Demo.WebApi.Infrastructure.Services; + +/// +/// The External WebApi client implementation. +/// +public class ExternalServiceClient : IExternalServiceClient +{ + private readonly IHttpClient _client; + private readonly string _url; + private readonly IHasher _hasher; + private readonly ExternalServiceOptions _externalServiceOptions; + + /// + /// The standard constructor. + /// + /// The http client. + /// The Hash service. + /// The http client settings. + /// The security settings. + public ExternalServiceClient( + IHttpClient client, + IHasher hasher, + HttpClientOptions httpClientSettings, + IOptions options) + { + _client = client ?? throw new ArgumentNullException(nameof(client)); + _hasher = hasher ?? throw new ArgumentNullException(nameof(hasher)); + _externalServiceOptions = options.Value ?? throw new ArgumentNullException(nameof(options)); + + if (httpClientSettings is null) + { + throw new ArgumentNullException(nameof(httpClientSettings)); + } + + string? url = httpClientSettings?.Services?["ca_issuer"]; + + if (string.IsNullOrWhiteSpace(url)) + { + throw new Exception("products http client settings cannot be null"); + } + + _url = url; + } + + private void SetHeaders(string request) + { + string hash = _hasher.Hash(request, _externalServiceOptions.Private); + string headerData = $"Credential={_externalServiceOptions.Public}, Signature={hash}"; + _client.SetHeaders(h => h.TryAddWithoutValidation("Authorization", headerData)); + } + + /// + /// Send a request for gift card issuing. + /// + /// The issuing Request. + /// The issuing Response containing the gift card details. + public async Task IssueAsync(IssuingRequest request) + { + string serializedRequest = JsonConvert.SerializeObject(request); + SetHeaders(serializedRequest); + using (var content = new StringContent(serializedRequest, System.Text.Encoding.UTF8, "application/json")) + { + return await _client.PostAsync($"{_url}/redemptions/gift-cards/{_externalServiceOptions.Caller}/direct-issue", content); + } + } + + /// + /// Get the product based on the productId. + /// + /// The redemption request. + /// The redemption Response. + public async Task RedeemAsync(RedemptionRequest request) + { + // SetHeaders(callerId); + return await _client.PostAsync($"{_url}/redemptions/gift-cards/custom/redeem", request); + } +} \ No newline at end of file diff --git a/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/IExternalServiceClient.Models.cs b/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/IExternalServiceClient.Models.cs new file mode 100644 index 00000000..aeb41244 --- /dev/null +++ b/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/IExternalServiceClient.Models.cs @@ -0,0 +1,151 @@ +using Newtonsoft.Json; + +namespace Genocs.Core.Demo.WebApi.Infrastructure.Services; + +/// +/// The request object for the issuing endpoint. +/// +public class IssuingRequest +{ + [JsonProperty("external_reference")] + public string? ExternalReference; + + [JsonProperty("loyalty_currency")] + public string? Currency; + + [JsonProperty("fiat_amount")] + public decimal Amount; + + [JsonProperty("partner_code")] + public string? PartnerCode; +} + +/// +/// The request object for the redemption endpoint. +/// +public class RedemptionRequest +{ + +} + +public class Metadata +{ + [JsonProperty("memberId")] + public string? MemberId { get; set; } + + [JsonProperty("requestId")] + public string? RequestId { get; set; } + + [JsonProperty("email")] + public string? Email { get; set; } +} + +/// +/// The response object for the issuing endpoint. +/// +public class IssuingResponse +{ + [JsonProperty("code")] + public CodeResponse? Code { get; set; } + + [JsonProperty("gift_card")] + public GiftCard? GiftCard { get; set; } +} + +/// +/// The Code data available in the response object. +/// +public class CodeResponse +{ + [JsonProperty("id")] + public string? Id { get; set; } + + [JsonProperty("code")] + public string? Code { get; set; } + + [JsonProperty("barcode_string")] + public string? Barcode { get; set; } + + [JsonProperty("barcode_format")] + public string? BarcodeFormat { get; set; } + + [JsonProperty("discount_amount")] + public int DiscountAmount { get; set; } + + [JsonProperty("delivery_url")] + public object? DeliveryUrl { get; set; } + + [JsonProperty("expiration_date")] + public string? ExpirationDate { get; set; } + + [JsonProperty("fiat_amount")] + public string? FiatAmount { get; set; } + + [JsonProperty("fiat_balance")] + public string? FiatBalance { get; set; } + + [JsonProperty("fiat_currency")] + public string? FiatCurrency { get; set; } + + [JsonProperty("issuance_details")] + public IssuanceDetails? IssuanceDetails { get; set; } + + [JsonProperty("pin")] + public string? Pin { get; set; } + + [JsonProperty("redemption_details")] + public object? RedemptionDetails { get; set; } + + [JsonProperty("redemption_status")] + public string? RedemptionStatus { get; set; } +} + +public class CustomSettingsData +{ +} + +public class GiftCard +{ + [JsonProperty("barcode_format")] + public string? BarcodeFormat { get; set; } + + public List countries { get; set; } + public string currency { get; set; } + public CustomSettingsData custom_settings_data { get; set; } + public string denomination_type { get; set; } + public List denominations { get; set; } + public string description { get; set; } + + // public int discount_value { get; set; } + public string expiration_policy { get; set; } + public string id { get; set; } + public object image { get; set; } + public string maximum_value { get; set; } + public string minimum_value { get; set; } + public string name { get; set; } + public string provider { get; set; } + public string redeem_instructions_html { get; set; } + public string terms_and_conditions_html { get; set; } + public string terms_and_conditions_url { get; set; } + public string type { get; set; } +} + +public class IssuanceDetails +{ + public CustomSettingsData custom_settings_data { get; set; } + public string external_reference { get; set; } + public LoyaltyInformation loyalty_information { get; set; } + + // public Metadata metadata { get; set; } + public string original_external_reference { get; set; } + public string original_code { get; set; } + public string partner_code { get; set; } +} + +public class LoyaltyInformation +{ + // public int? loyalty_amount { get; set; } + public string loyalty_currency { get; set; } + public string member_id { get; set; } + public string partner_name { get; set; } +} \ No newline at end of file diff --git a/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/IExternalServiceClient.cs b/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/IExternalServiceClient.cs new file mode 100644 index 00000000..443112b0 --- /dev/null +++ b/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/IExternalServiceClient.cs @@ -0,0 +1,21 @@ +namespace Genocs.Core.Demo.WebApi.Infrastructure.Services; + +/// +/// The External WebApi client definition. +/// +public interface IExternalServiceClient +{ + /// + /// Send a request for gift card issuing. + /// + /// The issuing Request. + /// The issuing Response containing the gift card details. + Task IssueAsync(IssuingRequest request); + + /// + /// Get the product based on the productId. + /// + /// The redemption request. + /// The redemption Response. + Task RedeemAsync(RedemptionRequest request); +} diff --git a/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/IVerificationServiceClient.Models.cs b/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/IVerificationServiceClient.Models.cs new file mode 100644 index 00000000..221a71f2 --- /dev/null +++ b/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/IVerificationServiceClient.Models.cs @@ -0,0 +1,66 @@ +using Newtonsoft.Json; + +namespace Genocs.Core.Demo.WebApi.Infrastructure.Services; + +/// +/// The Internal User verification request. +/// +public class VerificationApiRequest +{ + [JsonProperty("type")] + public string? Type { get; set; } + + [JsonProperty("joinedDate")] + public string? JoinedDate { get; set; } + + [JsonProperty("email")] + public string? Email { get; set; } + + [JsonProperty("personDetails")] + public PersonDetailsRequest? Details { get; set; } +} + +public class PersonDetailsRequest +{ + [JsonProperty("firstName")] + public string? FirstName { get; set; } + + [JsonProperty("lastName")] + public string? LastName { get; set; } + + [JsonProperty("dob")] + public string? DateOfBirth { get; set; } + + [JsonProperty("gender")] + public string? Gender { get; set; } + + [JsonProperty("nationality")] + public string? Nationality { get; set; } +} + +/// +/// The internal User verification response. +/// +public class VerificationApiResponse +{ + [JsonProperty("id")] + public string? Id { get; set; } + + [JsonProperty("createdAt")] + public string? CreatedAt { get; set; } + + [JsonProperty("updatedAt")] + public string? UpdatedAt { get; set; } + + [JsonProperty("type")] + public string? Type { get; set; } + + [JsonProperty("joinedDate")] + public string? JoinedDate { get; set; } + + [JsonProperty("email")] + public string? Email { get; set; } + + [JsonProperty("personDetails")] + public PersonDetailsRequest? Details { get; set; } +} \ No newline at end of file diff --git a/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/IVerificationServiceClient.cs b/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/IVerificationServiceClient.cs new file mode 100644 index 00000000..7129c59f --- /dev/null +++ b/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/IVerificationServiceClient.cs @@ -0,0 +1,15 @@ +namespace Genocs.Core.Demo.WebApi.Infrastructure.Services; + +/// +/// The External WebApi client definition. +/// This API client is used to verify the user. +/// +public interface IVerificationServiceClient +{ + /// + /// Send a request To verify the user. + /// + /// The request with the user data. + /// The user verification response. + Task VerifyAsync(VerificationApiRequest request); +} diff --git a/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/VerificationServiceClient.cs b/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/VerificationServiceClient.cs new file mode 100644 index 00000000..4820e3ae --- /dev/null +++ b/src/Genocs.Core.Demo.WebApi/Infrastructure/Services/VerificationServiceClient.cs @@ -0,0 +1,65 @@ +using Genocs.Core.Demo.WebApi.Configurations; +using Genocs.HTTP; +using Genocs.HTTP.Configurations; +using Newtonsoft.Json; + +namespace Genocs.Core.Demo.WebApi.Infrastructure.Services; + +/// +/// The Verification WebApi client implementation. +/// +public class VerificationServiceClient : IVerificationServiceClient +{ + private readonly IHttpClient _client; + private readonly string _url; + private readonly VerificationServiceOptions _externalServiceSettings; + + /// + /// The standard constructor. + /// + /// The http client. + /// The http client settings. + /// The security settings. + public VerificationServiceClient( + IHttpClient client, + HttpClientOptions httpClientSettings, + VerificationServiceOptions externalServiceSettings) + { + _client = client ?? throw new ArgumentNullException(nameof(client)); + _externalServiceSettings = externalServiceSettings ?? throw new ArgumentNullException(nameof(externalServiceSettings)); + + if (httpClientSettings is null) + { + throw new ArgumentNullException(nameof(httpClientSettings)); + } + + string? url = httpClientSettings?.Services?["user_verifier"]; + + if (string.IsNullOrWhiteSpace(url)) + { + throw new Exception("user_verifier http client settings cannot be null"); + } + + _url = url; + } + + private void SetHeaders() + { + _client.SetHeaders(h => h.TryAddWithoutValidation("Authorization", _externalServiceSettings.ApiKey)); + } + + /// + /// The method to verify the user. + /// + /// The verification api request. + /// The response. + public async Task VerifyAsync(VerificationApiRequest request) + { + SetHeaders(); + string serializedRequest = JsonConvert.SerializeObject(request); + using (var content = new StringContent(serializedRequest, System.Text.Encoding.UTF8, "application/json")) + { + return await _client.PostAsync($"{_url}/clients", request); + } + } +} \ No newline at end of file diff --git a/src/Genocs.Core.Demo.WebApi/Options/RabbitMQSettings.cs b/src/Genocs.Core.Demo.WebApi/Options/RabbitMQSettings.cs deleted file mode 100644 index 0afe6136..00000000 --- a/src/Genocs.Core.Demo.WebApi/Options/RabbitMQSettings.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Genocs.Core.Demo.WebApi.Options; - -public class RabbitMQSettings -{ - public const string Position = "RabbitMQ"; - - public string HostName { get; set; } = default!; - public string VirtualHost { get; set; } = default!; - public string UserName { get; set; } = default!; - public string Password { get; set; } = default!; - public int Port { get; set; } = default!; - public bool UseSSL { get; set; } - -} diff --git a/src/Genocs.Core.Demo.WebApi/Program.cs b/src/Genocs.Core.Demo.WebApi/Program.cs index f5ea325f..e0b37556 100644 --- a/src/Genocs.Core.Demo.WebApi/Program.cs +++ b/src/Genocs.Core.Demo.WebApi/Program.cs @@ -1,38 +1,36 @@ +// using Genocs.Auth; using Genocs.Core.Builders; +using Genocs.Core.Demo.WebApi.Configurations; using Genocs.Core.Demo.WebApi.Infrastructure.Extensions; using Genocs.Logging; -using Genocs.Monitoring; using Genocs.Persistence.MongoDb.Extensions; +using Genocs.Secrets.AzureKeyVault; +using Genocs.Tracing; using Microsoft.Extensions.Diagnostics.HealthChecks; using Serilog; -using Serilog.Events; +using Genocs.Auth; using System.Reflection; using System.Text.Json.Serialization; -Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .MinimumLevel.Override("Microsoft", LogEventLevel.Information) - .MinimumLevel.Override("MassTransit", LogEventLevel.Information) - .Enrich.FromLogContext() - .WriteTo.Console() - .CreateLogger(); - +StaticLogger.EnsureInitialized(); var builder = WebApplication.CreateBuilder(args); builder.Host + .UseAzureKeyVault() .UseLogging(); -// .UseVault(); - -// add services to DI container var services = builder.Services; services .AddGenocs(builder.Configuration) + .AddJwt() +// .AddOpenIdJwt() + .AddOpenTelemetry() .AddMongoFast() - .RegisterMongoRepositories(Assembly.GetExecutingAssembly()); - + .RegisterMongoRepositories(Assembly.GetExecutingAssembly()) + .AddApplicationServices() + .Build(); services.AddCors(); services.AddControllers().AddJsonOptions(x => @@ -43,6 +41,11 @@ services.AddHealthChecks(); +services.Configure(builder.Configuration.GetSection(SecretOptions.Position)); + +SecretOptions settings = builder.Configuration.GetOptions(SecretOptions.Position); +services.AddSingleton(settings); + services.Configure(options => { options.Delay = TimeSpan.FromSeconds(2); @@ -56,15 +59,10 @@ // Add Masstransit bus configuration services.AddCustomMassTransit(builder.Configuration); - services.AddOptions(); -// Set Custom Open telemetry -services.AddCustomOpenTelemetry(builder.Configuration); - var app = builder.Build(); - // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { @@ -72,7 +70,6 @@ app.UseSwaggerUI(); } - // global cors policy app.UseCors(x => x .SetIsOriginAllowed(origin => true) @@ -80,16 +77,18 @@ .AllowAnyHeader() .AllowCredentials()); - app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization(); +// Use it only if you need to authenticate with Firebase +// app.UseFirebaseAuthentication(); + app.MapControllers(); -app.MapHealthChecks("/healthz"); +app.MapHealthChecks("/hc"); app.Run(); diff --git a/src/Genocs.Core.Demo.WebApi/appsettings.Development.json b/src/Genocs.Core.Demo.WebApi/appsettings.Development.json deleted file mode 100644 index 12fcb59b..00000000 --- a/src/Genocs.Core.Demo.WebApi/appsettings.Development.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Information", - "Microsoft": "Information", - "Microsoft.Hosting.Lifetime": "Information" - } - } -} diff --git a/src/Genocs.Core.Demo.WebApi/appsettings.json b/src/Genocs.Core.Demo.WebApi/appsettings.json index 8bb32dc4..34a70d64 100644 --- a/src/Genocs.Core.Demo.WebApi/appsettings.json +++ b/src/Genocs.Core.Demo.WebApi/appsettings.json @@ -1,49 +1,96 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Information", - "Microsoft": "Information", - "Microsoft.Hosting.Lifetime": "Information" + "app": { + "name": "Demo WebApi", + "service": "Demo WebApi", + "instance": "1", + "version": "v1.0", + "displayBanner": true, + "displayVersion": true + }, + "logger": { + "level": "debug", + "applicationName": "demo-service", + "excludePaths": [ "/ping", "/metrics" ], + "console": { + "enabled": true + }, + "file": { + "enabled": true, + "path": "logs/logs.txt", + "interval": "day" + }, + "azure": { + "enabled": false, + "connectionString": "<>" } }, - "AllowedHosts": "*", - "ConnectionStrings": { - "ApplicationInsights": "" + "monitoring": { + "jaeger": "localhost" }, - "AppSettings": { - "ServiceName": "Demo-WebApi" + "jwt": { + "ValidIssuer": "http://localhost/Auth", + "ValidAudience": "https://localhost:5000", + "Secret": "" }, - "RabbitMQ": { + "_simmetric_jwt": { + "issuerSigningKey": "This is my custom Secret key for authentication S0M3RAN0MS3CR3T!1!MAG1C!1!", + "requireHttpsMetadata": false, + "saveToken": true, + "validateIssuerSigningKey": true, + "validateIssuer": false, + "validateLifetime": true, + "validateAudience": false, + "roleClaimType": "Role" + }, + "rabbitMQ": { "HostName": "localhost", "VirtualHost": "/", "UserName": "guest", "Password": "guest" }, - "AzureServiceBusTopic": { + "azureServiceBusTopic": { "ConnectionString": "Endpoint=sb://xxx.servicebus.windows.net/;SharedAccessKeyName=RMQ-xxxx;SharedAccessKey=xxxx", "TopicName": "topic-name", "SubscriptionName": "subscription-name" }, - "AzureServiceBusQueue": { + "azureServiceBusQueue": { "ConnectionString": "Endpoint=sb://xxx.servicebus.windows.net/;SharedAccessKeyName=RMQ-xxxx;SharedAccessKey=xxxx", "QueueName": "queue-name" }, - "Mongo": { - "ConnectionString": "mongodb://localhost", - "Database": "demo" - }, "mongodb": { - "ConnectionString": "mongodb://localhost", - "Database": "demo", + "connectionString": "mongodb://localhost:27017", + "database": "demo", "enableTracing": true }, - "jwt": { - "ValidIssuer": "http://localhost/Auth", - "ValidAudience": "https://localhost:5000", - "Secret": "<<>>" + "azureKeyVault": { + "enabled": false, + "name": "kv-genocs" + }, + "secrets": { + "Secret": "This is a secret coming from the appsettings.json file" + }, + "httpClient": { + "type": "", + "retries": 2, + "services": { + "ca_issuer": "<<>>" + } + }, + "externalService": { + "caller": "<<>>", + "public": "<<>>", + "private": "<<>>" }, - "Monitoring": { - "Jaeger": "localhost" + "swagger": { + "enabled": true, + "title": "Demo WebApi", + "description": "Demo WebApi", + "version": "v1", + "servers": [ + { + "name": "Genocs", + "email": "giovanni.nocco@genocs.com" + } + ] } -} \ No newline at end of file +} diff --git a/src/Genocs.Core.Demo.Worker/Consumers/SubmitOrderConsumer.cs b/src/Genocs.Core.Demo.Worker/Consumers/SubmitOrderConsumer.cs index 00ad7730..9c320201 100644 --- a/src/Genocs.Core.Demo.Worker/Consumers/SubmitOrderConsumer.cs +++ b/src/Genocs.Core.Demo.Worker/Consumers/SubmitOrderConsumer.cs @@ -1,6 +1,6 @@ using Genocs.Core.Demo.Contracts; using Genocs.Core.Demo.Domain.Aggregates; -using Genocs.Persistence.MongoDb.Repositories; +using Genocs.Persistence.MongoDb.Domain.Repositories; using MassTransit; namespace Genocs.Core.Demo.Worker.Consumers; @@ -19,7 +19,7 @@ public SubmitOrderConsumer(ILogger logger, IMongoDbReposito public async Task Consume(ConsumeContext context) { - Order order = new Order(context.Message.OrderId, context.Message.UserId, "", 1, "EUR"); + Order order = new Order(context.Message.OrderId, context.Message.UserId, 1, "EUR"); await _orderRepository.InsertAsync(order); _logger.LogInformation($"Order {context.Message.OrderId} processed!"); } diff --git a/src/Genocs.Core.Demo.Worker/Genocs.Core.Demo.Worker.csproj b/src/Genocs.Core.Demo.Worker/Genocs.Core.Demo.Worker.csproj index 5c94816b..f3eef8fe 100644 --- a/src/Genocs.Core.Demo.Worker/Genocs.Core.Demo.Worker.csproj +++ b/src/Genocs.Core.Demo.Worker/Genocs.Core.Demo.Worker.csproj @@ -1,36 +1,36 @@  - - net7.0 - enable - enable - false - false - __genocs - Linux - ..\.. - + + net8.0 + false + false + __genocs + Linux + ..\.. + - - - + + + + + - - - - - + + + + + - - - - + + + + + + + + + + + - - - - - - - diff --git a/src/Genocs.Core.Demo.Worker/MassTransitConsoleHostedService.cs b/src/Genocs.Core.Demo.Worker/MassTransitConsoleHostedService.cs index 39f50b58..c38ea277 100644 --- a/src/Genocs.Core.Demo.Worker/MassTransitConsoleHostedService.cs +++ b/src/Genocs.Core.Demo.Worker/MassTransitConsoleHostedService.cs @@ -3,8 +3,9 @@ namespace Genocs.Core.Demo.Worker; /// -/// General purpose worker. Please check the link below for further informations +/// General purpose worker. Please check the link below for further information /// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-7.0&tabs=visual-studio +/// . /// public class MassTransitConsoleHostedService : IHostedService { @@ -20,14 +21,18 @@ public MassTransitConsoleHostedService(IBusControl bus, ILogger { - // Run the hosted service + // Run the hosted service services.AddHostedService(); services .AddGenocs(hostContext.Configuration) + .AddOpenTelemetry() .AddMongoFast() .RegisterMongoRepositories(Assembly.GetExecutingAssembly()); // It registers the repositories that has been overridden. No need in case of standard repository ConfigureMassTransit(services, hostContext.Configuration); - services.AddCustomOpenTelemetry(hostContext.Configuration); }) .Build(); @@ -51,7 +44,6 @@ Log.CloseAndFlush(); - static IServiceCollection ConfigureMassTransit(IServiceCollection services, IConfiguration configuration) { services.TryAddSingleton(KebabCaseEndpointNameFormatter.Instance); @@ -75,7 +67,7 @@ static IServiceCollection RegisterCustomMongoRepository(IServiceCollection servi static void ConfigureBus(IBusRegistrationContext context, IRabbitMqBusFactoryConfigurator configurator) { - //configurator.UseMessageData(new MongoDbMessageDataRepository("mongodb://127.0.0.1", "attachments")); + // configurator.UseMessageData(new MongoDbMessageDataRepository("mongodb://127.0.0.1", "attachments")); //configurator.ReceiveEndpoint(KebabCaseEndpointNameFormatter.Instance.Consumer(), e => //{ @@ -99,7 +91,7 @@ static void ConfigureBus(IBusRegistrationContext context, IRabbitMqBusFactoryCon static void ConfigureAzureServiceBusTopic(IServiceCollection services, IConfiguration configuration) { - services.Configure(configuration.GetSection(AzureServiceBusTopicSettings.Position)); + services.Configure(configuration.GetSection(AzureServiceBusTopicOptions.Position)); services.AddSingleton(); @@ -107,12 +99,11 @@ static void ConfigureAzureServiceBusTopic(IServiceCollection services, IConfigur var topicBus = services.BuildServiceProvider().GetRequiredService(); topicBus.Subscribe>(); - } static void ConfigureAzureServiceBusQueue(IServiceCollection services, IConfiguration configuration) { - services.Configure(configuration.GetSection(AzureServiceBusQueueSettings.Position)); + services.Configure(configuration.GetSection(AzureServiceBusQueueOptions.Position)); services.AddSingleton(); diff --git a/src/Genocs.Core.Demo.Worker/appsettings.json b/src/Genocs.Core.Demo.Worker/appsettings.json index 62530ab2..9ebf71e1 100644 --- a/src/Genocs.Core.Demo.Worker/appsettings.json +++ b/src/Genocs.Core.Demo.Worker/appsettings.json @@ -24,8 +24,8 @@ "QueueName": "queue-name" }, "mongodb": { - "ConnectionString": "mongodb://localhost", - "Database": "demo", + "connectionString": "mongodb://localhost:27017", + "database": "demo", "enableTracing": true }, "Monitoring": { @@ -87,7 +87,7 @@ "Token" ], "console": { - "enabled": true + "enabled": false }, "elk": { "enabled": false, @@ -105,7 +105,7 @@ }, "azure": { "enabled": false, - "connectionString": "" + "connectionString": null }, "tags": {} }, diff --git a/src/Genocs.Core.UnitTests/Genocs.Core.UnitTests.csproj b/src/Genocs.Core.UnitTests/Genocs.Core.UnitTests.csproj index 1603b78b..99505860 100644 --- a/src/Genocs.Core.UnitTests/Genocs.Core.UnitTests.csproj +++ b/src/Genocs.Core.UnitTests/Genocs.Core.UnitTests.csproj @@ -1,23 +1,22 @@  - - net7.0 - enable - enable - false - false - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - + + net8.0 + false + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + \ No newline at end of file diff --git a/src/Genocs.Core.UnitTests/UnitTest1.cs b/src/Genocs.Core.UnitTests/UnitTest1.cs index aef15efe..44fa45bc 100644 --- a/src/Genocs.Core.UnitTests/UnitTest1.cs +++ b/src/Genocs.Core.UnitTests/UnitTest1.cs @@ -1,7 +1,7 @@ -namespace Genocs.Core.UnitTests; - using Xunit; +namespace Genocs.Core.UnitTests; + public class UnitTest1 { [Fact] diff --git a/src/Genocs.Core/Builders/Extensions.cs b/src/Genocs.Core/Builders/Extensions.cs index 790822de..72fa6c39 100644 --- a/src/Genocs.Core/Builders/Extensions.cs +++ b/src/Genocs.Core/Builders/Extensions.cs @@ -1,4 +1,4 @@ -using Genocs.Common.Options; +using Genocs.Common.Configurations; using Genocs.Common.Types; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; @@ -6,57 +6,60 @@ namespace Genocs.Core.Builders; - /// -/// Builders Extensions +/// Builders Extensions. /// public static class Extensions { /// - /// The Builder + /// The Builder. /// - /// - /// - /// + /// The service collection. + /// The configuration. + /// The builder. public static IGenocsBuilder AddGenocs(this IServiceCollection services, IConfiguration? configuration = null) { var builder = GenocsBuilder.Create(services, configuration); - var appOptions = builder.GetOptions(AppSettings.Position); - services.AddSingleton(appOptions); + var settings = builder.GetOptions(AppOptions.Position); + services.AddSingleton(settings); builder.Services.AddMemoryCache(); services.AddSingleton(); - if (!appOptions.DisplayBanner || string.IsNullOrWhiteSpace(appOptions.Name)) + if (!settings.DisplayBanner || string.IsNullOrWhiteSpace(settings.Name)) { return builder; } - var version = appOptions.DisplayVersion ? $" {appOptions.Version}" : string.Empty; - Console.WriteLine(Figgle.FiggleFonts.Doom.Render($"{appOptions.Name}{version}")); + + string version = settings.DisplayVersion ? $" {settings.Version}" : string.Empty; + Console.WriteLine(Figgle.FiggleFonts.Doom.Render(settings.Name + version)); + ConsoleColor current = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Blue; + Console.WriteLine("Runtime Version: {0}", Environment.Version.ToString()); + Console.ForegroundColor = current; return builder; } /// - /// Run the application initializer + /// Run the application initializer. /// - /// - /// + /// The application builder. + /// The application builder. public static IApplicationBuilder UseGenocs(this IApplicationBuilder app) { using var scope = app.ApplicationServices.CreateScope(); var initializer = scope.ServiceProvider.GetRequiredService(); Task.Run(() => initializer.InitializeAsync()).GetAwaiter().GetResult(); - return app; } /// - /// Get option helper method + /// Get option helper method. /// - /// + /// The option type parameter. /// /// - /// + /// The option model or the default options. public static TModel GetOptions(this IConfiguration configuration, string sectionName) where TModel : new() { @@ -66,22 +69,22 @@ public static TModel GetOptions(this IConfiguration configuration, strin } /// - /// Get option helper method + /// Get option helper method. /// - /// - /// - /// - /// - public static TModel GetOptions(this IGenocsBuilder builder, string settingsSectionName) + /// The option type parameter. + /// The builder. + /// The default section name. + /// The option. + public static TModel GetOptions(this IGenocsBuilder builder, string sectionName) where TModel : new() { if (builder.Configuration != null) { - return builder.Configuration.GetOptions(settingsSectionName); + return builder.Configuration.GetOptions(sectionName); } using var serviceProvider = builder.Services.BuildServiceProvider(); var configuration = serviceProvider.GetRequiredService(); - return configuration.GetOptions(settingsSectionName); + return configuration.GetOptions(sectionName); } } \ No newline at end of file diff --git a/src/Genocs.Core/Builders/GenocsBuilder.cs b/src/Genocs.Core/Builders/GenocsBuilder.cs index f3571e83..59c5b25b 100644 --- a/src/Genocs.Core/Builders/GenocsBuilder.cs +++ b/src/Genocs.Core/Builders/GenocsBuilder.cs @@ -1,14 +1,12 @@ -namespace Genocs.Core.Builders; - using Genocs.Common.Types; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using System; using System.Collections.Concurrent; -using System.Collections.Generic; + +namespace Genocs.Core.Builders; /// -/// Genocs builder implementation +/// Genocs builder implementation. /// public sealed class GenocsBuilder : IGenocsBuilder { @@ -18,7 +16,7 @@ public sealed class GenocsBuilder : IGenocsBuilder IServiceCollection IGenocsBuilder.Services => _services; /// - /// The configuration + /// The configuration. /// public IConfiguration? Configuration { get; } @@ -33,7 +31,7 @@ private GenocsBuilder(IServiceCollection services, IConfiguration? configuration public static IGenocsBuilder Create(IServiceCollection services, IConfiguration? configuration = null) => new GenocsBuilder(services, configuration); - public bool TryRegister(string name) + public bool TryRegister(string name) => _registry.TryAdd(name, true); public void AddBuildAction(Action execute) @@ -46,7 +44,8 @@ public void AddInitializer(IInitializer initializer) startupInitializer.AddInitializer(initializer); }); - public void AddInitializer() where TInitializer : IInitializer + public void AddInitializer() + where TInitializer : IInitializer => AddBuildAction(sp => { var initializer = sp.GetRequiredService(); diff --git a/src/Genocs.Core/Builders/IGenocsBuilder.cs b/src/Genocs.Core/Builders/IGenocsBuilder.cs index 4775fe67..5ec0b17e 100644 --- a/src/Genocs.Core/Builders/IGenocsBuilder.cs +++ b/src/Genocs.Core/Builders/IGenocsBuilder.cs @@ -5,33 +5,37 @@ namespace Genocs.Core.Builders; /// -/// The Application builder +/// The Application builder. /// public interface IGenocsBuilder { /// - /// Get the service collection + /// Get the service collection. /// IServiceCollection Services { get; } /// - /// Get the configuration + /// Get the configuration. /// IConfiguration Configuration { get; } /// - /// try to register a service by name + /// try to register a service by name. /// - /// Name of the service trying to register + /// Name of the service trying to register. /// bool TryRegister(string name); /// - /// Build the actions based on the service provider + /// Build the actions based on the service provider. /// /// void AddBuildAction(Action execute); + void AddInitializer(IInitializer initializer); - void AddInitializer() where TInitializer : IInitializer; + + void AddInitializer() + where TInitializer : IInitializer; + IServiceProvider Build(); } \ No newline at end of file diff --git a/src/Genocs.Core/Builders/IServiceId.cs b/src/Genocs.Core/Builders/IServiceId.cs index 41eb29af..7fbec02a 100644 --- a/src/Genocs.Core/Builders/IServiceId.cs +++ b/src/Genocs.Core/Builders/IServiceId.cs @@ -1,12 +1,12 @@ namespace Genocs.Core.Builders; /// -/// The ServiceId interface definition +/// The ServiceId interface definition. /// public interface IServiceId { /// - /// The id of the service as a getter + /// The id of the service as a getter. /// string Id { get; } } \ No newline at end of file diff --git a/src/Genocs.Core/Builders/ServiceId.cs b/src/Genocs.Core/Builders/ServiceId.cs index 8a9742c8..6b2200f5 100644 --- a/src/Genocs.Core/Builders/ServiceId.cs +++ b/src/Genocs.Core/Builders/ServiceId.cs @@ -1,7 +1,5 @@ namespace Genocs.Core.Builders; -using System; - internal class ServiceId : IServiceId { public string Id { get; } = $"{Guid.NewGuid():N}"; diff --git a/src/Genocs.Core/Builders/StartupInitializer.cs b/src/Genocs.Core/Builders/StartupInitializer.cs index 5ec96c38..fdba6116 100644 --- a/src/Genocs.Core/Builders/StartupInitializer.cs +++ b/src/Genocs.Core/Builders/StartupInitializer.cs @@ -1,33 +1,33 @@ using Genocs.Common.Types; +using Genocs.Core.Collections.Extensions; namespace Genocs.Core.Builders; /// -/// StartupInitializer implementation +/// StartupInitializer implementation. /// public class StartupInitializer : IStartupInitializer { private readonly IList _initializers = new List(); - /// - /// Add new initializer if not present + /// Add new initializer if not present. /// - /// + /// The initializer to be added. public void AddInitializer(IInitializer initializer) { - if (initializer is null || _initializers.Contains(initializer)) + if (initializer is null) { return; } - _initializers.Add(initializer); + _initializers.AddIfNotContains(initializer); } /// - /// Run the initializer + /// Run the initializer. /// - /// + /// The task. public async Task InitializeAsync() { foreach (var initializer in _initializers) diff --git a/src/Genocs.Core/CQRS/Commands/Dispatchers/CommandDispatcher.cs b/src/Genocs.Core/CQRS/Commands/Dispatchers/CommandDispatcher.cs index c5ae356a..86b1f6cf 100644 --- a/src/Genocs.Core/CQRS/Commands/Dispatchers/CommandDispatcher.cs +++ b/src/Genocs.Core/CQRS/Commands/Dispatchers/CommandDispatcher.cs @@ -1,26 +1,22 @@ -namespace Genocs.Core.CQRS.Commands.Dispatchers -{ - using Genocs.Core.CQRS.Commands; - using Microsoft.Extensions.DependencyInjection; - using System; - using System.Threading; - using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; - /// - /// CommandDispatcher implementation - /// - internal sealed class CommandDispatcher : ICommandDispatcher - { - private readonly IServiceProvider _serviceProvider; +namespace Genocs.Core.CQRS.Commands.Dispatchers; - public CommandDispatcher(IServiceProvider serviceProvider) - => _serviceProvider = serviceProvider; +/// +/// CommandDispatcher implementation. +/// +internal sealed class CommandDispatcher : ICommandDispatcher +{ + private readonly IServiceProvider _serviceProvider; - public async Task SendAsync(T command, CancellationToken cancellationToken = default) where T : class, ICommand - { - await using var scope = _serviceProvider.CreateAsyncScope(); - var handler = scope.ServiceProvider.GetRequiredService>(); - await handler.HandleAsync(command, cancellationToken); - } + public CommandDispatcher(IServiceProvider serviceProvider) + => _serviceProvider = serviceProvider; + + public async Task SendAsync(T command, CancellationToken cancellationToken = default) + where T : class, ICommand + { + await using var scope = _serviceProvider.CreateAsyncScope(); + var handler = scope.ServiceProvider.GetRequiredService>(); + await handler.HandleAsync(command, cancellationToken); } } \ No newline at end of file diff --git a/src/Genocs.Core/CQRS/Commands/Extensions.cs b/src/Genocs.Core/CQRS/Commands/Extensions.cs index a9a15b37..84cc6b25 100644 --- a/src/Genocs.Core/CQRS/Commands/Extensions.cs +++ b/src/Genocs.Core/CQRS/Commands/Extensions.cs @@ -6,15 +6,15 @@ namespace Genocs.Core.CQRS.Commands; /// -/// Extension method +/// Extension methods. /// public static class Extensions { /// - /// AddCommandHandlers + /// Add all the Command handlers to the DI container. /// - /// - /// + /// The builder. + /// The builder to be used for chaining pattern. public static IGenocsBuilder AddCommandHandlers(this IGenocsBuilder builder) { builder.Services.Scan(s => @@ -28,14 +28,13 @@ public static IGenocsBuilder AddCommandHandlers(this IGenocsBuilder builder) } /// - /// AddInMemoryCommandDispatcher + /// Add the In Memory command dispatcher to the DI container. /// - /// - /// + /// The builder. + /// The builder to be used for chaining pattern. public static IGenocsBuilder AddInMemoryCommandDispatcher(this IGenocsBuilder builder) { builder.Services.AddSingleton(); return builder; - } } \ No newline at end of file diff --git a/src/Genocs.Core/CQRS/Commands/ICommand.cs b/src/Genocs.Core/CQRS/Commands/ICommand.cs index 1e359e23..681357c7 100644 --- a/src/Genocs.Core/CQRS/Commands/ICommand.cs +++ b/src/Genocs.Core/CQRS/Commands/ICommand.cs @@ -1,9 +1,8 @@ -namespace Genocs.Core.CQRS.Commands +namespace Genocs.Core.CQRS.Commands; + +/// +/// CQRS command interface. +/// +public interface ICommand { - /// - /// CQRS command interface - /// - public interface ICommand - { - } } \ No newline at end of file diff --git a/src/Genocs.Core/CQRS/Commands/ICommandDispatcher.cs b/src/Genocs.Core/CQRS/Commands/ICommandDispatcher.cs index 4b8076c6..ccb7d157 100644 --- a/src/Genocs.Core/CQRS/Commands/ICommandDispatcher.cs +++ b/src/Genocs.Core/CQRS/Commands/ICommandDispatcher.cs @@ -1,20 +1,17 @@ -namespace Genocs.Core.CQRS.Commands -{ - using System.Threading; - using System.Threading.Tasks; +namespace Genocs.Core.CQRS.Commands; +/// +/// Command dispatcher interface. +/// +public interface ICommandDispatcher +{ /// - /// Command dispatcher interface + /// SendAsync. /// - public interface ICommandDispatcher - { - /// - /// SendAsync - /// - /// - /// - /// - /// - Task SendAsync(T command, CancellationToken cancellationToken = default) where T : class, ICommand; - } + /// The type of command. + /// The command object. + /// The cancellation token. + /// The task. + Task SendAsync(T command, CancellationToken cancellationToken = default) + where T : class, ICommand; } diff --git a/src/Genocs.Core/CQRS/Commands/ICommandHandler.cs b/src/Genocs.Core/CQRS/Commands/ICommandHandler.cs index 6c5f9c61..7e90ec19 100644 --- a/src/Genocs.Core/CQRS/Commands/ICommandHandler.cs +++ b/src/Genocs.Core/CQRS/Commands/ICommandHandler.cs @@ -1,34 +1,32 @@ namespace Genocs.Core.CQRS.Commands; -using System.Threading; -using System.Threading.Tasks; - /// -/// CQRS command handler interface +/// CQRS command handler interface. /// -/// -public interface ICommandHandler where TCommand : class, ICommand +/// The type of command. +public interface ICommandHandler + where TCommand : class, ICommand { /// - /// HandleAsync + /// HandleAsync. /// - /// - /// - /// + /// The command + /// The Cancellation token. + /// Async Task. Task HandleAsync(TCommand command, CancellationToken cancellationToken = default); } - /// -/// Legacy CQRS command handler interface +/// Legacy CQRS command handler interface. /// -/// -public interface ICommandHandlerLegacy where T : ICommand +/// The type of command. +public interface ICommandHandlerLegacy + where T : ICommand { /// - /// Legacy HandleAsync + /// Legacy HandleAsync. /// - /// - /// + /// The command. + /// Async Task. Task HandleCommand(T @command); } \ No newline at end of file diff --git a/src/Genocs.Core/CQRS/Commons/Extensions.cs b/src/Genocs.Core/CQRS/Commons/Extensions.cs index 88cd514e..014e8e6c 100644 --- a/src/Genocs.Core/CQRS/Commons/Extensions.cs +++ b/src/Genocs.Core/CQRS/Commons/Extensions.cs @@ -10,20 +10,20 @@ namespace Genocs.Core.CQRS.Commons; /// -/// Extension helper to handle the whole set of Dispatcher +/// Extension helper to handle the whole set of Dispatcher. /// public static class Extensions { /// - /// AddHandlers implementation + /// AddHandlers implementation. /// - /// - /// + /// The service collection. + /// Name of the project. /// public static IServiceCollection AddHandlers(this IServiceCollection services, string project) { var assemblies = AppDomain.CurrentDomain.GetAssemblies() - .Where(x => x.FullName != null && x.FullName.Contains(project)) + .Where(x => x.FullName != null && x.FullName.Contains(project)) .ToArray(); services.Scan(s => s.FromAssemblies(assemblies) @@ -48,10 +48,10 @@ public static IServiceCollection AddHandlers(this IServiceCollection services, s } /// - /// AddDispatchers Implementation + /// AddDispatchers Implementation. /// - /// - /// + /// The service collection. + /// The service collection to be used for chaining pattern. public static IServiceCollection AddDispatchers(this IServiceCollection services) => services .AddSingleton() diff --git a/src/Genocs.Core/CQRS/Commons/IDispatcher.cs b/src/Genocs.Core/CQRS/Commons/IDispatcher.cs index fbdec5fa..4df4070f 100644 --- a/src/Genocs.Core/CQRS/Commons/IDispatcher.cs +++ b/src/Genocs.Core/CQRS/Commons/IDispatcher.cs @@ -1,41 +1,40 @@ -namespace Genocs.Core.CQRS.Commons -{ - using Genocs.Core.CQRS.Commands; - using Genocs.Core.CQRS.Events; - using Genocs.Core.CQRS.Queries; - using System.Threading; - using System.Threading.Tasks; +using Genocs.Core.CQRS.Commands; +using Genocs.Core.CQRS.Events; +using Genocs.Core.CQRS.Queries; + +namespace Genocs.Core.CQRS.Commons; +/// +/// Generic dispatcher interface. +/// +public interface IDispatcher +{ /// - /// Generic dispatcher interface + /// Generic command sender. /// - public interface IDispatcher - { - /// - /// Generic command sender - /// - /// - /// - /// - /// - Task SendAsync(T command, CancellationToken cancellationToken = default) where T : class, ICommand; + /// + /// + /// + /// + Task SendAsync(T command, CancellationToken cancellationToken = default) + where T : class, ICommand; - /// - /// Generic event publisher - /// - /// - /// - /// - /// - Task PublishAsync(T @event, CancellationToken cancellationToken = default) where T : class, IEvent; + /// + /// Generic event publisher. + /// + /// + /// + /// + /// + Task PublishAsync(T @event, CancellationToken cancellationToken = default) + where T : class, IEvent; - /// - /// Generic query fetcher - /// - /// - /// - /// - /// - Task QueryAsync(IQuery query, CancellationToken cancellationToken = default); - } + /// + /// Generic query fetcher. + /// + /// + /// + /// + /// + Task QueryAsync(IQuery query, CancellationToken cancellationToken = default); } diff --git a/src/Genocs.Core/CQRS/Commons/InMemoryDispatcher.cs b/src/Genocs.Core/CQRS/Commons/InMemoryDispatcher.cs index c3c872ca..609ac04f 100644 --- a/src/Genocs.Core/CQRS/Commons/InMemoryDispatcher.cs +++ b/src/Genocs.Core/CQRS/Commons/InMemoryDispatcher.cs @@ -1,14 +1,11 @@ -namespace Genocs.Core.CQRS.Commons; - -using Genocs.Core.CQRS.Commands; +using Genocs.Core.CQRS.Commands; using Genocs.Core.CQRS.Events; using Genocs.Core.CQRS.Queries; -using System.Threading; -using System.Threading.Tasks; +namespace Genocs.Core.CQRS.Commons; /// -/// The class name will be renamed!!! +/// The class name will be renamed. /// internal sealed class InMemoryDispatcher : IDispatcher { @@ -16,18 +13,22 @@ internal sealed class InMemoryDispatcher : IDispatcher private readonly IEventDispatcher _eventDispatcher; private readonly IQueryDispatcher _queryDispatcher; - public InMemoryDispatcher(ICommandDispatcher commandDispatcher, IEventDispatcher eventDispatcher, - IQueryDispatcher queryDispatcher) + public InMemoryDispatcher( + ICommandDispatcher commandDispatcher, + IEventDispatcher eventDispatcher, + IQueryDispatcher queryDispatcher) { - _commandDispatcher = commandDispatcher; - _eventDispatcher = eventDispatcher; - _queryDispatcher = queryDispatcher; + _commandDispatcher = commandDispatcher ?? throw new ArgumentNullException(nameof(commandDispatcher)); + _eventDispatcher = eventDispatcher ?? throw new ArgumentNullException(nameof(eventDispatcher)); + _queryDispatcher = queryDispatcher ?? throw new ArgumentNullException(nameof(queryDispatcher)); } - public Task SendAsync(T command, CancellationToken cancellationToken = default) where T : class, ICommand + public Task SendAsync(T command, CancellationToken cancellationToken = default) + where T : class, ICommand => _commandDispatcher.SendAsync(command, cancellationToken); - public Task PublishAsync(T @event, CancellationToken cancellationToken = default) where T : class, IEvent + public Task PublishAsync(T @event, CancellationToken cancellationToken = default) + where T : class, IEvent => _eventDispatcher.PublishAsync(@event, cancellationToken); public Task QueryAsync(IQuery query, CancellationToken cancellationToken = default) diff --git a/src/Genocs.Core/CQRS/Events/Dispatchers/EventDispatcher.cs b/src/Genocs.Core/CQRS/Events/Dispatchers/EventDispatcher.cs index edccd1cb..cdea3890 100644 --- a/src/Genocs.Core/CQRS/Events/Dispatchers/EventDispatcher.cs +++ b/src/Genocs.Core/CQRS/Events/Dispatchers/EventDispatcher.cs @@ -1,20 +1,31 @@ -namespace Genocs.Core.CQRS.Events.Dispatchers; - -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Genocs.Core.CQRS.Events; using Microsoft.Extensions.DependencyInjection; +namespace Genocs.Core.CQRS.Events.Dispatchers; + +/// +/// The event dispatcher is responsible for dispatching events to their respective handlers. +/// internal sealed class EventDispatcher : IEventDispatcher { private readonly IServiceProvider _serviceProvider; + /// + /// standard constructor. + /// + /// The service provider. public EventDispatcher(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider; - public async Task PublishAsync(T @event, CancellationToken cancellationToken = default) where T : class, IEvent + /// + /// The event dispatcher is responsible for dispatching events to their respective handlers. + /// + /// The type of the event to publish. + /// The event object instance. + /// The cancellation token. + /// The event cannot be null. + /// Async task. + public async Task PublishAsync(T @event, CancellationToken cancellationToken = default) + where T : class, IEvent { if (@event is null) { diff --git a/src/Genocs.Core/CQRS/Events/Extensions.cs b/src/Genocs.Core/CQRS/Events/Extensions.cs index b845280b..db9d35dc 100644 --- a/src/Genocs.Core/CQRS/Events/Extensions.cs +++ b/src/Genocs.Core/CQRS/Events/Extensions.cs @@ -6,12 +6,12 @@ namespace Genocs.Core.CQRS.Events; /// -/// CQRS events extensions +/// CQRS events extensions. /// public static class Extensions { /// - /// AddEventHandlers + /// AddEventHandlers. /// /// /// @@ -29,7 +29,7 @@ public static IGenocsBuilder AddEventHandlers(this IGenocsBuilder builder) /// - /// AddInMemoryEventDispatcher + /// AddInMemoryEventDispatcher. /// /// /// @@ -40,7 +40,7 @@ public static IGenocsBuilder AddInMemoryEventDispatcher(this IGenocsBuilder buil } /// - /// AddEventHandlers + /// AddEventHandlers. /// /// /// @@ -57,7 +57,7 @@ public static IServiceCollection AddEventHandlers(this IServiceCollection servic } /// - /// AddInMemoryEventDispatcher + /// AddInMemoryEventDispatcher. /// /// /// diff --git a/src/Genocs.Core/CQRS/Events/IEvent.cs b/src/Genocs.Core/CQRS/Events/IEvent.cs index 4ea82680..c61433d7 100644 --- a/src/Genocs.Core/CQRS/Events/IEvent.cs +++ b/src/Genocs.Core/CQRS/Events/IEvent.cs @@ -1,7 +1,7 @@ namespace Genocs.Core.CQRS.Events; /// -/// The CQRS event interface that defines a generic event used for integration tasks +/// The CQRS event interface that defines a generic event used for integration tasks. /// public interface IEvent { diff --git a/src/Genocs.Core/CQRS/Events/IEventDispatcher.cs b/src/Genocs.Core/CQRS/Events/IEventDispatcher.cs index 3b3d7b20..9d9ae905 100644 --- a/src/Genocs.Core/CQRS/Events/IEventDispatcher.cs +++ b/src/Genocs.Core/CQRS/Events/IEventDispatcher.cs @@ -1,19 +1,17 @@ namespace Genocs.Core.CQRS.Events; -using System.Threading; -using System.Threading.Tasks; - /// -/// The CQRS event dispatcher interface used to publish an integration event +/// The CQRS event dispatcher interface used to publish an integration event. /// public interface IEventDispatcher { /// - /// It allows to Publish an integration event Async + /// It allows to Publish an integration event Async. /// - /// + /// The Type of the event. /// /// /// - Task PublishAsync(T @event, CancellationToken cancellationToken = default) where T : class, IEvent; + Task PublishAsync(T @event, CancellationToken cancellationToken = default) + where T : class, IEvent; } \ No newline at end of file diff --git a/src/Genocs.Core/CQRS/Events/IEventHandler.cs b/src/Genocs.Core/CQRS/Events/IEventHandler.cs index f636e7fb..cf03ffbc 100644 --- a/src/Genocs.Core/CQRS/Events/IEventHandler.cs +++ b/src/Genocs.Core/CQRS/Events/IEventHandler.cs @@ -1,13 +1,11 @@ namespace Genocs.Core.CQRS.Events; -using System.Threading; -using System.Threading.Tasks; - /// -/// Generic interface for CQRS Event handler +/// Generic interface for CQRS Event handler. /// /// -public interface IEventHandler where TEvent : class, IEvent +public interface IEventHandler + where TEvent : class, IEvent { /// /// Standard Event handler @@ -18,15 +16,15 @@ public interface IEventHandler where TEvent : class, IEvent Task HandleAsync(TEvent @event, CancellationToken cancellationToken = default); } - /// -/// Legacy Event handler interface definition +/// Legacy Event handler interface definition. /// /// -public interface IEventHandlerLegacy where T : IEvent +public interface IEventHandlerLegacy + where T : IEvent { /// - /// Legacy event handler place holder + /// Legacy event handler place holder. /// /// /// diff --git a/src/Genocs.Core/CQRS/Events/IRejectedEvent.cs b/src/Genocs.Core/CQRS/Events/IRejectedEvent.cs index 22e1f9e0..9fbae293 100644 --- a/src/Genocs.Core/CQRS/Events/IRejectedEvent.cs +++ b/src/Genocs.Core/CQRS/Events/IRejectedEvent.cs @@ -1,8 +1,7 @@ -namespace Genocs.Core.CQRS.Events +namespace Genocs.Core.CQRS.Events; + +public interface IRejectedEvent : IEvent { - public interface IRejectedEvent : IEvent - { - string Reason { get; } - string Code { get; } - } + string Reason { get; } + string Code { get; } } \ No newline at end of file diff --git a/src/Genocs.Core/CQRS/Events/RejectedEvent.cs b/src/Genocs.Core/CQRS/Events/RejectedEvent.cs index 11109941..7d2f82d4 100644 --- a/src/Genocs.Core/CQRS/Events/RejectedEvent.cs +++ b/src/Genocs.Core/CQRS/Events/RejectedEvent.cs @@ -1,21 +1,19 @@ -namespace Genocs.Core.CQRS.Events -{ - using System.Text.Json.Serialization; +using System.Text.Json.Serialization; - public class RejectedEvent : IRejectedEvent - { - public string Reason { get; } - public string Code { get; } +namespace Genocs.Core.CQRS.Events; - [JsonConstructor] - public RejectedEvent(string reason, string code) - { - Reason = reason; - Code = code; - } +public class RejectedEvent : IRejectedEvent +{ + public string Reason { get; } + public string Code { get; } - public static IRejectedEvent For(string name) - => new RejectedEvent($"There was an error when executing: " + - $"{name}", $"{name}_error"); + [JsonConstructor] + public RejectedEvent(string reason, string code) + { + Reason = reason; + Code = code; } + + public static IRejectedEvent For(string name) + => new RejectedEvent($"There was an error when executing: " + $"{name}", $"{name}_error"); } \ No newline at end of file diff --git a/src/Genocs.Core/CQRS/Queries/Dispatchers/QueryDispatcher.cs b/src/Genocs.Core/CQRS/Queries/Dispatchers/QueryDispatcher.cs index 999262df..4e1fadd5 100644 --- a/src/Genocs.Core/CQRS/Queries/Dispatchers/QueryDispatcher.cs +++ b/src/Genocs.Core/CQRS/Queries/Dispatchers/QueryDispatcher.cs @@ -1,46 +1,42 @@ -namespace Genocs.Core.CQRS.Queries.Dispatchers +using Microsoft.Extensions.DependencyInjection; + +namespace Genocs.Core.CQRS.Queries.Dispatchers; + +internal sealed class QueryDispatcher : IQueryDispatcher { - using System; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.Extensions.DependencyInjection; + private readonly IServiceProvider _serviceProvider; - internal sealed class QueryDispatcher : IQueryDispatcher + public QueryDispatcher(IServiceProvider serviceProvider) { - private readonly IServiceProvider _serviceProvider; + _serviceProvider = serviceProvider; + } - public QueryDispatcher(IServiceProvider serviceProvider) + public async Task QueryAsync(IQuery query, CancellationToken cancellationToken = default) + { + if (query is null) { - _serviceProvider = serviceProvider; + throw new InvalidOperationException("Query cannot be null."); } - public async Task QueryAsync(IQuery query, CancellationToken cancellationToken = default) - { - if (query is null) - { - throw new InvalidOperationException("Query cannot be null."); - } - - await using var scope = _serviceProvider.CreateAsyncScope(); - var handlerType = typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult)); - var handler = scope.ServiceProvider.GetRequiredService(handlerType); + await using var scope = _serviceProvider.CreateAsyncScope(); + var handlerType = typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult)); + object handler = scope.ServiceProvider.GetRequiredService(handlerType); - var method = handlerType.GetMethod(nameof(IQueryHandler, TResult>.HandleAsync)); - if (method is null) - { - throw new InvalidOperationException($"Query handler for '{typeof(TResult).Name}' is invalid."); - } + var method = handlerType.GetMethod(nameof(IQueryHandler, TResult>.HandleAsync)); + if (method is null) + { + throw new InvalidOperationException($"Query handler for '{typeof(TResult).Name}' is invalid."); + } - return await (Task)method.Invoke(handler, new object[] { query, cancellationToken }); + return await (Task)method.Invoke(handler, new object[] { query, cancellationToken }); - } + } - public async Task QueryAsync(TQuery query, CancellationToken cancellationToken = default) - where TQuery : class, IQuery - { - using var scope = _serviceProvider.CreateScope(); - var handler = scope.ServiceProvider.GetRequiredService>(); - return await handler.HandleAsync(query, cancellationToken); - } + public async Task QueryAsync(TQuery query, CancellationToken cancellationToken = default) + where TQuery : class, IQuery + { + using var scope = _serviceProvider.CreateScope(); + var handler = scope.ServiceProvider.GetRequiredService>(); + return await handler.HandleAsync(query, cancellationToken); } } diff --git a/src/Genocs.Core/CQRS/Queries/Extensions.cs b/src/Genocs.Core/CQRS/Queries/Extensions.cs index aa026f06..16024689 100644 --- a/src/Genocs.Core/CQRS/Queries/Extensions.cs +++ b/src/Genocs.Core/CQRS/Queries/Extensions.cs @@ -6,12 +6,12 @@ namespace Genocs.Core.CQRS.Queries; /// -/// +/// Extension helper class for CQRS Queries. /// public static class Extensions { /// - /// AddQueryHandlers + /// AddQueryHandlers. /// /// /// @@ -28,7 +28,7 @@ public static IGenocsBuilder AddQueryHandlers(this IGenocsBuilder builder) } /// - /// AddInMemoryQueryDispatcher + /// AddInMemoryQueryDispatcher. /// /// /// diff --git a/src/Genocs.Core/CQRS/Queries/IPagedFilter.cs b/src/Genocs.Core/CQRS/Queries/IPagedFilter.cs index 72ea7a99..5e117797 100644 --- a/src/Genocs.Core/CQRS/Queries/IPagedFilter.cs +++ b/src/Genocs.Core/CQRS/Queries/IPagedFilter.cs @@ -1,20 +1,18 @@ -namespace Genocs.Core.CQRS.Queries -{ - using System.Collections.Generic; +namespace Genocs.Core.CQRS.Queries; +/// +/// The paged filter interface. +/// +/// +/// +public interface IPagedFilter + where TQuery : IQuery +{ /// - /// The paged filter interface + /// Filter. /// - /// - /// - public interface IPagedFilter where TQuery : IQuery - { - /// - /// Filter - /// - /// - /// - /// - PagedResult Filter(IEnumerable values, TQuery query); - } + /// + /// + /// + PagedResult Filter(IEnumerable values, TQuery query); } \ No newline at end of file diff --git a/src/Genocs.Core/CQRS/Queries/IPagedQuery.cs b/src/Genocs.Core/CQRS/Queries/IPagedQuery.cs index d8b9f5e1..ffd6fd89 100644 --- a/src/Genocs.Core/CQRS/Queries/IPagedQuery.cs +++ b/src/Genocs.Core/CQRS/Queries/IPagedQuery.cs @@ -1,28 +1,27 @@ -namespace Genocs.Core.CQRS.Queries +namespace Genocs.Core.CQRS.Queries; + +/// +/// Query for pagination. +/// +public interface IPagedQuery : IQuery { /// - /// Query for pagination + /// Page to query zero indexed. /// - public interface IPagedQuery : IQuery - { - /// - /// Page to query zero indexed - /// - int Page { get; } + int Page { get; } - /// - /// Number of results. Aka page size - /// - int Results { get; } + /// + /// Number of results. Aka page size. + /// + int Results { get; } - /// - /// The field used to order by - /// - string? OrderBy { get; } + /// + /// The field used to order by. + /// + string? OrderBy { get; } - /// - /// Type of order. It could be ASC or DESC - /// - string? SortOrder { get; } - } + /// + /// Type of order. It could be ASC or DESC. + /// + string? SortOrder { get; } } \ No newline at end of file diff --git a/src/Genocs.Core/CQRS/Queries/IQuery.cs b/src/Genocs.Core/CQRS/Queries/IQuery.cs index 4b0cfd95..ba1ea122 100644 --- a/src/Genocs.Core/CQRS/Queries/IQuery.cs +++ b/src/Genocs.Core/CQRS/Queries/IQuery.cs @@ -1,17 +1,16 @@ -namespace Genocs.Core.CQRS.Queries +namespace Genocs.Core.CQRS.Queries; + +/// +/// Generic interface for query. +/// +public interface IQuery { - /// - /// Generic interface for query. - /// - public interface IQuery - { - } +} - /// - /// Generic interface for type - /// - /// - public interface IQuery : IQuery - { - } +/// +/// Generic interface for type. +/// +/// +public interface IQuery : IQuery +{ } \ No newline at end of file diff --git a/src/Genocs.Core/CQRS/Queries/IQueryDispatcher.cs b/src/Genocs.Core/CQRS/Queries/IQueryDispatcher.cs index eb5f3ca5..b3ee53eb 100644 --- a/src/Genocs.Core/CQRS/Queries/IQueryDispatcher.cs +++ b/src/Genocs.Core/CQRS/Queries/IQueryDispatcher.cs @@ -1,31 +1,27 @@ -namespace Genocs.Core.CQRS.Queries -{ - using System.Threading.Tasks; - using System.Threading; +namespace Genocs.Core.CQRS.Queries; +/// +/// The query dispatcher interface. +/// +public interface IQueryDispatcher +{ /// - /// The query dispatcher interface + /// QueryAsync. /// - public interface IQueryDispatcher - { - /// - /// QueryAsync - /// - /// - /// - /// - /// - Task QueryAsync(IQuery query, CancellationToken cancellationToken = default); + /// The result type. + /// The query. + /// The Cancellation token. + /// The query result. + Task QueryAsync(IQuery query, CancellationToken cancellationToken = default); - /// - /// QueryAsync - /// - /// - /// - /// - /// - /// - Task QueryAsync(TQuery query, CancellationToken cancellationToken = default) - where TQuery : class, IQuery; - } + /// + /// QueryAsync. + /// + /// The type of the query. + /// The result. + /// The query object instance. + /// The cancellation token. + /// The query result. + Task QueryAsync(TQuery query, CancellationToken cancellationToken = default) + where TQuery : class, IQuery; } \ No newline at end of file diff --git a/src/Genocs.Core/CQRS/Queries/IQueryHandler.cs b/src/Genocs.Core/CQRS/Queries/IQueryHandler.cs index f05d4858..9c3f30e8 100644 --- a/src/Genocs.Core/CQRS/Queries/IQueryHandler.cs +++ b/src/Genocs.Core/CQRS/Queries/IQueryHandler.cs @@ -1,21 +1,21 @@ -namespace Genocs.Core.CQRS.Queries -{ - using System.Threading.Tasks; - using System.Threading; +using System.Threading.Tasks; +using System.Threading; + +namespace Genocs.Core.CQRS.Queries; +/// +/// The query handler interface. +/// +/// +/// +public interface IQueryHandler + where TQuery : class, IQuery +{ /// - /// The query handler interface + /// HandleAsync. /// - /// - /// - public interface IQueryHandler where TQuery : class, IQuery - { - /// - /// HandleAsync - /// - /// - /// - /// - Task HandleAsync(TQuery query, CancellationToken cancellationToken = default); - } + /// + /// + /// + Task HandleAsync(TQuery query, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/Genocs.Core/CQRS/Queries/PagedQueryBase.cs b/src/Genocs.Core/CQRS/Queries/PagedQueryBase.cs index 43e90b9f..f518a21e 100644 --- a/src/Genocs.Core/CQRS/Queries/PagedQueryBase.cs +++ b/src/Genocs.Core/CQRS/Queries/PagedQueryBase.cs @@ -1,28 +1,27 @@ -namespace Genocs.Core.CQRS.Queries +namespace Genocs.Core.CQRS.Queries; + +/// +/// The paged query result. +/// +public abstract class PagedQueryBase : IPagedQuery { /// - /// The paged query result + /// The zero based page index. /// - public abstract class PagedQueryBase : IPagedQuery - { - /// - /// The zero based page index - /// - public int Page { get; set; } + public int Page { get; set; } - /// - /// Number of results. Aka page size - /// - public int Results { get; set; } + /// + /// Number of results. Aka page size. + /// + public int Results { get; set; } - /// - /// The field used to order by - /// - public string? OrderBy { get; set; } + /// + /// The field used to order by. + /// + public string? OrderBy { get; set; } - /// - /// Type of order. It could be ASC or DESC - /// - public string? SortOrder { get; set; } - } + /// + /// Type of order. It could be ASC or DESC. + /// + public string? SortOrder { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Core/CQRS/Queries/PagedQueryWithFilter.cs b/src/Genocs.Core/CQRS/Queries/PagedQueryWithFilter.cs index 2c4ebac3..03fdaf18 100644 --- a/src/Genocs.Core/CQRS/Queries/PagedQueryWithFilter.cs +++ b/src/Genocs.Core/CQRS/Queries/PagedQueryWithFilter.cs @@ -1,13 +1,12 @@ -namespace Genocs.Core.CQRS.Queries +namespace Genocs.Core.CQRS.Queries; + +/// +/// Paged query extension with Filter. +/// +public class PagedQueryWithFilter : PagedQueryBase { /// - /// Paged query extension with Filter + /// The filter. /// - public class PagedQueryWithFilter : PagedQueryBase - { - /// - /// The filter - /// - public string FilterBy { get; set; } = string.Empty; - } + public string FilterBy { get; set; } = string.Empty; } \ No newline at end of file diff --git a/src/Genocs.Core/CQRS/Queries/PagedResult.cs b/src/Genocs.Core/CQRS/Queries/PagedResult.cs index be31ae0f..e501d3ae 100644 --- a/src/Genocs.Core/CQRS/Queries/PagedResult.cs +++ b/src/Genocs.Core/CQRS/Queries/PagedResult.cs @@ -1,74 +1,82 @@ -namespace Genocs.Core.CQRS.Queries +using System.Text.Json.Serialization; + +namespace Genocs.Core.CQRS.Queries; + +/// +/// The paged result. +/// +/// The page result type. +public class PagedResult : PagedResultBase { - using System.Collections.Generic; - using System.Linq; - using System.Text.Json.Serialization; + /// + /// Returned items. + /// + public IEnumerable Items { get; } /// - /// The paged result + /// Default constructor. /// - /// - public class PagedResult : PagedResultBase + protected PagedResult() { - /// - /// Returned items - /// - public IEnumerable Items { get; } - - /// - /// Default constructor - /// - protected PagedResult() - { - Items = Enumerable.Empty(); - } + Items = Enumerable.Empty(); + } - /// - /// Standard constructor - /// - /// - /// - /// - /// - /// - [JsonConstructor] - protected PagedResult(IEnumerable items, - int currentPage, - int resultsPerPage, - int totalPages, - long totalResults) : - base(currentPage, resultsPerPage, totalPages, totalResults) - { - Items = items; - } + /// + /// Standard constructor. + /// + /// The list of items. + /// Zero based current page. + /// Number of results within the page. + /// Total number of pages. + /// Total number of results. + [JsonConstructor] + protected PagedResult( + IEnumerable items, + int currentPage, + int resultsPerPage, + int totalPages, + long totalResults) + : base(currentPage, resultsPerPage, totalPages, totalResults) + { + Items = items; + } - /// - /// Create helper - /// - /// - /// - /// - /// - /// - /// - public static PagedResult Create(IEnumerable items, - int currentPage, int resultsPerPage, - int totalPages, long totalResults) - => new PagedResult(items, currentPage, resultsPerPage, totalPages, totalResults); + /// + /// Create helper. + /// + /// The list of items. + /// Zero based current page. + /// Number of results within the page. + /// Total number of pages. + /// Total number of results. + /// The paged results. + public static PagedResult Create( + IEnumerable items, + int currentPage, + int resultsPerPage, + int totalPages, + long totalResults) + => new(items, currentPage, resultsPerPage, totalPages, totalResults); - /// - /// From helper - /// - /// - /// - /// - public static PagedResult From(PagedResultBase result, IEnumerable items) - => new PagedResult(items, result.CurrentPage, result.ResultsPerPage, - result.TotalPages, result.TotalResults); + /// + /// From helper. + /// + /// + /// + /// + public static PagedResult From( + PagedResultBase result, + IEnumerable items) + => new( + items, + result.CurrentPage, + result.ResultsPerPage, + result.TotalPages, + result.TotalResults); - /// - /// Static helper to get Empty result - /// - public static PagedResult Empty => new PagedResult(); - } + /// + /// Static helper to get Empty result. + /// + public static PagedResult Empty + => new(); } \ No newline at end of file diff --git a/src/Genocs.Core/CQRS/Queries/PagedResultBase.cs b/src/Genocs.Core/CQRS/Queries/PagedResultBase.cs index 79dec7f1..b9a8a5e2 100644 --- a/src/Genocs.Core/CQRS/Queries/PagedResultBase.cs +++ b/src/Genocs.Core/CQRS/Queries/PagedResultBase.cs @@ -1,51 +1,49 @@ -namespace Genocs.Core.CQRS.Queries +namespace Genocs.Core.CQRS.Queries; + +/// +/// The page result base class. +/// +public abstract class PagedResultBase { /// - /// The page result base class + /// Returned zero index page. /// - public abstract class PagedResultBase - { - /// - /// Returned zero index page - /// - public int CurrentPage { get; } + public int CurrentPage { get; } - /// - /// Number of returned results. Aka page size - /// - public int ResultsPerPage { get; } + /// + /// Number of returned results. Aka page size. + /// + public int ResultsPerPage { get; } - /// - /// Number of pages - /// - public int TotalPages { get; } + /// + /// Number of pages. + /// + public int TotalPages { get; } - /// - /// Total number of results - /// - public long TotalResults { get; } + /// + /// Total number of results. + /// + public long TotalResults { get; } - /// - /// Default constructor - /// - protected PagedResultBase() - { - } + /// + /// Default constructor. + /// + protected PagedResultBase() + { + } - /// - /// Standard constructor - /// - /// - /// - /// - /// - protected PagedResultBase(int currentPage, int resultsPerPage, - int totalPages, long totalResults) - { - CurrentPage = currentPage > totalPages ? totalPages : currentPage; - ResultsPerPage = resultsPerPage; - TotalPages = totalPages; - TotalResults = totalResults; - } + /// + /// Standard constructor. + /// + /// + /// + /// + /// + protected PagedResultBase(int currentPage, int resultsPerPage, int totalPages, long totalResults) + { + CurrentPage = currentPage > totalPages ? totalPages : currentPage; + ResultsPerPage = resultsPerPage; + TotalPages = totalPages; + TotalResults = totalResults; } } \ No newline at end of file diff --git a/src/Genocs.Core/CQRS/Queries/Request.cs b/src/Genocs.Core/CQRS/Queries/Request.cs index 97ddc21d..08c620d8 100644 --- a/src/Genocs.Core/CQRS/Queries/Request.cs +++ b/src/Genocs.Core/CQRS/Queries/Request.cs @@ -1,16 +1,15 @@ -namespace Genocs.Core.CQRS.Queries +namespace Genocs.Core.CQRS.Queries; + +public interface ISearchRequest { - public interface ISearchRequest - { - string q { get; set; } + string q { get; set; } - int MaxItems { get; set; } - } + int MaxItems { get; set; } +} - public class SearchRequest : ISearchRequest - { - public string q { get; set; } = string.Empty; +public class SearchRequest : ISearchRequest +{ + public string q { get; set; } = string.Empty; - public int MaxItems { get; set; } = 10; - } + public int MaxItems { get; set; } = 10; } diff --git a/src/Genocs.Core/Collections/Extensions/CollectionExtensions.cs b/src/Genocs.Core/Collections/Extensions/CollectionExtensions.cs index edadc987..3722f391 100644 --- a/src/Genocs.Core/Collections/Extensions/CollectionExtensions.cs +++ b/src/Genocs.Core/Collections/Extensions/CollectionExtensions.cs @@ -1,7 +1,5 @@ namespace Genocs.Core.Collections.Extensions { - using System; - using System.Collections.Generic; /// /// Extension methods for Collections. @@ -19,9 +17,9 @@ public static bool IsNullOrEmpty(this ICollection source) /// /// Adds an item to the collection if it's not already in the collection. /// - /// Collection - /// Item to check and add - /// Type of the items in the collection + /// Collection. + /// Item to check and add. + /// Type of the items in the collection. /// Returns True if added, returns False if not. public static bool AddIfNotContains(this ICollection source, T item) { diff --git a/src/Genocs.Core/Collections/Extensions/DictionaryExtensions.cs b/src/Genocs.Core/Collections/Extensions/DictionaryExtensions.cs index e69a577f..925839ed 100644 --- a/src/Genocs.Core/Collections/Extensions/DictionaryExtensions.cs +++ b/src/Genocs.Core/Collections/Extensions/DictionaryExtensions.cs @@ -1,80 +1,76 @@ -namespace Genocs.Core.Collections.Extensions -{ - using System; - using System.Collections.Generic; +namespace Genocs.Core.Collections.Extensions; +/// +/// Extension methods for Dictionary. +/// +public static class DictionaryExtensions +{ /// - /// Extension methods for Dictionary. + /// This method is used to try to get a value in a dictionary if it does exists. /// - public static class DictionaryExtensions + /// Type of the value. + /// The collection object. + /// Key. + /// Value of the key (or default value if key not exists). + /// True if key does exists in the dictionary. + internal static bool TryGetValue(this IDictionary dictionary, string key, out T? value) { - /// - /// This method is used to try to get a value in a dictionary if it does exists. - /// - /// Type of the value - /// The collection object - /// Key - /// Value of the key (or default value if key not exists) - /// True if key does exists in the dictionary - internal static bool TryGetValue(this IDictionary dictionary, string key, out T value) + object? valueObj; + if (dictionary.TryGetValue(key, out valueObj) && valueObj is T) { - object valueObj; - if (dictionary.TryGetValue(key, out valueObj) && valueObj is T) - { - value = (T)valueObj; - return true; - } - - value = default(T); - return false; + value = (T)valueObj; + return true; } - /// - /// Gets a value from the dictionary with given key. Returns default value if can not find. - /// - /// Dictionary to check and get - /// Key to find the value - /// Type of the key - /// Type of the value - /// Value if found, default if can not found. - public static TValue GetOrDefault(this IDictionary dictionary, TKey key) - { - TValue obj; - return dictionary.TryGetValue(key, out obj) ? obj : default(TValue); - } + value = default(T); + return false; + } - /// - /// Gets a value from the dictionary with given key. Returns default value if can not find. - /// - /// Dictionary to check and get - /// Key to find the value - /// A factory method used to create the value if not found in the dictionary - /// Type of the key - /// Type of the value - /// Value if found, default if can not found. - public static TValue GetOrAdd(this IDictionary dictionary, TKey key, Func factory) - { - TValue obj; - if (dictionary.TryGetValue(key, out obj)) - { - return obj; - } + /// + /// Gets a value from the dictionary with given key. Returns default value if can not find. + /// + /// Dictionary to check and get. + /// Key to find the value. + /// Type of the key. + /// Type of the value. + /// Value if found, default if can not found. + public static TValue? GetOrDefault(this IDictionary dictionary, TKey key) + { + TValue? obj; + return dictionary.TryGetValue(key, out obj) ? obj : default(TValue); + } - return dictionary[key] = factory(key); - } - - /// - /// Gets a value from the dictionary with given key. Returns default value if can not find. - /// - /// Dictionary to check and get - /// Key to find the value - /// A factory method used to create the value if not found in the dictionary - /// Type of the key - /// Type of the value - /// Value if found, default if can not found. - public static TValue GetOrAdd(this IDictionary dictionary, TKey key, Func factory) + /// + /// Gets a value from the dictionary with given key. Returns default value if can not find. + /// + /// Dictionary to check and get. + /// Key to find the value. + /// A factory method used to create the value if not found in the dictionary. + /// Type of the key. + /// Type of the value. + /// Value if found, default if can not found. + public static TValue? GetOrAdd(this IDictionary dictionary, TKey key, Func factory) + { + TValue? obj; + if (dictionary.TryGetValue(key, out obj)) { - return dictionary.GetOrAdd(key, k => factory()); + return obj; } + + return dictionary[key] = factory(key); + } + + /// + /// Gets a value from the dictionary with given key. Returns default value if can not find. + /// + /// Dictionary to check and get. + /// Key to find the value. + /// A factory method used to create the value if not found in the dictionary. + /// Type of the key. + /// Type of the value. + /// Value if found, default if can not found. + public static TValue? GetOrAdd(this IDictionary dictionary, TKey key, Func factory) + { + return dictionary.GetOrAdd(key, k => factory()); } } \ No newline at end of file diff --git a/src/Genocs.Core/Collections/Extensions/EnumerableExtensions.cs b/src/Genocs.Core/Collections/Extensions/EnumerableExtensions.cs index cd71be8e..29f0c7e7 100644 --- a/src/Genocs.Core/Collections/Extensions/EnumerableExtensions.cs +++ b/src/Genocs.Core/Collections/Extensions/EnumerableExtensions.cs @@ -1,66 +1,60 @@ -namespace Genocs.Core.Collections.Extensions -{ - using System; - using System.Collections.Generic; - using System.Linq; - +namespace Genocs.Core.Collections.Extensions; - /// - /// Extension methods for . +/// +/// Extension methods for . +/// +public static class EnumerableExtensions +{ + /// + /// Concatenates the members of a constructed collection of type System.String, using the specified separator between each member. + /// This is a shortcut for string.Join(...) /// - public static class EnumerableExtensions + /// A collection that contains the strings to concatenate. + /// The string to use as a separator. separator is included in the returned string only if values has more than one element. + /// A string that consists of the members of values delimited by the separator string. If values has no members, the method returns System.String.Empty. + public static string JoinAsString(this IEnumerable source, string separator) { - /// - /// Concatenates the members of a constructed collection of type System.String, using the specified separator between each member. - /// This is a shortcut for string.Join(...) - /// - /// A collection that contains the strings to concatenate. - /// The string to use as a separator. separator is included in the returned string only if values has more than one element. - /// A string that consists of the members of values delimited by the separator string. If values has no members, the method returns System.String.Empty. - public static string JoinAsString(this IEnumerable source, string separator) - { - return string.Join(separator, source); - } + return string.Join(separator, source); + } - /// - /// Concatenates the members of a collection, using the specified separator between each member. - /// This is a shortcut for string.Join(...) - /// - /// A collection that contains the objects to concatenate. - /// The string to use as a separator. separator is included in the returned string only if values has more than one element. - /// The type of the members of values. - /// A string that consists of the members of values delimited by the separator string. If values has no members, the method returns System.String.Empty. - public static string JoinAsString(this IEnumerable source, string separator) - { - return string.Join(separator, source); - } + /// + /// Concatenates the members of a collection, using the specified separator between each member. + /// This is a shortcut for string.Join(...) + /// + /// A collection that contains the objects to concatenate. + /// The string to use as a separator. separator is included in the returned string only if values has more than one element. + /// The type of the members of values. + /// A string that consists of the members of values delimited by the separator string. If values has no members, the method returns System.String.Empty. + public static string JoinAsString(this IEnumerable source, string separator) + { + return string.Join(separator, source); + } - /// - /// Filters a by given predicate if given condition is true. - /// - /// Enumerable to apply filtering - /// A boolean value - /// Predicate to filter the enumerable - /// Filtered or not filtered enumerable based on - public static IEnumerable WhereIf(this IEnumerable source, bool condition, Func predicate) - { - return condition - ? source.Where(predicate) - : source; - } + /// + /// Filters a by given predicate if given condition is true. + /// + /// Enumerable to apply filtering. + /// A boolean value. + /// Predicate to filter the enumerable. + /// Filtered or not filtered enumerable based on . + public static IEnumerable WhereIf(this IEnumerable source, bool condition, Func predicate) + { + return condition + ? source.Where(predicate) + : source; + } - /// - /// Filters a by given predicate if given condition is true. - /// - /// Enumerable to apply filtering - /// A boolean value - /// Predicate to filter the enumerable - /// Filtered or not filtered enumerable based on - public static IEnumerable WhereIf(this IEnumerable source, bool condition, Func predicate) - { - return condition - ? source.Where(predicate) - : source; - } + /// + /// Filters a by given predicate if given condition is true. + /// + /// Enumerable to apply filtering. + /// A boolean value. + /// Predicate to filter the enumerable. + /// Filtered or not filtered enumerable based on . + public static IEnumerable WhereIf(this IEnumerable source, bool condition, Func predicate) + { + return condition + ? source.Where(predicate) + : source; } } diff --git a/src/Genocs.Core/Collections/Extensions/ListExtensions.cs b/src/Genocs.Core/Collections/Extensions/ListExtensions.cs index 16d76145..44a21f72 100644 --- a/src/Genocs.Core/Collections/Extensions/ListExtensions.cs +++ b/src/Genocs.Core/Collections/Extensions/ListExtensions.cs @@ -1,73 +1,68 @@ -namespace Genocs.Core.Collections.Extensions -{ - using System; - using System.Collections.Generic; +namespace Genocs.Core.Collections.Extensions; +/// +/// Extension methods for . +/// +public static class ListExtensions +{ /// - /// Extension methods for . + /// Sort a list by a topological sorting, which consider their dependencies. /// - public static class ListExtensions + /// The type of the members of values. + /// A list of objects to sort. + /// Function to resolve the dependencies. + /// + public static List SortByDependencies(this IEnumerable source, Func> getDependencies) { - /// - /// Sort a list by a topological sorting, which consider their dependencies - /// - /// The type of the members of values. - /// A list of objects to sort - /// Function to resolve the dependencies - /// - public static List SortByDependencies(this IEnumerable source, Func> getDependencies) + /* See: http://www.codeproject.com/Articles/869059/Topological-sorting-in-Csharp + * http://en.wikipedia.org/wiki/Topological_sorting + */ + + var sorted = new List(); + var visited = new Dictionary(); + + foreach (var item in source) { - /* See: http://www.codeproject.com/Articles/869059/Topological-sorting-in-Csharp - * http://en.wikipedia.org/wiki/Topological_sorting - */ + SortByDependenciesVisit(item, getDependencies, sorted, visited); + } - var sorted = new List(); - var visited = new Dictionary(); + return sorted; + } + + /// + /// + /// + /// The type of the members of values. + /// Item to resolve. + /// Function to resolve the dependencies. + /// List with the sortet items. + /// Dictionary with the visited items. + private static void SortByDependenciesVisit(T item, Func> getDependencies, List sorted, Dictionary visited) + { + bool alreadyVisited = visited.TryGetValue(item, out bool inProcess); - foreach (var item in source) + if (alreadyVisited) + { + if (inProcess) { - SortByDependenciesVisit(item, getDependencies, sorted, visited); + throw new ArgumentException("Cyclic dependency found! Item: " + item); } - - return sorted; } - - /// - /// - /// - /// The type of the members of values. - /// Item to resolve - /// Function to resolve the dependencies - /// List with the sortet items - /// Dictionary with the visited items - private static void SortByDependenciesVisit(T item, Func> getDependencies, List sorted, Dictionary visited) + else { - bool inProcess; - var alreadyVisited = visited.TryGetValue(item, out inProcess); + visited[item] = true; - if (alreadyVisited) + var dependencies = getDependencies(item); + if (dependencies != null) { - if (inProcess) + foreach (var dependency in dependencies) { - throw new ArgumentException("Cyclic dependency found! Item: " + item); + SortByDependenciesVisit(dependency, getDependencies, sorted, visited); } } - else - { - visited[item] = true; - var dependencies = getDependencies(item); - if (dependencies != null) - { - foreach (var dependency in dependencies) - { - SortByDependenciesVisit(dependency, getDependencies, sorted, visited); - } - } - - visited[item] = false; - sorted.Add(item); - } + visited[item] = false; + sorted.Add(item); } } } diff --git a/src/Genocs.Core/Collections/ITypeList.cs b/src/Genocs.Core/Collections/ITypeList.cs index be8fad3d..0756221b 100644 --- a/src/Genocs.Core/Collections/ITypeList.cs +++ b/src/Genocs.Core/Collections/ITypeList.cs @@ -1,39 +1,38 @@ -namespace Genocs.Core.Collections +namespace Genocs.Core.Collections; + +/// +/// A shortcut for to use object as base type. +/// +public interface ITypeList : ITypeList { - using System; - using System.Collections.Generic; +} + +/// +/// Extends to add restriction a specific base type. +/// +/// Base Type of s in this list. +public interface ITypeList : IList +{ /// - /// A shortcut for to use object as base type. + /// Adds a type to list. /// - public interface ITypeList : ITypeList - { - - } + /// The type of the param. + void Add() + where T : TBaseType; /// - /// Extends to add restriction a specific base type. + /// Checks if a type exists in the list. /// - /// Base Type of s in this list - public interface ITypeList : IList - { - /// - /// Adds a type to list. - /// - /// Type - void Add() where T : TBaseType; + /// The type of the param. + /// True when the list contains the item otherwise False. + bool Contains() + where T : TBaseType; - /// - /// Checks if a type exists in the list. - /// - /// Type - /// - bool Contains() where T : TBaseType; - - /// - /// Removes a type from list - /// - /// - void Remove() where T : TBaseType; - } + /// + /// Removes a type from list. + /// + /// The type of the param. + void Remove() + where T : TBaseType; } \ No newline at end of file diff --git a/src/Genocs.Core/Collections/TypeList.cs b/src/Genocs.Core/Collections/TypeList.cs index 727b1b1d..d8c636c6 100644 --- a/src/Genocs.Core/Collections/TypeList.cs +++ b/src/Genocs.Core/Collections/TypeList.cs @@ -1,143 +1,156 @@ -namespace Genocs.Core.Collections +using System.Collections; +using System.Reflection; + +namespace Genocs.Core.Collections; + +/// +/// A shortcut for to use object as base type. +/// +public class TypeList : TypeList, ITypeList { - using System; - using System.Collections; - using System.Collections.Generic; - using System.Reflection; +} +/// +/// Extends to add restriction a specific base type. +/// +/// Base Type of s in this list. +public class TypeList : ITypeList +{ /// - /// A shortcut for to use object as base type. + /// Gets the count. /// - public class TypeList : TypeList, ITypeList + /// The count. + public int Count { + get + { + return _typeList.Count; + } } /// - /// Extends to add restriction a specific base type. + /// Gets a value indicating whether this instance is read only. /// - /// Base Type of s in this list - public class TypeList : ITypeList - { - /// - /// Gets the count. - /// - /// The count. - public int Count { get { return _typeList.Count; } } - - /// - /// Gets a value indicating whether this instance is read only. - /// - /// true if this instance is read only; otherwise, false. - public bool IsReadOnly { get { return false; } } - - /// - /// Gets or sets the at the specified index. - /// - /// Index. - public Type this[int index] - { - get { return _typeList[index]; } - set - { - CheckType(value); - _typeList[index] = value; - } - } - - private readonly List _typeList; + /// true if this instance is read only; otherwise, false. + public bool IsReadOnly + { + get { return false; } + } - /// - /// Creates a new object. - /// - public TypeList() + /// + /// Gets or sets the at the specified index. + /// + /// Index. + public Type this[int index] + { + get { - _typeList = new List(); + return _typeList[index]; } - /// - public void Add() where T : TBaseType + set { - _typeList.Add(typeof(T)); + CheckType(value); + _typeList[index] = value; } + } - /// - public void Add(Type item) - { - CheckType(item); - _typeList.Add(item); - } + private readonly List _typeList; - /// - public void Insert(int index, Type item) - { - _typeList.Insert(index, item); - } + /// + /// Creates a new object. + /// + public TypeList() + { + _typeList = new List(); + } - /// - public int IndexOf(Type item) - { - return _typeList.IndexOf(item); - } + /// + public void Add() + where T : TBaseType + { + _typeList.Add(typeof(T)); + } - /// - public bool Contains() where T : TBaseType - { - return Contains(typeof(T)); - } + /// + public void Add(Type item) + { + CheckType(item); + _typeList.Add(item); + } - /// - public bool Contains(Type item) - { - return _typeList.Contains(item); - } + /// + public void Insert(int index, Type item) + { + _typeList.Insert(index, item); + } - /// - public void Remove() where T : TBaseType - { - _typeList.Remove(typeof(T)); - } + /// + public int IndexOf(Type item) + { + return _typeList.IndexOf(item); + } - /// - public bool Remove(Type item) - { - return _typeList.Remove(item); - } + /// + public bool Contains() + where T : TBaseType + { + return Contains(typeof(T)); + } - /// - public void RemoveAt(int index) - { - _typeList.RemoveAt(index); - } + /// + public bool Contains(Type item) + { + return _typeList.Contains(item); + } - /// - public void Clear() - { - _typeList.Clear(); - } + /// + public void Remove() + where T : TBaseType + { + _typeList.Remove(typeof(T)); + } - /// - public void CopyTo(Type[] array, int arrayIndex) - { - _typeList.CopyTo(array, arrayIndex); - } + /// + public bool Remove(Type item) + { + return _typeList.Remove(item); + } - /// - public IEnumerator GetEnumerator() - { - return _typeList.GetEnumerator(); - } + /// + public void RemoveAt(int index) + { + _typeList.RemoveAt(index); + } - IEnumerator IEnumerable.GetEnumerator() - { - return _typeList.GetEnumerator(); - } + /// + public void Clear() + { + _typeList.Clear(); + } + + /// + public void CopyTo(Type[] array, int arrayIndex) + { + _typeList.CopyTo(array, arrayIndex); + } + + /// + public IEnumerator GetEnumerator() + { + return _typeList.GetEnumerator(); + } - private static void CheckType(Type item) + IEnumerator IEnumerable.GetEnumerator() + { + return _typeList.GetEnumerator(); + } + + private static void CheckType(Type item) + { + if (!typeof(TBaseType).GetTypeInfo().IsAssignableFrom(item)) { - if (!typeof(TBaseType).GetTypeInfo().IsAssignableFrom(item)) - { - throw new ArgumentException("Given item is not type of " + typeof(TBaseType).AssemblyQualifiedName, "item"); - } + throw new ArgumentException("Given item is not type of " + typeof(TBaseType).AssemblyQualifiedName, "item"); } } } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Entities/AggregateRoot.cs b/src/Genocs.Core/Domain/Entities/AggregateRoot.cs index 61afe498..75ca66ea 100644 --- a/src/Genocs.Core/Domain/Entities/AggregateRoot.cs +++ b/src/Genocs.Core/Domain/Entities/AggregateRoot.cs @@ -1,17 +1,17 @@ -//using System.Collections.Generic; -//using System.Collections.ObjectModel; -//using System.ComponentModel.DataAnnotations.Schema; -//using Genocs.Events.Bus; +// using System.Collections.Generic; +// using System.Collections.ObjectModel; +// using System.ComponentModel.DataAnnotations.Schema; +// using Genocs.Events.Bus; namespace Genocs.Core.Domain.Entities; - -public class AggregateRoot : AggregateRoot, IAggregateRoot +public class AggregateRoot : AggregateRoot, IAggregateRoot { } -public class AggregateRoot : Entity, IAggregateRoot +public class AggregateRoot + : Entity, IAggregateRoot { /* [NotMapped] diff --git a/src/Genocs.Core/Domain/Entities/Auditing/AuditedAggregateRoot.cs b/src/Genocs.Core/Domain/Entities/Auditing/AuditedAggregateRoot.cs index 00113b4e..e3e831cc 100644 --- a/src/Genocs.Core/Domain/Entities/Auditing/AuditedAggregateRoot.cs +++ b/src/Genocs.Core/Domain/Entities/Auditing/AuditedAggregateRoot.cs @@ -1,54 +1,52 @@ -namespace Genocs.Core.Domain.Entities.Auditing +using System.ComponentModel.DataAnnotations.Schema; + +namespace Genocs.Core.Domain.Entities.Auditing; + +/// +/// A shortcut of for most used primary key type (). +/// +[Serializable] +public abstract class AuditedAggregateRoot : AuditedAggregateRoot { - using System; - using System.ComponentModel.DataAnnotations.Schema; +} + +/// +/// This class can be used to simplify implementing for aggregate roots. +/// +/// Type of the primary key of the entity +[Serializable] +public abstract class AuditedAggregateRoot : CreationAuditedAggregateRoot, IAudited +{ /// - /// A shortcut of for most used primary key type (). + /// Last modification date of this entity. /// - [Serializable] - public abstract class AuditedAggregateRoot : AuditedAggregateRoot - { - - } + public virtual DateTime? LastUpdate { get; set; } /// - /// This class can be used to simplify implementing for aggregate roots. + /// Last modifier user of this entity. /// - /// Type of the primary key of the entity - [Serializable] - public abstract class AuditedAggregateRoot : CreationAuditedAggregateRoot, IAudited - { - /// - /// Last modification date of this entity. - /// - public virtual DateTime? LastModificationTime { get; set; } - - /// - /// Last modifier user of this entity. - /// - public virtual long? LastModifierUserId { get; set; } - } + public virtual long? UpdatedBy { get; set; } +} +/// +/// This class can be used to simplify implementing for aggregate roots. +/// +/// Type of the primary key of the entity. +/// Type of the user. +[Serializable] +public abstract class AuditedAggregateRoot : AuditedAggregateRoot, IAudited + where TUser : IEntity +{ /// - /// This class can be used to simplify implementing for aggregate roots. + /// Reference to the creator user of this entity. /// - /// Type of the primary key of the entity - /// Type of the user - [Serializable] - public abstract class AuditedAggregateRoot : AuditedAggregateRoot, IAudited - where TUser : IEntity - { - /// - /// Reference to the creator user of this entity. - /// - [ForeignKey("CreatorUserId")] - public virtual TUser CreatorUser { get; set; } + [ForeignKey("CreatorUserId")] + public virtual TUser? CreatorUser { get; set; } - /// - /// Reference to the last modifier user of this entity. - /// - [ForeignKey("LastModifierUserId")] - public virtual TUser LastModifierUser { get; set; } - } + /// + /// Reference to the last modifier user of this entity. + /// + [ForeignKey("LastModifierUserId")] + public virtual TUser? UpdatedByUser { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Entities/Auditing/AuditedEntity.cs b/src/Genocs.Core/Domain/Entities/Auditing/AuditedEntity.cs index b240ffdc..3476f704 100644 --- a/src/Genocs.Core/Domain/Entities/Auditing/AuditedEntity.cs +++ b/src/Genocs.Core/Domain/Entities/Auditing/AuditedEntity.cs @@ -1,54 +1,52 @@ -namespace Genocs.Core.Domain.Entities.Auditing +using System.ComponentModel.DataAnnotations.Schema; + +namespace Genocs.Core.Domain.Entities.Auditing; + +/// +/// A shortcut of for most used primary key type (). +/// +[Serializable] +public abstract class AuditedEntity : AuditedEntity, IEntity { - using System; - using System.ComponentModel.DataAnnotations.Schema; +} + +/// +/// This class can be used to simplify implementing . +/// +/// Type of the primary key of the entity. +[Serializable] +public abstract class AuditedEntity : CreationAuditedEntity, IAudited +{ /// - /// A shortcut of for most used primary key type (). + /// Last modification date of this entity. /// - [Serializable] - public abstract class AuditedEntity : AuditedEntity, IEntity - { - - } + public virtual DateTime? LastUpdate { get; set; } /// - /// This class can be used to simplify implementing . + /// Last modifier user of this entity. /// - /// Type of the primary key of the entity - [Serializable] - public abstract class AuditedEntity : CreationAuditedEntity, IAudited - { - /// - /// Last modification date of this entity. - /// - public virtual DateTime? LastModificationTime { get; set; } - - /// - /// Last modifier user of this entity. - /// - public virtual long? LastModifierUserId { get; set; } - } + public virtual long? UpdatedBy { get; set; } +} +/// +/// This class can be used to simplify implementing . +/// +/// Type of the primary key of the entity. +/// Type of the user. +[Serializable] +public abstract class AuditedEntity : AuditedEntity, IAudited + where TUser : IEntity +{ /// - /// This class can be used to simplify implementing . + /// Reference to the creator user of this entity. /// - /// Type of the primary key of the entity - /// Type of the user - [Serializable] - public abstract class AuditedEntity : AuditedEntity, IAudited - where TUser : IEntity - { - /// - /// Reference to the creator user of this entity. - /// - [ForeignKey("CreatorUserId")] - public virtual TUser CreatorUser { get; set; } + [ForeignKey("CreatorUserId")] + public virtual TUser? CreatorUser { get; set; } - /// - /// Reference to the last modifier user of this entity. - /// - [ForeignKey("LastModifierUserId")] - public virtual TUser LastModifierUser { get; set; } - } + /// + /// Reference to the last modifier user of this entity. + /// + [ForeignKey("LastModifierUserId")] + public virtual TUser? UpdatedByUser { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Entities/Auditing/CreationAuditedAggregateRoot.cs b/src/Genocs.Core/Domain/Entities/Auditing/CreationAuditedAggregateRoot.cs index bf60ca63..8b9e04af 100644 --- a/src/Genocs.Core/Domain/Entities/Auditing/CreationAuditedAggregateRoot.cs +++ b/src/Genocs.Core/Domain/Entities/Auditing/CreationAuditedAggregateRoot.cs @@ -1,59 +1,56 @@ -namespace Genocs.Core.Domain.Entities.Auditing +using System.ComponentModel.DataAnnotations.Schema; + +// using Genocs.Timing; + +namespace Genocs.Core.Domain.Entities.Auditing; + +/// +/// A shortcut of for most used primary key type (). +/// +[Serializable] +public abstract class CreationAuditedAggregateRoot : CreationAuditedAggregateRoot { - using System; - using System.ComponentModel.DataAnnotations.Schema; - //using Genocs.Timing; +} +/// +/// This class can be used to simplify implementing for aggregate roots. +/// +/// Type of the primary key of the entity. +[Serializable] +public abstract class CreationAuditedAggregateRoot : AggregateRoot, ICreationAudited +{ /// - /// A shortcut of for most used primary key type (). + /// Creation time of this entity. /// - [Serializable] - public abstract class CreationAuditedAggregateRoot : CreationAuditedAggregateRoot - { - - } + public virtual DateTime CreatedAt { get; set; } /// - /// This class can be used to simplify implementing for aggregate roots. + /// Creator of this entity. /// - /// Type of the primary key of the entity - [Serializable] - public abstract class CreationAuditedAggregateRoot : AggregateRoot, ICreationAudited - { - /// - /// Creation time of this entity. - /// - public virtual DateTime CreationTime { get; set; } - - /// - /// Creator of this entity. - /// - public virtual long? CreatorUserId { get; set; } - - /// - /// Constructor. - /// - protected CreationAuditedAggregateRoot() - { - //CreationTime = Clock.Now; // Manage timezone - CreationTime = DateTime.Now; - - } - } + public virtual long CreatorUserId { get; set; } /// - /// This class can be used to simplify implementing for aggregate roots. + /// Constructor. /// - /// Type of the primary key of the entity - /// Type of the user - [Serializable] - public abstract class CreationAuditedAggregateRoot : CreationAuditedAggregateRoot, ICreationAudited - where TUser : IEntity + protected CreationAuditedAggregateRoot() { - /// - /// Reference to the creator user of this entity. - /// - [ForeignKey("CreatorUserId")] - public virtual TUser CreatorUser { get; set; } + // CreationTime = Clock.Now; // Manage time zone + CreatedAt = DateTime.Now; } } + +/// +/// This class can be used to simplify implementing for aggregate roots. +/// +/// Type of the primary key of the entity. +/// Type of the user. +[Serializable] +public abstract class CreationAuditedAggregateRoot : CreationAuditedAggregateRoot, ICreationAudited + where TUser : IEntity +{ + /// + /// Reference to the creator user of this entity. + /// + [ForeignKey("CreatorUserId")] + public virtual TUser? CreatorUser { get; set; } +} diff --git a/src/Genocs.Core/Domain/Entities/Auditing/CreationAuditedEntity.cs b/src/Genocs.Core/Domain/Entities/Auditing/CreationAuditedEntity.cs index 0de55ae5..9299acb6 100644 --- a/src/Genocs.Core/Domain/Entities/Auditing/CreationAuditedEntity.cs +++ b/src/Genocs.Core/Domain/Entities/Auditing/CreationAuditedEntity.cs @@ -1,59 +1,56 @@ -namespace Genocs.Core.Domain.Entities.Auditing +using System.ComponentModel.DataAnnotations.Schema; + +namespace Genocs.Core.Domain.Entities.Auditing; + +/// +/// A shortcut of for most used primary key type (). +/// +[Serializable] +public abstract class CreationAuditedEntity : CreationAuditedEntity, IEntity { - using System; - using System.ComponentModel.DataAnnotations.Schema; - //using Genocs.Timing; +} + +/// +/// This class can be used to simplify implementing . +/// +/// Type of the primary key of the entity. +[Serializable] +public abstract class CreationAuditedEntity : Entity, ICreationAudited +{ /// - /// A shortcut of for most used primary key type (). + /// Creation time of this entity. /// - [Serializable] - public abstract class CreationAuditedEntity : CreationAuditedEntity, IEntity - { + public virtual DateTime CreatedAt { get; set; } - } + /// + /// Creator of this entity. + /// + public virtual long CreatorUserId { get; set; } /// - /// This class can be used to simplify implementing . + /// Constructor. /// - /// Type of the primary key of the entity - [Serializable] - public abstract class CreationAuditedEntity : Entity, ICreationAudited + protected CreationAuditedEntity() { - /// - /// Creation time of this entity. - /// - public virtual DateTime CreationTime { get; set; } - - /// - /// Creator of this entity. - /// - public virtual long? CreatorUserId { get; set; } - - /// - /// Constructor. - /// - protected CreationAuditedEntity() - { - //CreationTime = Clock.Now; - CreationTime = DateTime.Now; - - } + // CreationTime = Clock.Now; + CreatedAt = DateTime.Now; + } +} +/// +/// This class can be used to simplify implementing . +/// +/// Type of the primary key of the entity. +/// Type of the user. +[Serializable] +public abstract class CreationAuditedEntity : CreationAuditedEntity, ICreationAudited + where TUser : IEntity +{ /// - /// This class can be used to simplify implementing . + /// Reference to the creator user of this entity. /// - /// Type of the primary key of the entity - /// Type of the user - [Serializable] - public abstract class CreationAuditedEntity : CreationAuditedEntity, ICreationAudited - where TUser : IEntity - { - /// - /// Reference to the creator user of this entity. - /// - [ForeignKey("CreatorUserId")] - public virtual TUser CreatorUser { get; set; } - } + [ForeignKey("CreatorUserId")] + public virtual TUser CreatorUser { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Entities/Auditing/EntityAuditingHelper.cs b/src/Genocs.Core/Domain/Entities/Auditing/EntityAuditingHelper.cs index 10c0bfbf..e05e7853 100644 --- a/src/Genocs.Core/Domain/Entities/Auditing/EntityAuditingHelper.cs +++ b/src/Genocs.Core/Domain/Entities/Auditing/EntityAuditingHelper.cs @@ -1,120 +1,120 @@ -namespace Genocs.Core.Domain.Entities.Auditing +// using Genocs.Timing; +// using Genocs.Core.Configuration.Startup; +// using Genocs.Core.MultiTenancy; +using Genocs.Core.Extensions; + +namespace Genocs.Core.Domain.Entities.Auditing; + +public static class EntityAuditingHelper { - //using Genocs.Timing; - using System; - //using Genocs.Core.Configuration.Startup; - //using Genocs.Core.MultiTenancy; - using Genocs.Core.Extensions; + public static void SetCreationAuditProperties( - public static class EntityAuditingHelper + // IMultiTenancyConfig multiTenancyConfig, + object entityAsObj, + int? tenantId, + long? userId) { - public static void SetCreationAuditProperties( - //IMultiTenancyConfig multiTenancyConfig, - object entityAsObj, - int? tenantId, - long? userId) + var entityWithCreationTime = entityAsObj as IHasCreationTime; + if (entityWithCreationTime == null) { - var entityWithCreationTime = entityAsObj as IHasCreationTime; - if (entityWithCreationTime == null) - { - //Object does not implement IHasCreationTime - return; - } + // Object does not implement IHasCreationTime + return; + } - if (entityWithCreationTime.CreationTime == default(DateTime)) - { - //entityWithCreationTime.CreationTime = Clock.Now; - entityWithCreationTime.CreationTime = DateTime.Now; + if (entityWithCreationTime.CreatedAt == default(DateTime)) + { + // entityWithCreationTime.CreationTime = Clock.Now; + // entityWithCreationTime.CreatedAt = DateTime.Now; + } - } + if (!(entityAsObj is ICreationAudited)) + { + // Object does not implement ICreationAudited + return; + } - if (!(entityAsObj is ICreationAudited)) - { - //Object does not implement ICreationAudited - return; - } + if (!userId.HasValue) + { + // Unknown user + return; + } - if (!userId.HasValue) - { - //Unknown user - return; - } + var entity = entityAsObj as ICreationAudited; + if (entity.CreatorUserId != null) + { + // CreatorUserId is already set + return; + } - var entity = entityAsObj as ICreationAudited; - if (entity.CreatorUserId != null) - { - //CreatorUserId is already set - return; - } + //if (multiTenancyConfig?.IsEnabled == true) + //{ + // if (MultiTenancyHelper.IsMultiTenantEntity(entity) && + // !MultiTenancyHelper.IsTenantEntity(entity, tenantId)) + // { + // //A tenant entitiy is created by host or a different tenant + // return; + // } + + // if (tenantId.HasValue && MultiTenancyHelper.IsHostEntity(entity)) + // { + // //Tenant user created a host entity + // return; + // } + //} + + // Finally, set CreatorUserId! + entity.CreatorUserId = userId.Value; + } - //if (multiTenancyConfig?.IsEnabled == true) - //{ - // if (MultiTenancyHelper.IsMultiTenantEntity(entity) && - // !MultiTenancyHelper.IsTenantEntity(entity, tenantId)) - // { - // //A tenant entitiy is created by host or a different tenant - // return; - // } - - // if (tenantId.HasValue && MultiTenancyHelper.IsHostEntity(entity)) - // { - // //Tenant user created a host entity - // return; - // } - //} - - //Finally, set CreatorUserId! - entity.CreatorUserId = userId; + public static void SetModificationAuditProperties( + + // IMultiTenancyConfig multiTenancyConfig, + object entityAsObj, + int? tenantId, + long? userId) + { + if (entityAsObj is IHasModificationTime) + { + // entityAsObj.As().LastModificationTime = Clock.Now; + entityAsObj.As().LastUpdate = DateTime.Now; } - public static void SetModificationAuditProperties( - //IMultiTenancyConfig multiTenancyConfig, - object entityAsObj, - int? tenantId, - long? userId) + if (!(entityAsObj is IModificationAudited)) { - if (entityAsObj is IHasModificationTime) - { - //entityAsObj.As().LastModificationTime = Clock.Now; - entityAsObj.As().LastModificationTime = DateTime.Now; - } + // Entity does not implement IModificationAudited + return; + } - if (!(entityAsObj is IModificationAudited)) - { - //Entity does not implement IModificationAudited - return; - } + var entity = entityAsObj.As(); - var entity = entityAsObj.As(); + if (userId == null) + { + // Unknown user + entity.UpdatedBy = null; + return; + } - if (userId == null) + /* + if (multiTenancyConfig?.IsEnabled == true) + { + if (MultiTenancyHelper.IsMultiTenantEntity(entity) && + !MultiTenancyHelper.IsTenantEntity(entity, tenantId)) { - //Unknown user + //A tenant entity is modified by host or a different tenant entity.LastModifierUserId = null; return; } - /* - if (multiTenancyConfig?.IsEnabled == true) + if (tenantId.HasValue && MultiTenancyHelper.IsHostEntity(entity)) { - if (MultiTenancyHelper.IsMultiTenantEntity(entity) && - !MultiTenancyHelper.IsTenantEntity(entity, tenantId)) - { - //A tenant entitiy is modified by host or a different tenant - entity.LastModifierUserId = null; - return; - } - - if (tenantId.HasValue && MultiTenancyHelper.IsHostEntity(entity)) - { - //Tenant user modified a host entity - entity.LastModifierUserId = null; - return; - } + //Tenant user modified a host entity + entity.LastModifierUserId = null; + return; } - */ - //Finally, set LastModifierUserId! - entity.LastModifierUserId = userId; } + */ + + // Finally, set LastModifierUserId! + entity.UpdatedBy = userId; } } diff --git a/src/Genocs.Core/Domain/Entities/Auditing/FullAuditedAggregateRoot.cs b/src/Genocs.Core/Domain/Entities/Auditing/FullAuditedAggregateRoot.cs index c09c5654..63f29d02 100644 --- a/src/Genocs.Core/Domain/Entities/Auditing/FullAuditedAggregateRoot.cs +++ b/src/Genocs.Core/Domain/Entities/Auditing/FullAuditedAggregateRoot.cs @@ -1,68 +1,66 @@ -namespace Genocs.Core.Domain.Entities.Auditing +using System.ComponentModel.DataAnnotations.Schema; + +namespace Genocs.Core.Domain.Entities.Auditing; + +/// +/// A shortcut of for most used primary key type (). +/// +[Serializable] +public abstract class FullAuditedAggregateRoot : FullAuditedAggregateRoot { - using System; - using System.ComponentModel.DataAnnotations.Schema; +} + +/// +/// Implements to be a base class for full-audited aggregate roots. +/// +/// Type of the primary key of the entity. +[Serializable] +public abstract class FullAuditedAggregateRoot : AuditedAggregateRoot, IFullAudited +{ /// - /// A shortcut of for most used primary key type (). + /// It defines whether this entity is deleted or not. /// - [Serializable] - public abstract class FullAuditedAggregateRoot : FullAuditedAggregateRoot - { - - } + public virtual bool IsDeleted { get; set; } /// - /// Implements to be a base class for full-audited aggregate roots. + /// Which user deleted this entity. /// - /// Type of the primary key of the entity - [Serializable] - public abstract class FullAuditedAggregateRoot : AuditedAggregateRoot, IFullAudited - { - /// - /// Is this entity Deleted? - /// - public virtual bool IsDeleted { get; set; } - - /// - /// Which user deleted this entity? - /// - public virtual long? DeleterUserId { get; set; } + public virtual long? DeletedBy { get; set; } - /// - /// Deletion time of this entity. - /// - public virtual DateTime? DeletionTime { get; set; } - } + /// + /// Deletion time of this entity. + /// + public virtual DateTime? DeletedAt { get; set; } +} +/// +/// Implements to be a base class for full-audited aggregate roots. +/// +/// Type of the primary key of the entity. +/// Type of the user. +[Serializable] +public abstract class FullAuditedAggregateRoot : AuditedAggregateRoot, IFullAudited + where TUser : IEntity +{ /// - /// Implements to be a base class for full-audited aggregate roots. + /// It defines whether this entity is deleted or not. /// - /// Type of the primary key of the entity - /// Type of the user - [Serializable] - public abstract class FullAuditedAggregateRoot : AuditedAggregateRoot, IFullAudited - where TUser : IEntity - { - /// - /// Is this entity Deleted? - /// - public virtual bool IsDeleted { get; set; } + public virtual bool IsDeleted { get; set; } - /// - /// Reference to the deleter user of this entity. - /// - [ForeignKey("DeleterUserId")] - public virtual TUser DeleterUser { get; set; } + /// + /// Reference to the deleter user of this entity. + /// + [ForeignKey("DeleterUserId")] + public virtual TUser DeletedByUser { get; set; } - /// - /// Which user deleted this entity? - /// - public virtual long? DeleterUserId { get; set; } + /// + /// Which user deleted this entity. + /// + public virtual long? DeletedBy { get; set; } - /// - /// Deletion time of this entity. - /// - public virtual DateTime? DeletionTime { get; set; } - } + /// + /// Deletion time of this entity. + /// + public virtual DateTime? DeletedAt { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Entities/Auditing/FullAuditedEntity.cs b/src/Genocs.Core/Domain/Entities/Auditing/FullAuditedEntity.cs index 774135a5..72a30ccb 100644 --- a/src/Genocs.Core/Domain/Entities/Auditing/FullAuditedEntity.cs +++ b/src/Genocs.Core/Domain/Entities/Auditing/FullAuditedEntity.cs @@ -1,68 +1,67 @@ -namespace Genocs.Core.Domain.Entities.Auditing +using System.ComponentModel.DataAnnotations.Schema; + +namespace Genocs.Core.Domain.Entities.Auditing; + +/// +/// A shortcut of for most used primary key type (). +/// +[Serializable] +public abstract class FullAuditedEntity : FullAuditedEntity, IEntity { - using System; - using System.ComponentModel.DataAnnotations.Schema; +} + +/// +/// Implements to be a base class for full-audited entities. +/// +/// Type of the primary key of the entity. +[Serializable] +public abstract class FullAuditedEntity : AuditedEntity, IFullAudited +{ /// - /// A shortcut of for most used primary key type (). + /// it determines if the entity is deleted. + /// Used for soft delete. /// - [Serializable] - public abstract class FullAuditedEntity : FullAuditedEntity, IEntity - { - - } + public virtual bool IsDeleted { get; set; } /// - /// Implements to be a base class for full-audited entities. + /// It determines the user who deleted this entity. /// - /// Type of the primary key of the entity - [Serializable] - public abstract class FullAuditedEntity : AuditedEntity, IFullAudited - { - /// - /// Is this entity Deleted? - /// - public virtual bool IsDeleted { get; set; } - - /// - /// Which user deleted this entity? - /// - public virtual long? DeleterUserId { get; set; } + public virtual long? DeletedBy { get; set; } - /// - /// Deletion time of this entity. - /// - public virtual DateTime? DeletionTime { get; set; } - } + /// + /// Deletion time of this entity. + /// + public virtual DateTime? DeletedAt { get; set; } +} +/// +/// Implements to be a base class for full-audited entities. +/// +/// Type of the primary key of the entity. +/// Type of the user. +[Serializable] +public abstract class FullAuditedEntity : AuditedEntity, IFullAudited + where TUser : IEntity +{ /// - /// Implements to be a base class for full-audited entities. + /// It determines if the entity is deleted. /// - /// Type of the primary key of the entity - /// Type of the user - [Serializable] - public abstract class FullAuditedEntity : AuditedEntity, IFullAudited - where TUser : IEntity - { - /// - /// Is this entity Deleted? - /// - public virtual bool IsDeleted { get; set; } + public virtual bool IsDeleted { get; set; } - /// - /// Reference to the deleter user of this entity. - /// - [ForeignKey("DeleterUserId")] - public virtual TUser DeleterUser { get; set; } + /// + /// Reference to the deleter user of this entity. + /// + [ForeignKey("DeleterUserId")] + public virtual TUser? DeletedByUser { get; set; } - /// - /// Which user deleted this entity? - /// - public virtual long? DeleterUserId { get; set; } + /// + /// Which user deleted this entity. + /// + public virtual long? DeletedBy { get; set; } - /// - /// Deletion time of this entity. - /// - public virtual DateTime? DeletionTime { get; set; } - } + /// + /// Deletion time of this entity. + /// + public virtual DateTime? DeletedAt { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Entities/Auditing/IAudited.cs b/src/Genocs.Core/Domain/Entities/Auditing/IAudited.cs index 2a967930..45873b8c 100644 --- a/src/Genocs.Core/Domain/Entities/Auditing/IAudited.cs +++ b/src/Genocs.Core/Domain/Entities/Auditing/IAudited.cs @@ -1,21 +1,20 @@ -namespace Genocs.Core.Domain.Entities.Auditing +namespace Genocs.Core.Domain.Entities.Auditing; + +/// +/// This interface is implemented by entities which must be audited. +/// Related properties automatically set when saving/updating objects. +/// +public interface IAudited : ICreationAudited, IModificationAudited { - /// - /// This interface is implemented by entities which must be audited. - /// Related properties automatically set when saving/updating objects. - /// - public interface IAudited : ICreationAudited, IModificationAudited - { - } +} - /// - /// Adds navigation properties to interface for user. - /// - /// Type of the user - public interface IAudited : IAudited, ICreationAudited, IModificationAudited - where TUser : IEntity - { +/// +/// Adds navigation properties to interface for user. +/// +/// Type of the user. +public interface IAudited : IAudited, ICreationAudited, IModificationAudited + where TUser : IEntity +{ - } } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Entities/Auditing/ICreationAudited.cs b/src/Genocs.Core/Domain/Entities/Auditing/ICreationAudited.cs index b220f065..8b2082b0 100644 --- a/src/Genocs.Core/Domain/Entities/Auditing/ICreationAudited.cs +++ b/src/Genocs.Core/Domain/Entities/Auditing/ICreationAudited.cs @@ -1,27 +1,26 @@ -namespace Genocs.Core.Domain.Entities.Auditing +namespace Genocs.Core.Domain.Entities.Auditing; + +/// +/// This interface is implemented by entities that is wanted to store creation information (who and when created). +/// Creation time and creator user are automatically set when saving to database. +/// +public interface ICreationAudited : IHasCreationTime { /// - /// This interface is implemented by entities that is wanted to store creation information (who and when created). - /// Creation time and creator user are automatically set when saving to database. + /// Id of the creator user of this entity. /// - public interface ICreationAudited : IHasCreationTime - { - /// - /// Id of the creator user of this entity. - /// - long? CreatorUserId { get; set; } - } + long CreatorUserId { get; set; } +} +/// +/// Adds navigation properties to interface for user. +/// +/// Type of the user +public interface ICreationAudited : ICreationAudited + where TUser : IEntity +{ /// - /// Adds navigation properties to interface for user. + /// Reference to the creator user of this entity. /// - /// Type of the user - public interface ICreationAudited : ICreationAudited - where TUser : IEntity - { - /// - /// Reference to the creator user of this entity. - /// - TUser CreatorUser { get; set; } - } + TUser CreatorUser { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Entities/Auditing/IDeletionAudited.cs b/src/Genocs.Core/Domain/Entities/Auditing/IDeletionAudited.cs index 17b56ca1..125de9aa 100644 --- a/src/Genocs.Core/Domain/Entities/Auditing/IDeletionAudited.cs +++ b/src/Genocs.Core/Domain/Entities/Auditing/IDeletionAudited.cs @@ -1,26 +1,25 @@ -namespace Genocs.Core.Domain.Entities.Auditing +namespace Genocs.Core.Domain.Entities.Auditing; + +/// +/// This interface is implemented by entities which wanted to store deletion information (who and when deleted). +/// +public interface IDeletionAudited : IHasDeletionTime { /// - /// This interface is implemented by entities which wanted to store deletion information (who and when deleted). + /// Which user deleted this entity. /// - public interface IDeletionAudited : IHasDeletionTime - { - /// - /// Which user deleted this entity? - /// - long? DeleterUserId { get; set; } - } + long? DeletedBy { get; set; } +} +/// +/// Adds navigation properties to interface for user. +/// +/// Type of the user. +public interface IDeletionAudited : IDeletionAudited + where TUser : IEntity +{ /// - /// Adds navigation properties to interface for user. + /// Reference to the deleter user of this entity. /// - /// Type of the user - public interface IDeletionAudited : IDeletionAudited - where TUser : IEntity - { - /// - /// Reference to the deleter user of this entity. - /// - TUser DeleterUser { get; set; } - } + TUser? DeletedByUser { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Entities/Auditing/IFullAudited.cs b/src/Genocs.Core/Domain/Entities/Auditing/IFullAudited.cs index 720cc2c1..aa902887 100644 --- a/src/Genocs.Core/Domain/Entities/Auditing/IFullAudited.cs +++ b/src/Genocs.Core/Domain/Entities/Auditing/IFullAudited.cs @@ -1,20 +1,18 @@ -namespace Genocs.Core.Domain.Entities.Auditing +namespace Genocs.Core.Domain.Entities.Auditing; + +/// +/// This interface ads to for a fully audited entity. +/// +public interface IFullAudited : IAudited, IDeletionAudited { - /// - /// This interface ads to for a fully audited entity. - /// - public interface IFullAudited : IAudited, IDeletionAudited - { - - } +} - /// - /// Adds navigation properties to interface for user. - /// - /// Type of the user - public interface IFullAudited : IAudited, IFullAudited, IDeletionAudited - where TUser : IEntity - { +/// +/// Adds navigation properties to interface for user. +/// +/// Type of the user. +public interface IFullAudited : IAudited, IFullAudited, IDeletionAudited + where TUser : IEntity +{ - } } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Entities/Auditing/IHasCreationTime.cs b/src/Genocs.Core/Domain/Entities/Auditing/IHasCreationTime.cs index 7aae3621..b8867ce7 100644 --- a/src/Genocs.Core/Domain/Entities/Auditing/IHasCreationTime.cs +++ b/src/Genocs.Core/Domain/Entities/Auditing/IHasCreationTime.cs @@ -1,16 +1,13 @@ -namespace Genocs.Core.Domain.Entities.Auditing -{ - using System; +namespace Genocs.Core.Domain.Entities.Auditing; +/// +/// An entity can implement this interface if of this entity must be stored. +/// is automatically set when saving to database. +/// +public interface IHasCreationTime +{ /// - /// An entity can implement this interface if of this entity must be stored. - /// is automatically set when saving to database. + /// Creation time of this entity. /// - public interface IHasCreationTime - { - /// - /// Creation time of this entity. - /// - DateTime CreationTime { get; set; } - } + DateTime CreatedAt { get; } } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Entities/Auditing/IHasDeletionTime.cs b/src/Genocs.Core/Domain/Entities/Auditing/IHasDeletionTime.cs index bcf30abe..9adc7903 100644 --- a/src/Genocs.Core/Domain/Entities/Auditing/IHasDeletionTime.cs +++ b/src/Genocs.Core/Domain/Entities/Auditing/IHasDeletionTime.cs @@ -1,16 +1,13 @@ -namespace Genocs.Core.Domain.Entities.Auditing -{ - using System; +namespace Genocs.Core.Domain.Entities.Auditing; +/// +/// An entity can implement this interface if of this entity must be stored. +/// is automatically set when deleting . +/// +public interface IHasDeletionTime : ISoftDelete +{ /// - /// An entity can implement this interface if of this entity must be stored. - /// is automatically set when deleting . + /// Deletion time of this entity. /// - public interface IHasDeletionTime : ISoftDelete - { - /// - /// Deletion time of this entity. - /// - DateTime? DeletionTime { get; set; } - } + DateTime? DeletedAt { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Entities/Auditing/IHasModificationTime.cs b/src/Genocs.Core/Domain/Entities/Auditing/IHasModificationTime.cs index 80519008..4c0c647b 100644 --- a/src/Genocs.Core/Domain/Entities/Auditing/IHasModificationTime.cs +++ b/src/Genocs.Core/Domain/Entities/Auditing/IHasModificationTime.cs @@ -1,16 +1,13 @@ -namespace Genocs.Core.Domain.Entities.Auditing -{ - using System; +namespace Genocs.Core.Domain.Entities.Auditing; +/// +/// An entity can implement this interface if of this entity must be stored. +/// is automatically set when updating . +/// +public interface IHasModificationTime +{ /// - /// An entity can implement this interface if of this entity must be stored. - /// is automatically set when updating . + /// The last modified time for this entity. /// - public interface IHasModificationTime - { - /// - /// The last modified time for this entity. - /// - DateTime? LastModificationTime { get; set; } - } + DateTime? LastUpdate { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Entities/Auditing/IModificationAudited.cs b/src/Genocs.Core/Domain/Entities/Auditing/IModificationAudited.cs index bdcd8c3a..d12f0438 100644 --- a/src/Genocs.Core/Domain/Entities/Auditing/IModificationAudited.cs +++ b/src/Genocs.Core/Domain/Entities/Auditing/IModificationAudited.cs @@ -1,27 +1,26 @@ -namespace Genocs.Core.Domain.Entities.Auditing +namespace Genocs.Core.Domain.Entities.Auditing; + +/// +/// This interface is implemented by entities that is wanted to store modification information (who and when modified lastly). +/// Properties are automatically set when updating the . +/// +public interface IModificationAudited : IHasModificationTime { /// - /// This interface is implemented by entities that is wanted to store modification information (who and when modified lastly). - /// Properties are automatically set when updating the . + /// Last modifier user for this entity. /// - public interface IModificationAudited : IHasModificationTime - { - /// - /// Last modifier user for this entity. - /// - long? LastModifierUserId { get; set; } - } + long? UpdatedBy { get; set; } +} +/// +/// Adds navigation properties to interface for user. +/// +/// Type of the user +public interface IModificationAudited : IModificationAudited + where TUser : IEntity +{ /// - /// Adds navigation properties to interface for user. + /// Reference to the last modifier user of this entity. /// - /// Type of the user - public interface IModificationAudited : IModificationAudited - where TUser : IEntity - { - /// - /// Reference to the last modifier user of this entity. - /// - TUser LastModifierUser { get; set; } - } + TUser? UpdatedByUser { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Entities/Entity.cs b/src/Genocs.Core/Domain/Entities/Entity.cs index 7e1c7cba..3dd43e58 100644 --- a/src/Genocs.Core/Domain/Entities/Entity.cs +++ b/src/Genocs.Core/Domain/Entities/Entity.cs @@ -1,14 +1,12 @@ using System.Reflection; -//using Genocs.Extensions; namespace Genocs.Core.Domain.Entities; - /// -/// A shortcut of for most used primary key type (). +/// A shortcut of for most used primary key type (). /// [Serializable] -public abstract class Entity : Entity, IEntity +public abstract class Entity : Entity { } @@ -17,27 +15,27 @@ public abstract class Entity : Entity, IEntity /// Basic implementation of IEntity interface. /// An entity can inherit this class of directly implement to IEntity interface. /// -/// Type of the primary key of the entity +/// Type of the primary key of the entity. [Serializable] public abstract class Entity : IEntity { /// /// Unique identifier for this entity. /// - public virtual TPrimaryKey Id { get; set; } + public virtual TPrimaryKey Id { get; set; } = default!; /// /// Checks if this entity is transient (it has not an Id). /// - /// True, if this entity is transient + /// True, if this entity is transient. public virtual bool IsTransient() { - if (EqualityComparer.Default.Equals(Id, default(TPrimaryKey))) + if (EqualityComparer.Default.Equals(Id!, default!)) { return true; } - //Workaround for EF Core since it sets int/long to min value when attaching to dbcontext + // Workaround for EF Core since it sets int/long to min value when attaching to dB context if (typeof(TPrimaryKey) == typeof(int)) { return Convert.ToInt32(Id) <= 0; @@ -52,27 +50,27 @@ public virtual bool IsTransient() } /// - public override bool Equals(object obj) + public override bool Equals(object? obj) { - if (obj == null || !(obj is Entity)) + if (obj == null || obj is not Entity) { return false; } - //Same instances must be considered as equal + // Same instances must be considered as equal if (ReferenceEquals(this, obj)) { return true; } - //Transient objects are not considered as equal + // Transient objects are not considered as equal var other = (Entity)obj; if (IsTransient() && other.IsTransient()) { return false; } - //Must have a IS-A relation of types or must be same type + // Must have a IS-A relation of types or must be same type var typeOfThis = GetType(); var typeOfOther = other.GetType(); if (!typeOfThis.GetTypeInfo().IsAssignableFrom(typeOfOther) && !typeOfOther.GetTypeInfo().IsAssignableFrom(typeOfThis)) @@ -93,7 +91,7 @@ public override bool Equals(object obj) return false; } */ - return Id.Equals(other.Id); + return Id!.Equals(other.Id); } /// diff --git a/src/Genocs.Core/Domain/Entities/EntityExtensions.cs b/src/Genocs.Core/Domain/Entities/EntityExtensions.cs index 66666e4b..f085e29d 100644 --- a/src/Genocs.Core/Domain/Entities/EntityExtensions.cs +++ b/src/Genocs.Core/Domain/Entities/EntityExtensions.cs @@ -3,7 +3,6 @@ namespace Genocs.Core.Domain.Entities; - /// /// Some useful extension methods for Entities. /// @@ -27,8 +26,8 @@ public static void UnDelete(this ISoftDelete entity) if (entity is IDeletionAudited) { var deletionAuditedEntity = entity.As(); - deletionAuditedEntity.DeletionTime = null; - deletionAuditedEntity.DeleterUserId = null; + deletionAuditedEntity.DeletedAt = null; + deletionAuditedEntity.DeletedBy = null; } } } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Entities/EntityNotFoundException.cs b/src/Genocs.Core/Domain/Entities/EntityNotFoundException.cs index ea0a21f7..be8d2623 100644 --- a/src/Genocs.Core/Domain/Entities/EntityNotFoundException.cs +++ b/src/Genocs.Core/Domain/Entities/EntityNotFoundException.cs @@ -1,80 +1,78 @@ -namespace Genocs.Core.Domain.Entities +using Genocs.Core.Exceptions; +using System.Runtime.Serialization; + +namespace Genocs.Core.Domain.Entities; + +/// +/// This exception is thrown if an entity excepted to be found but not found. +/// +[Serializable] +public class EntityNotFoundException : GenocsException { - using System; - using System.Runtime.Serialization; - using Genocs.Core.Exceptions; + /// + /// Type of the entity. + /// + public Type? EntityType { get; set; } /// - /// This exception is thrown if an entity excepted to be found but not found. + /// Id of the Entity. /// - [Serializable] - public class EntityNotFoundException : GenocsException - { - /// - /// Type of the entity. - /// - public Type EntityType { get; set; } + public object? Id { get; set; } - /// - /// Id of the Entity. - /// - public object Id { get; set; } + /// + /// Creates a new object. + /// + public EntityNotFoundException() + { - /// - /// Creates a new object. - /// - public EntityNotFoundException() - { + } - } + /// + /// Creates a new object. + /// + public EntityNotFoundException(SerializationInfo serializationInfo, StreamingContext context) + : base(serializationInfo, context) + { - /// - /// Creates a new object. - /// - public EntityNotFoundException(SerializationInfo serializationInfo, StreamingContext context) - : base(serializationInfo, context) - { + } - } - - /// - /// Creates a new object. - /// - public EntityNotFoundException(Type entityType, object id) - : this(entityType, id, null) - { + /// + /// Creates a new object. + /// + public EntityNotFoundException(Type entityType, object id) + : this(entityType, id, null) + { - } + } - /// - /// Creates a new object. - /// - public EntityNotFoundException(Type entityType, object id, Exception innerException) - : base($"There is no such an entity. Entity type: {entityType.FullName}, id: {id}", innerException) - { - EntityType = entityType; - Id = id; - } + /// + /// Creates a new object. + /// + public EntityNotFoundException(Type entityType, object id, Exception? innerException) + : base($"There is no such an entity. Entity type: {entityType.FullName}, id: {id}", innerException) + { + EntityType = entityType; + Id = id; + } - /// - /// Creates a new object. - /// - /// Exception message - public EntityNotFoundException(string message) - : base(message) - { + /// + /// Creates a new object. + /// + /// Exception message. + public EntityNotFoundException(string message) + : base(message) + { - } + } - /// - /// Creates a new object. - /// - /// Exception message - /// Inner exception - public EntityNotFoundException(string message, Exception innerException) - : base(message, innerException) - { + /// + /// Creates a new object. + /// + /// Exception message. + /// Inner exception. + public EntityNotFoundException(string message, Exception? innerException) + : base(message, innerException) + { - } } } diff --git a/src/Genocs.Core/Domain/Entities/IAggregateRoot.cs b/src/Genocs.Core/Domain/Entities/IAggregateRoot.cs index 20c1f6ea..0150eeff 100644 --- a/src/Genocs.Core/Domain/Entities/IAggregateRoot.cs +++ b/src/Genocs.Core/Domain/Entities/IAggregateRoot.cs @@ -1,21 +1,22 @@ -namespace Genocs.Core.Domain.Entities +// using System.Collections.Generic; +// using Genocs.Events.Bus; + +namespace Genocs.Core.Domain.Entities; + +public interface IAggregateRoot : IEntity { - //using System.Collections.Generic; - //using Genocs.Events.Bus; - public interface IAggregateRoot : IAggregateRoot, IEntity - { +} - } +public interface IAggregateRoot : IEntity, IAggregateRoot/*, IGeneratesDomainEvents */ +{ - public interface IAggregateRoot : IEntity/*, IGeneratesDomainEvents */ - { +} - } /* - public interface IGeneratesDomainEvents - { - ICollection DomainEvents { get; } - } - */ -} \ No newline at end of file +public interface IGeneratesDomainEvents +{ + ICollection DomainEvents { get; } +} + +*/ diff --git a/src/Genocs.Core/Domain/Entities/IEntity.cs b/src/Genocs.Core/Domain/Entities/IEntity.cs index 69eb3aff..2403716f 100644 --- a/src/Genocs.Core/Domain/Entities/IEntity.cs +++ b/src/Genocs.Core/Domain/Entities/IEntity.cs @@ -1,12 +1,14 @@ -using Genocs.Common.Types; +namespace Genocs.Core.Domain.Entities; -namespace Genocs.Core.Domain.Entities +/// +/// The base interface for all entities. +/// +public interface IEntity { + /// - /// A shortcut of for most used primary key type (). + /// Checks if this entity is transient (not persisted to database) />). /// - public interface IEntity : IIdentifiable - { - - } + /// True, if this entity is transient, otherwise false. + bool IsTransient(); } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Entities/IEntityOfTPrimaryKey.cs b/src/Genocs.Core/Domain/Entities/IEntityOfTPrimaryKey.cs index 057839c1..07863ce8 100644 --- a/src/Genocs.Core/Domain/Entities/IEntityOfTPrimaryKey.cs +++ b/src/Genocs.Core/Domain/Entities/IEntityOfTPrimaryKey.cs @@ -1,14 +1,11 @@ -namespace Genocs.Core.Domain.Entities +using Genocs.Common.Types; + +namespace Genocs.Core.Domain.Entities; + +/// +/// Defines interface for base entity type. All the domain object must implement this interface. +/// +/// Type of the primary key of the entity. +public interface IEntity : IEntity, IIdentifiable { - /// - /// Defines interface for base entity type. All entities in the system must implement this interface. - /// - /// Type of the primary key of the entity - public interface IEntity - { - /// - /// Unique identifier for this entity. - /// - TPrimaryKey Id { get; set; } - } } diff --git a/src/Genocs.Core/Domain/Entities/ISoftDelete.cs b/src/Genocs.Core/Domain/Entities/ISoftDelete.cs index 8d747ddb..67044680 100644 --- a/src/Genocs.Core/Domain/Entities/ISoftDelete.cs +++ b/src/Genocs.Core/Domain/Entities/ISoftDelete.cs @@ -1,16 +1,15 @@ -namespace Genocs.Core.Domain.Entities +namespace Genocs.Core.Domain.Entities; + +/// +/// Used to standardize soft deleting entities. +/// Soft-delete entities are not actually deleted, +/// marked as IsDeleted = true in the database, +/// but can not be retrieved to the application. +/// +public interface ISoftDelete { /// - /// Used to standardize soft deleting entities. - /// Soft-delete entities are not actually deleted, - /// marked as IsDeleted = true in the database, - /// but can not be retrieved to the application. + /// Used to mark an Entity as 'Deleted'. /// - public interface ISoftDelete - { - /// - /// Used to mark an Entity as 'Deleted'. - /// - bool IsDeleted { get; set; } - } + bool IsDeleted { get; set; } } diff --git a/src/Genocs.Core/Domain/Repositories/AutoRepositoryTypesAttribute.cs b/src/Genocs.Core/Domain/Repositories/AutoRepositoryTypesAttribute.cs index 315403fe..5b28b5a3 100644 --- a/src/Genocs.Core/Domain/Repositories/AutoRepositoryTypesAttribute.cs +++ b/src/Genocs.Core/Domain/Repositories/AutoRepositoryTypesAttribute.cs @@ -1,34 +1,31 @@ -namespace Genocs.Core.Domain.Repositories -{ - using System; +namespace Genocs.Core.Domain.Repositories; - /// - /// Used to define auto-repository types for entities. - /// This can be used for DbContext types. - /// - [AttributeUsage(AttributeTargets.Class)] - public class AutoRepositoryTypesAttribute : Attribute - { - public Type RepositoryInterface { get; } +/// +/// Used to define auto-repository types for entities. +/// This can be used for DbContext types. +/// +[AttributeUsage(AttributeTargets.Class)] +public class AutoRepositoryTypesAttribute : Attribute +{ + public Type RepositoryInterface { get; } - public Type RepositoryInterfaceWithPrimaryKey { get; } + public Type RepositoryInterfaceWithPrimaryKey { get; } - public Type RepositoryImplementation { get; } + public Type RepositoryImplementation { get; } - public Type RepositoryImplementationWithPrimaryKey { get; } + public Type RepositoryImplementationWithPrimaryKey { get; } - public bool WithDefaultRepositoryInterfaces { get; set; } + public bool WithDefaultRepositoryInterfaces { get; set; } - public AutoRepositoryTypesAttribute( - Type repositoryInterface, - Type repositoryInterfaceWithPrimaryKey, - Type repositoryImplementation, - Type repositoryImplementationWithPrimaryKey) - { - RepositoryInterface = repositoryInterface; - RepositoryInterfaceWithPrimaryKey = repositoryInterfaceWithPrimaryKey; - RepositoryImplementation = repositoryImplementation; - RepositoryImplementationWithPrimaryKey = repositoryImplementationWithPrimaryKey; - } + public AutoRepositoryTypesAttribute( + Type repositoryInterface, + Type repositoryInterfaceWithPrimaryKey, + Type repositoryImplementation, + Type repositoryImplementationWithPrimaryKey) + { + RepositoryInterface = repositoryInterface; + RepositoryInterfaceWithPrimaryKey = repositoryInterfaceWithPrimaryKey; + RepositoryImplementation = repositoryImplementation; + RepositoryImplementationWithPrimaryKey = repositoryImplementationWithPrimaryKey; } } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Repositories/IRepository.cs b/src/Genocs.Core/Domain/Repositories/IRepository.cs index 018363e5..b7232eca 100644 --- a/src/Genocs.Core/Domain/Repositories/IRepository.cs +++ b/src/Genocs.Core/Domain/Repositories/IRepository.cs @@ -1,5 +1,4 @@ -using Genocs.Common.Types; - +using Genocs.Core.Domain.Entities; namespace Genocs.Core.Domain.Repositories; @@ -7,7 +6,8 @@ namespace Genocs.Core.Domain.Repositories; /// This interface is used to identify a repository so can be used to be registered by convention. /// Implement generic version instead of this one. /// -public interface IRepository where TEntity : IIdentifiable +public interface IRepository + where TEntity : IEntity { } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Repositories/IRepositoryOfTEntityAndTPrimaryKey.cs b/src/Genocs.Core/Domain/Repositories/IRepositoryOfTEntityAndTPrimaryKey.cs index b3026231..59774660 100644 --- a/src/Genocs.Core/Domain/Repositories/IRepositoryOfTEntityAndTPrimaryKey.cs +++ b/src/Genocs.Core/Domain/Repositories/IRepositoryOfTEntityAndTPrimaryKey.cs @@ -1,334 +1,329 @@ -namespace Genocs.Core.Domain.Repositories +using Genocs.Core.Domain.Entities; +using System.Linq.Expressions; + +namespace Genocs.Core.Domain.Repositories; + +/// +/// This interface is implemented by all repositories to ensure implementation of fixed methods. +/// +/// Main Entity type this repository works on. +/// Primary key type of the entity. +public interface IRepositoryOfEntity : IRepository + where TEntity : IEntity { - using Genocs.Common.Types; - using System; - using System.Collections.Generic; - using System.Linq; - using System.Linq.Expressions; - using System.Threading.Tasks; - - /// - /// This interface is implemented by all repositories to ensure implementation of fixed methods. - /// - /// Main Entity type this repository works on - /// Primary key type of the entity - public interface IRepositoryOfEntity : IRepository where TEntity : IIdentifiable - { - #region Select/Get/Query - - /// - /// Used to get a IQueryable that is used to retrieve entities from entire table. - /// - /// IQueryable to be used to select entities from database - IQueryable GetAll(); - - /// - /// Used to get a IQueryable that is used to retrieve entities from entire table. - /// One or more - /// - /// A list of include expressions. - /// IQueryable to be used to select entities from database - IQueryable GetAllIncluding(params Expression>[] propertySelectors); - - /// - /// Used to get all entities. - /// - /// List of all entities - List GetAllList(); - - /// - /// Used to get all entities. - /// - /// List of all entities - Task> GetAllListAsync(); - - /// - /// Used to get all entities based on given . - /// - /// A condition to filter entities - /// List of all entities - List GetAllList(Expression> predicate); - - /// - /// Used to get all entities based on given . - /// - /// A condition to filter entities - /// List of all entities - Task> GetAllListAsync(Expression> predicate); - - /// - /// Used to run a query over entire entities. - /// attribute is not always necessary (as opposite to ) - /// if finishes IQueryable with ToList, FirstOrDefault etc.. - /// - /// Type of return value of this method - /// This method is used to query over entities - /// Query result - T Query(Func, T> queryMethod); - - /// - /// Gets an entity with given primary key. - /// - /// Primary key of the entity to get - /// Entity - TEntity Get(TIdentifiable id); - - /// - /// Gets an entity with given primary key. - /// - /// Primary key of the entity to get - /// Entity - Task GetAsync(TIdentifiable id); - - /// - /// Gets exactly one entity with given predicate. - /// Throws exception if no entity or more than one entity. - /// - /// Entity - TEntity Single(Expression> predicate); - - /// - /// Gets exactly one entity with given predicate. - /// Throws exception if no entity or more than one entity. - /// - /// Entity - Task SingleAsync(Expression> predicate); - - /// - /// Gets an entity with given primary key or null if not found. - /// - /// Primary key of the entity to get - /// Entity or null - TEntity FirstOrDefault(TIdentifiable id); - - /// - /// Gets an entity with given primary key or null if not found. - /// - /// Primary key of the entity to get - /// Entity or null - Task FirstOrDefaultAsync(TIdentifiable id); - - /// - /// Gets an entity with given given predicate or null if not found. - /// - /// Predicate to filter entities - TEntity FirstOrDefault(Expression> predicate); - - /// - /// Gets an entity with given given predicate or null if not found. - /// - /// Predicate to filter entities - Task FirstOrDefaultAsync(Expression> predicate); - - /// - /// Creates an entity with given primary key without database access. - /// - /// Primary key of the entity to load - /// Entity - TEntity Load(TIdentifiable id); - - #endregion - - #region Insert - - /// - /// Inserts a new entity. - /// - /// Inserted entity - TEntity Insert(TEntity entity); - - /// - /// Inserts a new entity. - /// - /// Inserted entity - Task InsertAsync(TEntity entity); - - /// - /// Inserts a new entity and gets it's Id. - /// It may require to save current unit of work - /// to be able to retrieve id. - /// - /// Entity - /// Id of the entity - TIdentifiable InsertAndGetId(TEntity entity); - - /// - /// Inserts a new entity and gets it's Id. - /// It may require to save current unit of work - /// to be able to retrieve id. - /// - /// Entity - /// Id of the entity - Task InsertAndGetIdAsync(TEntity entity); - - /// - /// Inserts or updates given entity depending on Id's value. - /// - /// Entity - TEntity InsertOrUpdate(TEntity entity); - - /// - /// Inserts or updates given entity depending on Id's value. - /// - /// Entity - Task InsertOrUpdateAsync(TEntity entity); - - /// - /// Inserts or updates given entity depending on Id's value. - /// Also returns Id of the entity. - /// It may require to save current unit of work - /// to be able to retrieve id. - /// - /// Entity - /// Id of the entity - TIdentifiable InsertOrUpdateAndGetId(TEntity entity); - - /// - /// Inserts or updates given entity depending on Id's value. - /// Also returns Id of the entity. - /// It may require to save current unit of work - /// to be able to retrieve id. - /// - /// Entity - /// Id of the entity - Task InsertOrUpdateAndGetIdAsync(TEntity entity); - - #endregion - - #region Update - - /// - /// Updates an existing entity. - /// - /// Entity - TEntity Update(TEntity entity); - - /// - /// Updates an existing entity. - /// - /// Entity - Task UpdateAsync(TEntity entity); - - /// - /// Updates an existing entity. - /// - /// Id of the entity - /// Action that can be used to change values of the entity - /// Updated entity - TEntity Update(TIdentifiable id, Action updateAction); - - /// - /// Updates an existing entity. - /// - /// Id of the entity - /// Action that can be used to change values of the entity - /// Updated entity - Task UpdateAsync(TIdentifiable id, Func updateAction); - - #endregion - - #region Delete - - /// - /// Deletes an entity. - /// - /// Entity to be deleted - void Delete(TEntity entity); - - /// - /// Deletes an entity. - /// - /// Entity to be deleted - Task DeleteAsync(TEntity entity); - - /// - /// Deletes an entity by primary key. - /// - /// Primary key of the entity - void Delete(TIdentifiable id); - - /// - /// Deletes an entity by primary key. - /// - /// Primary key of the entity - Task DeleteAsync(TIdentifiable id); - - /// - /// Deletes many entities by function. - /// Notice that: All entities fits to given predicate are retrieved and deleted. - /// This may cause major performance problems if there are too many entities with - /// given predicate. - /// - /// A condition to filter entities - void Delete(Expression> predicate); - - /// - /// Deletes many entities by function. - /// Notice that: All entities fits to given predicate are retrieved and deleted. - /// This may cause major performance problems if there are too many entities with - /// given predicate. - /// - /// A condition to filter entities - Task DeleteAsync(Expression> predicate); - - #endregion - - #region Aggregates - - /// - /// Gets count of all entities in this repository. - /// - /// Count of entities - int Count(); - - /// - /// Gets count of all entities in this repository. - /// - /// Count of entities - Task CountAsync(); - - /// - /// Gets count of all entities in this repository based on given . - /// - /// A method to filter count - /// Count of entities - int Count(Expression> predicate); - - /// - /// Gets count of all entities in this repository based on given . - /// - /// A method to filter count - /// Count of entities - Task CountAsync(Expression> predicate); - - /// - /// Gets count of all entities in this repository (use if expected return value is greater than . - /// - /// Count of entities - long LongCount(); - - /// - /// Gets count of all entities in this repository (use if expected return value is greater than . - /// - /// Count of entities - Task LongCountAsync(); - - /// - /// Gets count of all entities in this repository based on given - /// (use this overload if expected return value is greater than ). - /// - /// A method to filter count - /// Count of entities - long LongCount(Expression> predicate); - - /// - /// Gets count of all entities in this repository based on given - /// (use this overload if expected return value is greater than ). - /// - /// A method to filter count - /// Count of entities - Task LongCountAsync(Expression> predicate); - - #endregion - } + #region Select/Get/Query + + /// + /// Used to get a IQueryable that is used to retrieve entities from entire table. + /// + /// IQueryable to be used to select entities from database. + IQueryable GetAll(); + + /// + /// Used to get a IQueryable that is used to retrieve entities from entire table. + /// One or more. + /// + /// A list of include expressions. + /// IQueryable to be used to select entities from database. + IQueryable GetAllIncluding(params Expression>[] propertySelectors); + + /// + /// Used to get all entities. + /// + /// List of all entities. + List GetAllList(); + + /// + /// Used to get all entities. + /// + /// List of all entities. + Task> GetAllListAsync(); + + /// + /// Used to get all entities based on given . + /// + /// A condition to filter entities. + /// List of all entities. + List GetAllList(Expression> predicate); + + /// + /// Used to get all entities based on given . + /// + /// A condition to filter entities. + /// List of all entities. + Task> GetAllListAsync(Expression> predicate); + + /// + /// Used to run a query over entire entities. + /// if finishes IQueryable with ToList, FirstOrDefault etc.. + /// + /// Type of return value of this method. + /// This method is used to query over entities. + /// Query result. + T Query(Func, T> queryMethod); + + /// + /// Gets an entity with given primary key. + /// + /// Primary key of the entity to get. + /// Entity. + TEntity Get(TKey id); + + /// + /// Gets an entity with given primary key. + /// + /// Primary key of the entity to get. + /// Entity. + Task GetAsync(TKey id); + + /// + /// Gets exactly one entity with given predicate. + /// Throws exception if no entity or more than one entity. + /// + /// Entity. + TEntity Single(Expression> predicate); + + /// + /// Gets exactly one entity with given predicate. + /// Throws exception if no entity or more than one entity. + /// + /// Entity. + Task SingleAsync(Expression> predicate); + + /// + /// Gets an entity with given primary key or null if not found. + /// + /// Primary key of the entity to get. + /// Entity or null. + TEntity? FirstOrDefault(TKey id); + + /// + /// Gets an entity with given primary key or null if not found. + /// + /// Primary key of the entity to get. + /// Entity or null. + Task FirstOrDefaultAsync(TKey id); + + /// + /// Gets an entity with given predicate or null if not found. + /// + /// Predicate to filter entities. + TEntity? FirstOrDefault(Expression> predicate); + + /// + /// Gets an entity with given predicate or null if not found. + /// + /// Predicate to filter entities. + Task FirstOrDefaultAsync(Expression> predicate); + + /// + /// Creates an entity with given primary key without database access. + /// + /// Primary key of the entity to load. + /// Entity. + TEntity? Load(TKey id); + + #endregion + + #region Insert + + /// + /// Inserts a new entity. + /// + /// Inserted entity. + TEntity Insert(TEntity entity); + + /// + /// Inserts a new entity. + /// + /// Inserted entity. + Task InsertAsync(TEntity entity); + + /// + /// Inserts a new entity and gets it's Id. + /// It may require to save current unit of work + /// to be able to retrieve id. + /// + /// Entity. + /// Id of the entity. + TKey InsertAndGetId(TEntity entity); + + /// + /// Inserts a new entity and gets it's Id. + /// It may require to save current unit of work + /// to be able to retrieve id. + /// + /// Entity. + /// Id of the entity. + Task InsertAndGetIdAsync(TEntity entity); + + /// + /// Inserts or updates given entity depending on Id's value. + /// + /// Entity. + TEntity InsertOrUpdate(TEntity entity); + + /// + /// Inserts or updates given entity depending on Id's value. + /// + /// Entity. + Task InsertOrUpdateAsync(TEntity entity); + + /// + /// Inserts or updates given entity depending on Id's value. + /// Also returns Id of the entity. + /// It may require to save current unit of work + /// to be able to retrieve id. + /// + /// Entity. + /// Id of the entity. + TKey InsertOrUpdateAndGetId(TEntity entity); + + /// + /// Inserts or updates given entity depending on Id's value. + /// Also returns Id of the entity. + /// It may require to save current unit of work + /// to be able to retrieve id. + /// + /// Entity. + /// Id of the entity. + Task InsertOrUpdateAndGetIdAsync(TEntity entity); + + #endregion + + #region Update + + /// + /// Updates an existing entity. + /// + /// Entity. + TEntity Update(TEntity entity); + + /// + /// Updates an existing entity. + /// + /// Entity. + Task UpdateAsync(TEntity entity); + + /// + /// Updates an existing entity. + /// + /// Id of the entity. + /// Action that can be used to change values of the entity. + /// Updated entity. + TEntity Update(TKey id, Action updateAction); + + /// + /// Updates an existing entity. + /// + /// Id of the entity. + /// Action that can be used to change values of the entity. + /// Updated entity. + Task UpdateAsync(TKey id, Func updateAction); + + #endregion + + #region Delete + + /// + /// Deletes an entity. + /// + /// Entity to be deleted. + void Delete(TEntity entity); + + /// + /// Deletes an entity. + /// + /// Entity to be deleted. + Task DeleteAsync(TEntity entity); + + /// + /// Deletes an entity by primary key. + /// + /// Primary key of the entity. + void Delete(TKey id); + + /// + /// Deletes an entity by primary key. + /// + /// Primary key of the entity. + Task DeleteAsync(TKey id); + + /// + /// Deletes many entities by function. + /// Notice that: All entities fits to given predicate are retrieved and deleted. + /// This may cause major performance problems if there are too many entities with + /// given predicate. + /// + /// A condition to filter entities. + void Delete(Expression> predicate); + + /// + /// Deletes many entities by function. + /// Notice that: All entities fits to given predicate are retrieved and deleted. + /// This may cause major performance problems if there are too many entities with + /// given predicate. + /// + /// A condition to filter entities. + Task DeleteAsync(Expression> predicate); + + #endregion + + #region Aggregates + + /// + /// Gets count of all entities in this repository. + /// + /// Count of entities. + int Count(); + + /// + /// Gets count of all entities in this repository. + /// + /// Count of entities. + Task CountAsync(); + + /// + /// Gets count of all entities in this repository based on given . + /// + /// A method to filter count. + /// Count of entities. + int Count(Expression> predicate); + + /// + /// Gets count of all entities in this repository based on given . + /// + /// A method to filter count. + /// Count of entities. + Task CountAsync(Expression> predicate); + + /// + /// Gets count of all entities in this repository (use if expected return value is greater than . + /// + /// Count of entities. + long LongCount(); + + /// + /// Gets count of all entities in this repository (use if expected return value is greater than . + /// + /// Count of entities. + Task LongCountAsync(); + + /// + /// Gets count of all entities in this repository based on given + /// (use this overload if expected return value is greater than ). + /// + /// A method to filter count. + /// Count of entities. + long LongCount(Expression> predicate); + + /// + /// Gets count of all entities in this repository based on given + /// (use this overload if expected return value is greater than ). + /// + /// A method to filter count. + /// Count of entities. + Task LongCountAsync(Expression> predicate); + + #endregion } diff --git a/src/Genocs.Core/Domain/Repositories/ISupportsExplicitLoading.cs b/src/Genocs.Core/Domain/Repositories/ISupportsExplicitLoading.cs index c847bbbd..f74dc875 100644 --- a/src/Genocs.Core/Domain/Repositories/ISupportsExplicitLoading.cs +++ b/src/Genocs.Core/Domain/Repositories/ISupportsExplicitLoading.cs @@ -1,25 +1,20 @@ -namespace Genocs.Core.Domain.Repositories -{ - using System; - using System.Collections.Generic; - using System.Linq.Expressions; - using System.Threading; - using System.Threading.Tasks; - using Genocs.Core.Domain.Entities; +using Genocs.Core.Domain.Entities; +using System.Linq.Expressions; + +namespace Genocs.Core.Domain.Repositories; - public interface ISupportsExplicitLoading - where TEntity : class, IEntity - { - Task EnsureCollectionLoadedAsync( - TEntity entity, - Expression>> collectionExpression, - CancellationToken cancellationToken) - where TProperty : class; +public interface ISupportsExplicitLoading + where TEntity : class, IEntity +{ + Task EnsureCollectionLoadedAsync( + TEntity entity, + Expression>> collectionExpression, + CancellationToken cancellationToken) + where TProperty : class; - Task EnsurePropertyLoadedAsync( - TEntity entity, - Expression> propertyExpression, - CancellationToken cancellationToken) - where TProperty : class; - } + Task EnsurePropertyLoadedAsync( + TEntity entity, + Expression> propertyExpression, + CancellationToken cancellationToken) + where TProperty : class; } \ No newline at end of file diff --git a/src/Genocs.Core/Domain/Repositories/RepositoryBase.cs b/src/Genocs.Core/Domain/Repositories/RepositoryBase.cs index f3a05ebf..ce2d7c3b 100644 --- a/src/Genocs.Core/Domain/Repositories/RepositoryBase.cs +++ b/src/Genocs.Core/Domain/Repositories/RepositoryBase.cs @@ -1,278 +1,261 @@ -namespace Genocs.Core.Domain.Repositories +using Genocs.Core.Domain.Entities; +using System.Linq.Expressions; + +// using Genocs.Core.Dependency; +// using Genocs.Core.Domain.Uow; +// using Genocs.Core.MultiTenancy; +// using Genocs.Core.Reflection.Extensions; + +namespace Genocs.Core.Domain.Repositories; + +/// +/// Base class to implement . +/// It implements some methods in most simple way. +/// +/// Type of the Entity for this repository. +/// Type of the Primary Key for this repository. +public abstract class RepositoryBase : IRepository/*, IUnitOfWorkManagerAccessor */ + where TEntity : IEntity { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Linq.Expressions; - using System.Threading.Tasks; - using Genocs.Common.Types; - //using Genocs.Core.Dependency; - using Genocs.Core.Domain.Entities; - //using Genocs.Core.Domain.Uow; - //using Genocs.Core.MultiTenancy; - //using Genocs.Core.Reflection.Extensions; - - /// - /// Base class to implement . - /// It implements some methods in most simple way. + /// The multi tenancy side. /// - /// Type of the Entity for this repository - /// Primary key of the entity - public abstract class RepositoryBase : IRepository/*, IUnitOfWorkManagerAccessor */ - where TEntity : class, IIdentifiable - { - /// - /// The multi tenancy side - /// - //public static MultiTenancySides? MultiTenancySide { get; private set; } + // public static MultiTenancySides? MultiTenancySide { get; private set; } - //public IUnitOfWorkManager UnitOfWorkManager { get; set; } + // public IUnitOfWorkManager UnitOfWorkManager { get; set; } - //public IIocResolver IocResolver { get; set; } + // public IIocResolver IocResolver { get; set; } - static RepositoryBase() + static RepositoryBase() + { + /* + var attr = typeof (TEntity).GetSingleAttributeOfTypeOrBaseTypesOrNull(); + if (attr != null) { - /* - var attr = typeof (TEntity).GetSingleAttributeOfTypeOrBaseTypesOrNull(); - if (attr != null) - { - MultiTenancySide = attr.Side; - } - */ + MultiTenancySide = attr.Side; } + */ + } - public abstract IQueryable GetAll(); + public abstract IQueryable GetAll(); - public virtual IQueryable GetAllIncluding(params Expression>[] propertySelectors) - { - return GetAll(); - } + public virtual IQueryable GetAllIncluding(params Expression>[] propertySelectors) + { + return GetAll(); + } - public virtual List GetAllList() - { - return GetAll().ToList(); - } + public virtual List GetAllList() + { + return GetAll().ToList(); + } - public virtual Task> GetAllListAsync() - { - return Task.FromResult(GetAllList()); - } - - public virtual List GetAllList(Expression> predicate) - { - return GetAll().Where(predicate).ToList(); - } + public virtual Task> GetAllListAsync() + { + return Task.FromResult(GetAllList()); + } - public virtual Task> GetAllListAsync(Expression> predicate) - { - return Task.FromResult(GetAllList(predicate)); - } + public virtual List GetAllList(Expression> predicate) + { + return GetAll().Where(predicate).ToList(); + } - public virtual T Query(Func, T> queryMethod) - { - return queryMethod(GetAll()); - } - - public virtual TEntity Get(TPrimaryKey id) - { - var entity = FirstOrDefault(id); - if (entity == null) - { - throw new EntityNotFoundException(typeof(TEntity), id); - } + public virtual Task> GetAllListAsync(Expression> predicate) + { + return Task.FromResult(GetAllList(predicate)); + } - return entity; - } + public virtual T Query(Func, T> queryMethod) + { + return queryMethod(GetAll()); + } - public virtual async Task GetAsync(TPrimaryKey id) - { - var entity = await FirstOrDefaultAsync(id); - if (entity == null) - { - throw new EntityNotFoundException(typeof(TEntity), id); - } + public virtual TEntity Get(TKey id) + { + var entity = FirstOrDefault(id); + return entity ?? throw new EntityNotFoundException(typeof(TEntity), id); + } - return entity; - } - - public virtual TEntity Single(Expression> predicate) - { - return GetAll().Single(predicate); - } + public virtual async Task GetAsync(TKey id) + { + var entity = await FirstOrDefaultAsync(id); + return entity ?? throw new EntityNotFoundException(typeof(TEntity), id); + } - public virtual Task SingleAsync(Expression> predicate) - { - return Task.FromResult(Single(predicate)); - } - - public virtual TEntity FirstOrDefault(TPrimaryKey id) - { - return GetAll().FirstOrDefault(CreateEqualityExpressionForId(id)); - } + public virtual TEntity Single(Expression> predicate) + { + return GetAll().Single(predicate); + } - public virtual Task FirstOrDefaultAsync(TPrimaryKey id) - { - return Task.FromResult(FirstOrDefault(id)); - } - - public virtual TEntity FirstOrDefault(Expression> predicate) - { - return GetAll().FirstOrDefault(predicate); - } + public virtual Task SingleAsync(Expression> predicate) + { + return Task.FromResult(Single(predicate)); + } - public virtual Task FirstOrDefaultAsync(Expression> predicate) - { - return Task.FromResult(FirstOrDefault(predicate)); - } - - public virtual TEntity Load(TPrimaryKey id) - { - return Get(id); - } + public virtual TEntity? FirstOrDefault(TKey id) + { + return GetAll().FirstOrDefault(CreateEqualityExpressionForId(id)); + } - public abstract TEntity Insert(TEntity entity); - - public virtual Task InsertAsync(TEntity entity) - { - return Task.FromResult(Insert(entity)); - } + public virtual Task FirstOrDefaultAsync(TKey id) + { + return Task.FromResult(FirstOrDefault(id)); + } - public virtual TPrimaryKey InsertAndGetId(TEntity entity) - { - return Insert(entity).Id; - } + public virtual TEntity? FirstOrDefault(Expression> predicate) + { + return GetAll().FirstOrDefault(predicate); + } - public virtual Task InsertAndGetIdAsync(TEntity entity) - { - return Task.FromResult(InsertAndGetId(entity)); - } + public virtual Task FirstOrDefaultAsync(Expression> predicate) + { + return Task.FromResult(FirstOrDefault(predicate)); + } - public virtual TEntity InsertOrUpdate(TEntity entity) - { - return entity.IsTransient() - ? Insert(entity) - : Update(entity); - } - - public virtual async Task InsertOrUpdateAsync(TEntity entity) - { - return entity.IsTransient() - ? await InsertAsync(entity) - : await UpdateAsync(entity); - } + public virtual TEntity Load(TKey id) + { + return Get(id); + } - public virtual TPrimaryKey InsertOrUpdateAndGetId(TEntity entity) - { - return InsertOrUpdate(entity).Id; - } + public abstract TEntity Insert(TEntity entity); - public virtual Task InsertOrUpdateAndGetIdAsync(TEntity entity) - { - return Task.FromResult(InsertOrUpdateAndGetId(entity)); - } + public virtual Task InsertAsync(TEntity entity) + { + return Task.FromResult(Insert(entity)); + } - public abstract TEntity Update(TEntity entity); - - public virtual Task UpdateAsync(TEntity entity) - { - return Task.FromResult(Update(entity)); - } + public virtual TKey InsertAndGetId(TEntity entity) + { + return Insert(entity).Id; + } - public virtual TEntity Update(TPrimaryKey id, Action updateAction) - { - var entity = Get(id); - updateAction(entity); - return entity; - } + public virtual Task InsertAndGetIdAsync(TEntity entity) + { + return Task.FromResult(InsertAndGetId(entity)); + } - public virtual async Task UpdateAsync(TPrimaryKey id, Func updateAction) - { - var entity = await GetAsync(id); - await updateAction(entity); - return entity; - } + public virtual TEntity InsertOrUpdate(TEntity entity) + { + return entity.IsTransient() + ? Insert(entity) + : Update(entity); + } - public abstract void Delete(TEntity entity); - - public virtual Task DeleteAsync(TEntity entity) - { - Delete(entity); - return Task.FromResult(0); - } + public virtual async Task InsertOrUpdateAsync(TEntity entity) + { + return entity.IsTransient() + ? await InsertAsync(entity) + : await UpdateAsync(entity); + } - public abstract void Delete(TPrimaryKey id); - - public virtual Task DeleteAsync(TPrimaryKey id) - { - Delete(id); - return Task.FromResult(0); - } + public virtual TKey InsertOrUpdateAndGetId(TEntity entity) + { + return InsertOrUpdate(entity).Id; + } - public virtual void Delete(Expression> predicate) - { - foreach (var entity in GetAll().Where(predicate).ToList()) - { - Delete(entity); - } - } + public virtual Task InsertOrUpdateAndGetIdAsync(TEntity entity) + { + return Task.FromResult(InsertOrUpdateAndGetId(entity)); + } - public virtual Task DeleteAsync(Expression> predicate) - { - Delete(predicate); - return Task.FromResult(0); - } + public abstract TEntity Update(TEntity entity); - public virtual int Count() - { - return GetAll().Count(); - } + public virtual Task UpdateAsync(TEntity entity) + { + return Task.FromResult(Update(entity)); + } - public virtual Task CountAsync() - { - return Task.FromResult(Count()); - } + public virtual TEntity Update(TKey id, Action updateAction) + { + var entity = Get(id); + updateAction(entity); + return entity; + } - public virtual int Count(Expression> predicate) - { - return GetAll().Where(predicate).Count(); - } + public virtual async Task UpdateAsync(TKey id, Func updateAction) + { + var entity = await GetAsync(id); + await updateAction(entity); + return entity; + } - public virtual Task CountAsync(Expression> predicate) - { - return Task.FromResult(Count(predicate)); - } + public abstract void Delete(TEntity entity); - public virtual long LongCount() - { - return GetAll().LongCount(); - } + public virtual Task DeleteAsync(TEntity entity) + { + Delete(entity); + return Task.FromResult(0); + } - public virtual Task LongCountAsync() - { - return Task.FromResult(LongCount()); - } + public abstract void Delete(TKey id); - public virtual long LongCount(Expression> predicate) - { - return GetAll().Where(predicate).LongCount(); - } + public virtual Task DeleteAsync(TKey id) + { + Delete(id); + return Task.FromResult(0); + } - public virtual Task LongCountAsync(Expression> predicate) + public virtual void Delete(Expression> predicate) + { + foreach (var entity in GetAll().Where(predicate).ToList()) { - return Task.FromResult(LongCount(predicate)); + Delete(entity); } + } - protected virtual Expression> CreateEqualityExpressionForId(TPrimaryKey id) - { - var lambdaParam = Expression.Parameter(typeof(TEntity)); + public virtual Task DeleteAsync(Expression> predicate) + { + Delete(predicate); + return Task.FromResult(0); + } + + public virtual int Count() + { + return GetAll().Count(); + } - var lambdaBody = Expression.Equal( - Expression.PropertyOrField(lambdaParam, "Id"), - Expression.Constant(id, typeof(TPrimaryKey)) - ); + public virtual Task CountAsync() + { + return Task.FromResult(Count()); + } - return Expression.Lambda>(lambdaBody, lambdaParam); - } + public virtual int Count(Expression> predicate) + { + return GetAll().Where(predicate).Count(); + } + + public virtual Task CountAsync(Expression> predicate) + { + return Task.FromResult(Count(predicate)); + } + + public virtual long LongCount() + { + return GetAll().LongCount(); + } + + public virtual Task LongCountAsync() + { + return Task.FromResult(LongCount()); + } + + public virtual long LongCount(Expression> predicate) + { + return GetAll().Where(predicate).LongCount(); + } + + public virtual Task LongCountAsync(Expression> predicate) + { + return Task.FromResult(LongCount(predicate)); + } + + protected virtual Expression> CreateEqualityExpressionForId(TKey id) + { + var lambdaParam = Expression.Parameter(typeof(TEntity)); + + var lambdaBody = Expression.Equal( + Expression.PropertyOrField(lambdaParam, "Id"), + Expression.Constant(id, typeof(TKey))); + + return Expression.Lambda>(lambdaBody, lambdaParam); } } diff --git a/src/Genocs.Core/Domain/Repositories/TableMappingAttribute.cs b/src/Genocs.Core/Domain/Repositories/TableMappingAttribute.cs index a971d42c..ee57d19b 100644 --- a/src/Genocs.Core/Domain/Repositories/TableMappingAttribute.cs +++ b/src/Genocs.Core/Domain/Repositories/TableMappingAttribute.cs @@ -1,19 +1,18 @@ -namespace Genocs.Core.Domain.Repositories +namespace Genocs.Core.Domain.Repositories; + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] +public class TableMappingAttribute : Attribute { - [System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct)] - public class TableMappingAttribute : System.Attribute - { - /// - /// The Collection/Table name - /// - public string Name { get; set; } + /// + /// The Collection/Table name. + /// + public string Name { get; set; } - public double version; + public readonly double Version; - public TableMappingAttribute(string tableName) - { - Name = tableName; - version = 1.0; - } + public TableMappingAttribute(string tableName) + { + Name = tableName; + Version = 1.0; } } diff --git a/src/Genocs.Core/Exceptions/GenocsException.cs b/src/Genocs.Core/Exceptions/GenocsException.cs index 1a687054..0c233be5 100644 --- a/src/Genocs.Core/Exceptions/GenocsException.cs +++ b/src/Genocs.Core/Exceptions/GenocsException.cs @@ -1,17 +1,6 @@ -namespace Genocs.Core.Exceptions; +using System.Runtime.Serialization; -using System; - -/* Unmerged change from project 'Genocs.Core (netstandard2.1)' -Before: - using System.Runtime.Serialization; -After: - using System.Runtime.Serialization; - using Genocs; - using Genocs.Core; - using Genocs.Core.Exceptions; -*/ -using System.Runtime.Serialization; +namespace Genocs.Core.Exceptions; /// /// Base exception type for those are thrown by Genocs system for Genocs specific exceptions. @@ -39,7 +28,7 @@ public GenocsException(SerializationInfo serializationInfo, StreamingContext con /// /// Creates a new object. /// - /// Exception message + /// Exception message. public GenocsException(string message) : base(message) { @@ -49,9 +38,9 @@ public GenocsException(string message) /// /// Creates a new object. /// - /// Exception message - /// Inner exception - public GenocsException(string message, Exception innerException) + /// Exception message. + /// Inner exception. + public GenocsException(string message, Exception? innerException) : base(message, innerException) { diff --git a/src/Genocs.Core/Extensions/Encryption.cs b/src/Genocs.Core/Extensions/Encryption.cs index 4452e8c9..86e7b477 100644 --- a/src/Genocs.Core/Extensions/Encryption.cs +++ b/src/Genocs.Core/Extensions/Encryption.cs @@ -3,8 +3,7 @@ namespace Genocs.Core.Extensions; - -//https://github.com/dotnet/corefx/issues/23686 +// https://github.com/dotnet/corefx/issues/23686 public static class Encryption { @@ -21,14 +20,14 @@ public static void FromXmlFile(this RSA rsa, string xmlFilePath) { switch (node.Name) { - case "Modulus": parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "Exponent": parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "P": parameters.P = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "Q": parameters.Q = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "DP": parameters.DP = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "DQ": parameters.DQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "InverseQ": parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "D": parameters.D = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; + case "Modulus": parameters.Modulus = string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText); break; + case "Exponent": parameters.Exponent = string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText); break; + case "P": parameters.P = string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText); break; + case "Q": parameters.Q = string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText); break; + case "DP": parameters.DP = string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText); break; + case "DQ": parameters.DQ = string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText); break; + case "InverseQ": parameters.InverseQ = string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText); break; + case "D": parameters.D = string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText); break; } } } @@ -53,7 +52,6 @@ public static void ToXmlFile(this RSA rsa, bool includePrivateParameters, string parameters.DP != null ? Convert.ToBase64String(parameters.DP) : null, parameters.DQ != null ? Convert.ToBase64String(parameters.DQ) : null, parameters.InverseQ != null ? Convert.ToBase64String(parameters.InverseQ) : null, - parameters.D != null ? Convert.ToBase64String(parameters.D) : null) - ); + parameters.D != null ? Convert.ToBase64String(parameters.D) : null)); } } diff --git a/src/Genocs.Core/Extensions/ExceptionExtensions.cs b/src/Genocs.Core/Extensions/ExceptionExtensions.cs index 46eca4e0..e1c267db 100644 --- a/src/Genocs.Core/Extensions/ExceptionExtensions.cs +++ b/src/Genocs.Core/Extensions/ExceptionExtensions.cs @@ -11,7 +11,7 @@ public static class ExceptionExtensions /// Uses method to re-throws exception /// while preserving stack trace. /// - /// Exception to be re-thrown + /// Exception to be re-thrown. public static void ReThrow(this Exception exception) { ExceptionDispatchInfo.Capture(exception).Throw(); diff --git a/src/Genocs.Core/Extensions/ObjectExtensions.cs b/src/Genocs.Core/Extensions/ObjectExtensions.cs index 5b31ea58..c6656ba7 100644 --- a/src/Genocs.Core/Extensions/ObjectExtensions.cs +++ b/src/Genocs.Core/Extensions/ObjectExtensions.cs @@ -1,53 +1,50 @@ -namespace Genocs.Core.Extensions +using System.ComponentModel; +using System.Globalization; + +namespace Genocs.Core.Extensions; + +/// +/// Extension methods for all objects. +/// +public static class ObjectExtensions { - using System; - using System.Globalization; - using System.Linq; - using System.ComponentModel; + /// + /// Used to simplify and beautify casting an object to a type. + /// + /// Type to be casted. + /// Object to cast. + /// Casted object. + public static T As(this object obj) + where T : class + { + return (T)obj; + } /// - /// Extension methods for all objects. + /// Converts given object to a value type using method. /// - public static class ObjectExtensions + /// Object to be converted. + /// Type of the target object. + /// Converted object. + public static T To(this object obj) + where T : struct { - /// - /// Used to simplify and beautify casting an object to a type. - /// - /// Type to be casted - /// Object to cast - /// Casted object - public static T As(this object obj) - where T : class + if (typeof(T) == typeof(Guid)) { - return (T)obj; + return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromInvariantString(obj.ToString()); } - /// - /// Converts given object to a value type using method. - /// - /// Object to be converted - /// Type of the target object - /// Converted object - public static T To(this object obj) - where T : struct - { - if (typeof(T) == typeof(Guid)) - { - return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromInvariantString(obj.ToString()); - } - - return (T)Convert.ChangeType(obj, typeof(T), CultureInfo.InvariantCulture); - } + return (T)Convert.ChangeType(obj, typeof(T), CultureInfo.InvariantCulture); + } - /// - /// Check if an item is in a list. - /// - /// Item to check - /// List of items - /// Type of the items - public static bool IsIn(this T item, params T[] list) - { - return list.Contains(item); - } + /// + /// Check if an item is in a list. + /// + /// Item to check. + /// List of items. + /// Type of the items. + public static bool IsIn(this T item, params T[] list) + { + return list.Contains(item); } } diff --git a/src/Genocs.Core/Extensions/StringExtensions.cs b/src/Genocs.Core/Extensions/StringExtensions.cs index d87882ae..5841f1c2 100644 --- a/src/Genocs.Core/Extensions/StringExtensions.cs +++ b/src/Genocs.Core/Extensions/StringExtensions.cs @@ -1,526 +1,513 @@ -namespace Genocs.Core.Extensions +using Genocs.Core.Collections.Extensions; +using System.Globalization; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; + +namespace Genocs.Core.Extensions; + +/// +/// Extension methods for String class. +/// +public static class StringExtensions { - using System; - using System.Globalization; - using System.Linq; - using System.Security.Cryptography; - using System.Text; - using System.Text.RegularExpressions; - using Genocs.Core.Collections.Extensions; + /// + /// Adds a char to end of given string if it does not ends with the char. + /// + public static string? EnsureEndsWith(this string? str, char c) + { + return EnsureEndsWith(str, c, StringComparison.Ordinal); + } /// - /// Extension methods for String class. + /// Adds a char to end of given string if it does not ends with the char. /// - public static class StringExtensions + public static string? EnsureEndsWith(this string? str, char c, StringComparison comparisonType) { - /// - /// Adds a char to end of given string if it does not ends with the char. - /// - public static string EnsureEndsWith(this string str, char c) + if (string.IsNullOrWhiteSpace(str)) { - return EnsureEndsWith(str, c, StringComparison.Ordinal); + return str; } - /// - /// Adds a char to end of given string if it does not ends with the char. - /// - public static string EnsureEndsWith(this string str, char c, StringComparison comparisonType) + if (str.EndsWith(c.ToString(), comparisonType)) { - if (str == null) - { - throw new ArgumentNullException(nameof(str)); - } - - if (str.EndsWith(c.ToString(), comparisonType)) - { - return str; - } - - return str + c; + return str; } - /// - /// Adds a char to end of given string if it does not ends with the char. - /// - public static string EnsureEndsWith(this string str, char c, bool ignoreCase, CultureInfo culture) - { - if (str == null) - { - throw new ArgumentNullException(nameof(str)); - } - - if (str.EndsWith(c.ToString(culture), ignoreCase, culture)) - { - return str; - } + return str + c; + } - return str + c; + /// + /// Adds a char to end of given string if it does not ends with the char. + /// + public static string? EnsureEndsWith(this string? str, char c, bool ignoreCase, CultureInfo culture) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; } - /// - /// Adds a char to beginning of given string if it does not starts with the char. - /// - public static string EnsureStartsWith(this string str, char c) + if (str.EndsWith(c.ToString(culture), ignoreCase, culture)) { - return EnsureStartsWith(str, c, StringComparison.Ordinal); + return str; } - /// - /// Adds a char to beginning of given string if it does not starts with the char. - /// - public static string EnsureStartsWith(this string str, char c, StringComparison comparisonType) - { - if (str == null) - { - throw new ArgumentNullException(nameof(str)); - } + return str + c; + } - if (str.StartsWith(c.ToString(), comparisonType)) - { - return str; - } + /// + /// Adds a char to beginning of given string if it does not starts with the char. + /// + public static string? EnsureStartsWith(this string? str, char c) + { + return EnsureStartsWith(str, c, StringComparison.Ordinal); + } - return c + str; + /// + /// Adds a char to beginning of given string if it does not starts with the char. + /// + public static string? EnsureStartsWith(this string? str, char c, StringComparison comparisonType) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; } - /// - /// Adds a char to beginning of given string if it does not starts with the char. - /// - public static string EnsureStartsWith(this string str, char c, bool ignoreCase, CultureInfo culture) + if (str.StartsWith(c.ToString(), comparisonType)) { - if (str == null) - { - throw new ArgumentNullException("str"); - } + return str; + } - if (str.StartsWith(c.ToString(culture), ignoreCase, culture)) - { - return str; - } + return c + str; + } - return c + str; + /// + /// Adds a char to beginning of given string if it does not starts with the char. + /// + public static string? EnsureStartsWith(this string? str, char c, bool ignoreCase, CultureInfo culture) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; } - /// - /// Gets a substring of a string from beginning of the string. - /// - /// Thrown if is null - /// Thrown if is bigger that string's length - public static string Left(this string str, int len) + if (str.StartsWith(c.ToString(culture), ignoreCase, culture)) { - if (str == null) - { - throw new ArgumentNullException("str"); - } + return str; + } - if (str.Length < len) - { - throw new ArgumentException("len argument can not be bigger than given string's length!"); - } + return c + str; + } - return str.Substring(0, len); + /// + /// Gets a substring of a string from beginning of the string. + /// + /// Thrown if is null. + /// Thrown if is bigger that string's length. + public static string? Left(this string? str, int len) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; } - /// - /// Converts line endings in the string to . - /// - public static string NormalizeLineEndings(this string str) + if (str.Length < len) { - return str.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", Environment.NewLine); + throw new ArgumentException("len argument can not be bigger than given string's length!"); } - /// - /// Gets index of nth occurence of a char in a string. - /// - /// source string to be searched - /// Char to search in - /// Count of the occurence - public static int NthIndexOf(this string str, char c, int n) + return str[..len]; + } + + /// + /// Converts line endings in the string to . + /// + public static string? NormalizeLineEndings(this string? str) + { + if (string.IsNullOrWhiteSpace(str)) { - if (str == null) - { - throw new ArgumentNullException(nameof(str)); - } + return str; + } - var count = 0; - for (var i = 0; i < str.Length; i++) - { - if (str[i] != c) - { - continue; - } - - if ((++count) == n) - { - return i; - } - } + return str.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", Environment.NewLine); + } - return -1; - } + /// + /// Gets index of nth occurrence of a char in a string. + /// + /// source string to be searched. + /// Char to search in . + /// Count of the occurrence. + public static int NthIndexOf(this string str, char c, int n) + { + if (string.IsNullOrEmpty(str)) throw new ArgumentException("String can not be null or empty!", nameof(str)); - /// - /// Removes first occurrence of the given postfixes from end of the given string. - /// Ordering is important. If one of the postFixes is matched, others will not be tested. - /// - /// The string. - /// one or more postfix. - /// Modified string or the same string if it has not any of given postfixes - public static string RemovePostFix(this string str, params string[] postFixes) + int count = 0; + for (int i = 0; i < str.Length; i++) { - if (str == null) - { - return null; - } - - if (str == string.Empty) + if (str[i] != c) { - return string.Empty; + continue; } - if (postFixes.IsNullOrEmpty()) + if ((++count) == n) { - return str; + return i; } + } - foreach (var postFix in postFixes) - { - if (str.EndsWith(postFix)) - { - return str.Left(str.Length - postFix.Length); - } - } + return -1; + } + /// + /// Removes first occurrence of the given postfixes from end of the given string. + /// Ordering is important. If one of the postFixes is matched, others will not be tested. + /// + /// The string. + /// one or more postfix. + /// Modified string or the same string if it has not any of given postfixes. + public static string? RemovePostFix(this string? str, params string[] postFixes) + { + if (string.IsNullOrWhiteSpace(str)) + { return str; } - /// - /// Removes first occurrence of the given prefixes from beginning of the given string. - /// Ordering is important. If one of the preFixes is matched, others will not be tested. - /// - /// The string. - /// one or more prefix. - /// Modified string or the same string if it has not any of given prefixes - public static string RemovePreFix(this string str, params string[] preFixes) + if (postFixes.IsNullOrEmpty()) { - if (str == null) - { - return null; - } + return str; + } - if (str == string.Empty) + foreach (string postFix in postFixes) + { + if (str.EndsWith(postFix)) { - return string.Empty; + return str.Left(str.Length - postFix.Length); } + } - if (preFixes.IsNullOrEmpty()) - { - return str; - } + return str; + } - foreach (var preFix in preFixes) - { - if (str.StartsWith(preFix)) - { - return str.Right(str.Length - preFix.Length); - } - } + /// + /// Removes first occurrence of the given prefixes from beginning of the given string. + /// Ordering is important. If one of the preFixes is matched, others will not be tested. + /// + /// The string. + /// one or more prefix. + /// Modified string or the same string if it has not any of given prefixes. + public static string? RemovePreFix(this string? str, params string[] preFixes) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; + } + if (preFixes.IsNullOrEmpty()) + { return str; } - /// - /// Gets a substring of a string from end of the string. - /// - /// Thrown if is null - /// Thrown if is bigger that string's length - public static string Right(this string str, int len) + foreach (string preFix in preFixes) { - if (str == null) + if (str.StartsWith(preFix)) { - throw new ArgumentNullException("str"); + return str.Right(str.Length - preFix.Length); } + } - if (str.Length < len) - { - throw new ArgumentException("len argument can not be bigger than given string's length!"); - } + return str; + } - return str.Substring(str.Length - len, len); + /// + /// Gets a substring of a string from end of the string. + /// + /// Thrown if is null. + /// Thrown if is bigger that string's length. + public static string? Right(this string? str, int len) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; } - /// - /// Uses string.Split method to split given string by given separator. - /// - public static string[] Split(this string str, string separator) + if (str.Length < len) { - return str.Split(new[] { separator }, StringSplitOptions.None); + throw new ArgumentException("len argument can not be bigger than given string's length!"); } - /// - /// Uses string.Split method to split given string by given separator. - /// - public static string[] Split(this string str, string separator, StringSplitOptions options) + return str.Substring(str.Length - len, len); + } + + /// + /// Uses string.Split method to split given string by given separator. + /// + public static string[] Split(this string str, string separator) + { + return str.Split(new[] { separator }, StringSplitOptions.None); + } + + /// + /// Uses string.Split method to split given string by given separator. + /// + public static string[] Split(this string str, string separator, StringSplitOptions options) + { + return str.Split(new[] { separator }, options); + } + + /// + /// Uses string.Split method to split given string by . + /// + public static string[] SplitToLines(this string str) + { + return str.Split(Environment.NewLine); + } + + /// + /// Uses string.Split method to split given string by . + /// + public static string[] SplitToLines(this string str, StringSplitOptions options) + { + return str.Split(Environment.NewLine, options); + } + + /// + /// Converts PascalCase string to camelCase string. + /// + /// String to convert. + /// Invariant culture. + /// camelCase of the string. + public static string? ToCamelCase(this string? str, bool invariantCulture = true) + { + if (string.IsNullOrWhiteSpace(str)) { - return str.Split(new[] { separator }, options); + return str; } - /// - /// Uses string.Split method to split given string by . - /// - public static string[] SplitToLines(this string str) + if (str.Length == 1) { - return str.Split(Environment.NewLine); + return invariantCulture ? str.ToLowerInvariant() : str.ToLower(); } - /// - /// Uses string.Split method to split given string by . - /// - public static string[] SplitToLines(this string str, StringSplitOptions options) + return (invariantCulture ? char.ToLowerInvariant(str[0]) : char.ToLower(str[0])) + str.Substring(1); + } + + /// + /// Converts PascalCase string to camelCase string in specified culture. + /// + /// String to convert. + /// An object that supplies culture-specific casing rules. + /// camelCase of the string. + public static string? ToCamelCase(this string? str, CultureInfo culture) + { + if (string.IsNullOrWhiteSpace(str)) { - return str.Split(Environment.NewLine, options); + return str; } - /// - /// Converts PascalCase string to camelCase string. - /// - /// String to convert - /// Invariant culture - /// camelCase of the string - public static string ToCamelCase(this string str, bool invariantCulture = true) + if (str.Length == 1) { - if (string.IsNullOrWhiteSpace(str)) - { - return str; - } + return str.ToLower(culture); + } - if (str.Length == 1) - { - return invariantCulture ? str.ToLowerInvariant() : str.ToLower(); - } + return char.ToLower(str[0], culture) + str[1..]; + } - return (invariantCulture ? char.ToLowerInvariant(str[0]) : char.ToLower(str[0])) + str.Substring(1); + /// + /// Converts given PascalCase/camelCase string to sentence (by splitting words by space). + /// Example: "ThisIsSampleSentence" is converted to "This is a sample sentence". + /// + /// String to convert. + /// Invariant culture. + public static string? ToSentenceCase(this string? str, bool invariantCulture = false) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; } - /// - /// Converts PascalCase string to camelCase string in specified culture. - /// - /// String to convert - /// An object that supplies culture-specific casing rules - /// camelCase of the string - public static string ToCamelCase(this string str, CultureInfo culture) + return Regex.Replace( + str, + "[a-z][A-Z]", + m => m.Value[0] + " " + (invariantCulture ? char.ToLowerInvariant(m.Value[1]) : char.ToLower(m.Value[1])) + ); + } + + /// + /// Converts given PascalCase/camelCase string to sentence (by splitting words by space). + /// Example: "ThisIsSampleSentence" is converted to "This is a sample sentence". + /// + /// String to convert. + /// An object that supplies culture-specific casing rules. + public static string? ToSentenceCase(this string? str, CultureInfo culture) + { + if (string.IsNullOrWhiteSpace(str)) { - if (string.IsNullOrWhiteSpace(str)) - { - return str; - } + return str; + } - if (str.Length == 1) - { - return str.ToLower(culture); - } + return Regex.Replace(str, "[a-z][A-Z]", m => m.Value[0] + " " + char.ToLower(m.Value[1], culture)); + } - return char.ToLower(str[0], culture) + str.Substring(1); - } + /// + /// Converts string to enum value. + /// + /// Type of enum. + /// String value to convert. + /// Returns enum object. + public static T ToEnum(this string? str) + where T : struct + { + if (string.IsNullOrEmpty(str)) throw new ArgumentException("String can not be null or empty!", nameof(str)); - /// - /// Converts given PascalCase/camelCase string to sentence (by splitting words by space). - /// Example: "ThisIsSampleSentence" is converted to "This is a sample sentence". - /// - /// String to convert. - /// Invariant culture - public static string ToSentenceCase(this string str, bool invariantCulture = false) - { - if (string.IsNullOrWhiteSpace(str)) - { - return str; - } + return (T)Enum.Parse(typeof(T), str); + } - return Regex.Replace( - str, - "[a-z][A-Z]", - m => m.Value[0] + " " + (invariantCulture ? char.ToLowerInvariant(m.Value[1]) : char.ToLower(m.Value[1])) - ); - } + /// + /// Converts string to enum value. + /// + /// Type of enum. + /// String value to convert. + /// Ignore case. + /// Returns enum object. + public static T ToEnum(this string? str, bool ignoreCase) + where T : struct + { + if (string.IsNullOrEmpty(str)) throw new ArgumentException("String can not be null or empty!", nameof(str)); - /// - /// Converts given PascalCase/camelCase string to sentence (by splitting words by space). - /// Example: "ThisIsSampleSentence" is converted to "This is a sample sentence". - /// - /// String to convert. - /// An object that supplies culture-specific casing rules. - public static string ToSentenceCase(this string str, CultureInfo culture) - { - if (string.IsNullOrWhiteSpace(str)) - { - return str; - } + return (T)Enum.Parse(typeof(T), str, ignoreCase); + } - return Regex.Replace(str, "[a-z][A-Z]", m => m.Value[0] + " " + char.ToLower(m.Value[1], culture)); - } + public static string ToMd5(this string str) + { + using var md5 = MD5.Create(); + byte[] inputBytes = Encoding.UTF8.GetBytes(str); + byte[] hashBytes = md5.ComputeHash(inputBytes); - /// - /// Converts string to enum value. - /// - /// Type of enum - /// String value to convert - /// Returns enum object - public static T ToEnum(this string value) - where T : struct + var sb = new StringBuilder(); + foreach (byte hashByte in hashBytes) { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - return (T)Enum.Parse(typeof(T), value); + sb.Append(hashByte.ToString("X2")); } - /// - /// Converts string to enum value. - /// - /// Type of enum - /// String value to convert - /// Ignore case - /// Returns enum object - public static T ToEnum(this string value, bool ignoreCase) - where T : struct - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } + return sb.ToString(); + } - return (T)Enum.Parse(typeof(T), value, ignoreCase); + /// + /// Converts camelCase string to PascalCase string. + /// + /// String to convert. + /// Invariant culture. + /// PascalCase of the string. + public static string? ToPascalCase(this string? str, bool invariantCulture = true) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; } - public static string ToMd5(this string str) + if (str.Length == 1) { - using (var md5 = MD5.Create()) - { - var inputBytes = Encoding.UTF8.GetBytes(str); - var hashBytes = md5.ComputeHash(inputBytes); + return invariantCulture ? str.ToUpperInvariant() : str.ToUpper(); + } - var sb = new StringBuilder(); - foreach (var hashByte in hashBytes) - { - sb.Append(hashByte.ToString("X2")); - } + return (invariantCulture ? char.ToUpperInvariant(str[0]) : char.ToUpper(str[0])) + str.Substring(1); + } - return sb.ToString(); - } + /// + /// Converts camelCase string to PascalCase string in specified culture. + /// + /// String to convert. + /// An object that supplies culture-specific casing rules. + /// PascalCase of the string. + public static string? ToPascalCase(this string? str, CultureInfo culture) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; } - /// - /// Converts camelCase string to PascalCase string. - /// - /// String to convert - /// Invariant culture - /// PascalCase of the string - public static string ToPascalCase(this string str, bool invariantCulture = true) + if (str.Length == 1) { - if (string.IsNullOrWhiteSpace(str)) - { - return str; - } + return str.ToUpper(culture); + } - if (str.Length == 1) - { - return invariantCulture ? str.ToUpperInvariant(): str.ToUpper(); - } + return char.ToUpper(str[0], culture) + str.Substring(1); + } - return (invariantCulture ? char.ToUpperInvariant(str[0]) : char.ToUpper(str[0])) + str.Substring(1); + /// + /// Gets a substring of a string from beginning of the string if it exceeds maximum length. + /// + /// Truncated string if it is too long, otherwise the entire string. + public static string? Truncate(this string? str, int maxLength) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; } - /// - /// Converts camelCase string to PascalCase string in specified culture. - /// - /// String to convert - /// An object that supplies culture-specific casing rules - /// PascalCase of the string - public static string ToPascalCase(this string str, CultureInfo culture) + if (str.Length <= maxLength) { - if (string.IsNullOrWhiteSpace(str)) - { - return str; - } - - if (str.Length == 1) - { - return str.ToUpper(culture); - } - - return char.ToUpper(str[0], culture) + str.Substring(1); + return str; } - /// - /// Gets a substring of a string from beginning of the string if it exceeds maximum length. - /// - /// Thrown if is null - public static string Truncate(this string str, int maxLength) - { - if (str == null) - { - return null; - } + return str.Left(maxLength); + } - if (str.Length <= maxLength) - { - return str; - } + /// + /// Gets a substring of a string from beginning of the string if it exceeds maximum length. + /// It adds a "..." postfix to end of the string if it's truncated. + /// Returning string can not be longer than maxLength. + /// + /// Thrown if is null. + public static string? TruncateWithPostfix(this string? str, int maxLength) + { + return TruncateWithPostfix(str, maxLength, "..."); + } - return str.Left(maxLength); + /// + /// Gets a substring of a string from beginning of the string if it exceeds maximum length. + /// It adds given to end of the string if it's truncated. + /// Returning string can not be longer than maxLength. + /// + public static string? TruncateWithPostfix(this string? str, int maxLength, string postfix) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; } - /// - /// Gets a substring of a string from beginning of the string if it exceeds maximum length. - /// It adds a "..." postfix to end of the string if it's truncated. - /// Returning string can not be longer than maxLength. - /// - /// Thrown if is null - public static string TruncateWithPostfix(this string str, int maxLength) + if (maxLength == 0) { - return TruncateWithPostfix(str, maxLength, "..."); + return string.Empty; } - /// - /// Gets a substring of a string from beginning of the string if it exceeds maximum length. - /// It adds given to end of the string if it's truncated. - /// Returning string can not be longer than maxLength. - /// - /// Thrown if is null - public static string TruncateWithPostfix(this string str, int maxLength, string postfix) + if (str.Length <= maxLength) { - if (str == null) - { - return null; - } - - if (str == string.Empty || maxLength == 0) - { - return string.Empty; - } + return str; + } - if (str.Length <= maxLength) - { - return str; - } + if (maxLength <= postfix.Length) + { + return postfix.Left(maxLength); + } - if (maxLength <= postfix.Length) - { - return postfix.Left(maxLength); - } + return str.Left(maxLength - postfix.Length) + postfix; + } - return str.Left(maxLength - postfix.Length) + postfix; + /// + /// Helper method. It's used to convert a string to snake case. + /// + /// The input string. + /// The output result. + public static string? Underscore(this string? str) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; } - /// - /// Helper method - /// - /// - /// - public static string Underscore(this string value) - => string.Concat(value.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x : x.ToString())) - .ToLowerInvariant(); + return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x : x.ToString())) + .ToLowerInvariant(); } } \ No newline at end of file diff --git a/src/Genocs.Core/Genocs.Core.csproj b/src/Genocs.Core/Genocs.Core.csproj index 5a627bd2..32e880bc 100644 --- a/src/Genocs.Core/Genocs.Core.csproj +++ b/src/Genocs.Core/Genocs.Core.csproj @@ -1,79 +1,34 @@  - - netstandard2.1;net6.0;net7.0 - enable - enable - Genocs.Core - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The base library useful to build .NET Core projects. - The base library useful to build .NET Core projects. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - - - - - True - \ - - - True - \ - - - True - \ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + net8.0;net7.0;net6.0 + Genocs.Core + Genocs.Core + Genocs.Core + The Genocs Library - Core components. + The core components to build .NET Core projects along with Genocs Library. + true + 5.0.0 + Nocco Giovanni Emanuele + microservice microservices solid solid-principles genocs + README_NUGET.md + Updated to NET8 + True + latest + + + + + + + + + + + + + + + diff --git a/src/Genocs.Core/README.md b/src/Genocs.Core/README.md deleted file mode 100644 index d99be2c9..00000000 --- a/src/Genocs.Core/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# .NET Core library - -This package contains a set of base functionalities designed by Genocs. -The library is built to be used on .NET standard 2.1 and NET6 or NET7 as well. - -## Description - -Core NuGet package contains general purpose functionalities to be used on DDD services. - - -## Support - -Please check the GitHub repository getting more info. - - -## Release notes - -### [2023-03-12] 5.0.0-preview.5.0 -- Implemented MongoDB repository interfaces - -### [2023-03-12] 5.0.0 -- New Architecture - -### [2023-03-12] 3.1.0 -- Added Builders - -### [2023-03-12] 3.0.0 -- Refactory to implement CQRS pattern - -### [2023-03-04] 2.4.1 -- Updated System.Text.Json diff --git a/src/Genocs.Core/README_NUGET.md b/src/Genocs.Core/README_NUGET.md new file mode 100644 index 00000000..7aebc35c --- /dev/null +++ b/src/Genocs.Core/README_NUGET.md @@ -0,0 +1,63 @@ +# .NET Core library + +This package contains a set of base functionalities designed by Genocs. +The library is built to be used on .NET standard 2.1 and NET6, NET7 NET8. + +## Description + +Core NuGet package contains general purpose functionalities to be used on DDD services. + + +## Support + +Please check the GitHub repository getting more info. + + +## Release notes + +### [2024-06-22] 6.0.0-preview.1.0 +- Upgrade toward Version 6.0.0 +- + +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 +- Implemented MongoDB repository interfaces + +### [2023-03-12] 5.0.0 +- New Architecture + +### [2023-03-12] 3.1.0 +- Added Builders + +### [2023-03-12] 3.0.0 +- Refactory to implement CQRS pattern + +### [2023-03-04] 2.4.1 +- Updated System.Text.Json diff --git a/src/Genocs.Discovery.Consul/Builders/ConsulOptionsBuilder.cs b/src/Genocs.Discovery.Consul/Builders/ConsulOptionsBuilder.cs index 8c7e3685..f5b29178 100644 --- a/src/Genocs.Discovery.Consul/Builders/ConsulOptionsBuilder.cs +++ b/src/Genocs.Discovery.Consul/Builders/ConsulOptionsBuilder.cs @@ -1,10 +1,10 @@ -using Genocs.Discovery.Consul.Options; +using Genocs.Discovery.Consul.Configurations; namespace Genocs.Discovery.Consul.Builders; internal sealed class ConsulOptionsBuilder : IConsulOptionsBuilder { - private readonly ConsulSettings _options = new(); + private readonly ConsulOptions _options = new(); public IConsulOptionsBuilder Enable(bool enabled) { @@ -60,5 +60,5 @@ public IConsulOptionsBuilder WithSkippingLocalhostDockerDnsReplace(bool skipLoca return this; } - public ConsulSettings Build() => _options; + public ConsulOptions Build() => _options; } \ No newline at end of file diff --git a/src/Genocs.Discovery.Consul/Configurations/ConsulOptions.cs b/src/Genocs.Discovery.Consul/Configurations/ConsulOptions.cs new file mode 100644 index 00000000..f370ef4a --- /dev/null +++ b/src/Genocs.Discovery.Consul/Configurations/ConsulOptions.cs @@ -0,0 +1,33 @@ +namespace Genocs.Discovery.Consul.Configurations; + +public class ConsulOptions +{ + /// + /// Default section name. + /// + public const string Position = "consul"; + + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + public string? Url { get; set; } + public string? Service { get; set; } + public string? Address { get; set; } + public int Port { get; set; } + public bool PingEnabled { get; set; } + public string? PingEndpoint { get; set; } + public string? PingInterval { get; set; } + public string? RemoveAfterInterval { get; set; } + public List? Tags { get; set; } + public IDictionary? Meta { get; set; } + public bool EnableTagOverride { get; set; } + public bool SkipLocalhostDockerDnsReplace { get; set; } + public ConnectOptions? Connect { get; set; } + + public class ConnectOptions + { + public bool Enabled { get; set; } + } +} \ No newline at end of file diff --git a/src/Genocs.Discovery.Consul/Options/IConsulOptionsBuilder.cs b/src/Genocs.Discovery.Consul/Configurations/IConsulOptionsBuilder.cs similarity index 88% rename from src/Genocs.Discovery.Consul/Options/IConsulOptionsBuilder.cs rename to src/Genocs.Discovery.Consul/Configurations/IConsulOptionsBuilder.cs index c4d8d850..63b5aeda 100644 --- a/src/Genocs.Discovery.Consul/Options/IConsulOptionsBuilder.cs +++ b/src/Genocs.Discovery.Consul/Configurations/IConsulOptionsBuilder.cs @@ -1,4 +1,4 @@ -namespace Genocs.Discovery.Consul.Options; +namespace Genocs.Discovery.Consul.Configurations; public interface IConsulOptionsBuilder { @@ -11,5 +11,5 @@ public interface IConsulOptionsBuilder IConsulOptionsBuilder WithPingInterval(string pingInterval); IConsulOptionsBuilder WithRemoteAfterInterval(string remoteAfterInterval); IConsulOptionsBuilder WithSkippingLocalhostDockerDnsReplace(bool skipLocalhostDockerDnsReplace); - ConsulSettings Build(); + ConsulOptions Build(); } \ No newline at end of file diff --git a/src/Genocs.Discovery.Consul/Extensions.cs b/src/Genocs.Discovery.Consul/Extensions.cs index 81353d8d..a8a8c511 100644 --- a/src/Genocs.Discovery.Consul/Extensions.cs +++ b/src/Genocs.Discovery.Consul/Extensions.cs @@ -1,12 +1,12 @@ using Genocs.Core.Builders; using Genocs.Discovery.Consul.Builders; +using Genocs.Discovery.Consul.Configurations; using Genocs.Discovery.Consul.Http; using Genocs.Discovery.Consul.MessageHandlers; using Genocs.Discovery.Consul.Models; -using Genocs.Discovery.Consul.Options; using Genocs.Discovery.Consul.Services; using Genocs.HTTP; -using Genocs.HTTP.Options; +using Genocs.HTTP.Configurations; using Microsoft.Extensions.DependencyInjection; namespace Genocs.Discovery.Consul; @@ -17,28 +17,34 @@ public static class Extensions private const string SectionName = "consul"; private const string RegistryName = "discovery.consul"; - public static IGenocsBuilder AddConsul(this IGenocsBuilder builder, string sectionName = SectionName, - string httpClientSectionName = "httpClient") + public static IGenocsBuilder AddConsul( + this IGenocsBuilder builder, + string sectionName = SectionName, + string httpClientSectionName = "httpClient") { if (string.IsNullOrWhiteSpace(sectionName)) { sectionName = SectionName; } - var consulOptions = builder.GetOptions(sectionName); - var httpClientOptions = builder.GetOptions(httpClientSectionName); + var consulOptions = builder.GetOptions(sectionName); + var httpClientOptions = builder.GetOptions(httpClientSectionName); return builder.AddConsul(consulOptions, httpClientOptions); } - public static IGenocsBuilder AddConsul(this IGenocsBuilder builder, - Func buildOptions, HttpClientSettings httpClientOptions) + public static IGenocsBuilder AddConsul( + this IGenocsBuilder builder, + Func buildOptions, + HttpClientOptions httpClientOptions) { var options = buildOptions(new ConsulOptionsBuilder()).Build(); return builder.AddConsul(options, httpClientOptions); } - public static IGenocsBuilder AddConsul(this IGenocsBuilder builder, ConsulSettings options, - HttpClientSettings httpClientOptions) + public static IGenocsBuilder AddConsul( + this IGenocsBuilder builder, + ConsulOptions options, + HttpClientOptions httpClientOptions) { builder.Services.AddSingleton(options); if (!options.Enabled || !builder.TryRegister(RegistryName)) @@ -68,17 +74,16 @@ public static IGenocsBuilder AddConsul(this IGenocsBuilder builder, ConsulSettin return builder; } - public static void AddConsulHttpClient(this IGenocsBuilder builder, string clientName, string serviceName) + public static void AddConsulHttpClient(this IGenocsBuilder builder, string clientName, string? serviceName) => builder.Services.AddHttpClient(clientName) .AddHttpMessageHandler(c => new ConsulServiceDiscoveryMessageHandler( c.GetRequiredService(), - c.GetRequiredService(), serviceName, true)); + c.GetRequiredService(), serviceName, true)); - private static ServiceRegistration CreateConsulAgentRegistration(this IGenocsBuilder builder, - ConsulSettings options) + private static ServiceRegistration CreateConsulAgentRegistration(this IGenocsBuilder builder, ConsulOptions options) { - var enabled = options.Enabled; - var consulEnabled = Environment.GetEnvironmentVariable("CONSUL_ENABLED")?.ToLowerInvariant(); + bool enabled = options.Enabled; + string? consulEnabled = Environment.GetEnvironmentVariable("CONSUL_ENABLED")?.ToLowerInvariant(); if (!string.IsNullOrWhiteSpace(consulEnabled)) { enabled = consulEnabled is "true" or "1"; @@ -91,8 +96,7 @@ private static ServiceRegistration CreateConsulAgentRegistration(this IGenocsBui if (string.IsNullOrWhiteSpace(options.Address)) { - throw new ArgumentException("Consul address can not be empty.", - nameof(options.PingEndpoint)); + throw new ArgumentException("Consul address can not be empty.", nameof(options.PingEndpoint)); } builder.Services.AddHttpClient(c => c.BaseAddress = new Uri(options.Url)); @@ -132,7 +136,7 @@ private static ServiceRegistration CreateConsulAgentRegistration(this IGenocsBui pingEndpoint = pingEndpoint.Substring(0, pingEndpoint.Length - 1); } - var scheme = options.Address.StartsWith("http", StringComparison.InvariantCultureIgnoreCase) + string scheme = options.Address.StartsWith("http", StringComparison.InvariantCultureIgnoreCase) ? string.Empty : "http://"; var check = new ServiceCheck diff --git a/src/Genocs.Discovery.Consul/Genocs.Discovery.Consul.csproj b/src/Genocs.Discovery.Consul/Genocs.Discovery.Consul.csproj index 9f9d2066..fc01345c 100644 --- a/src/Genocs.Discovery.Consul/Genocs.Discovery.Consul.csproj +++ b/src/Genocs.Discovery.Consul/Genocs.Discovery.Consul.csproj @@ -1,60 +1,34 @@  - - net6.0;net7.0 - enable - enable - Genocs.Discovery.Consul - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The service discovery by Consul library useful to build .NET Core projects. - The service discovery by Consul library useful to build .NET Core projects. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.Discovery.Consul + Genocs.Discovery.Consul + Genocs.Discovery.Consul + The service discovery by Consul library useful to build .NET Core projects. + The service discovery by Consul library useful to build .NET Core projects. + true + 5.0.0 + Nocco Giovanni Emanuele + microservice microservices solid solid-principles genocs service-discovery + README_NUGET.md + Aligned to the ecosystem + True + latest + - - - True - \ - - - True - \ - - - True - \ - - + + + - - - + - - - - - - - - - - + + + + + + + diff --git a/src/Genocs.Discovery.Consul/Http/ConsulHttpClient.cs b/src/Genocs.Discovery.Consul/Http/ConsulHttpClient.cs index 5b8691fe..23bef586 100644 --- a/src/Genocs.Discovery.Consul/Http/ConsulHttpClient.cs +++ b/src/Genocs.Discovery.Consul/Http/ConsulHttpClient.cs @@ -1,12 +1,16 @@ using Genocs.HTTP; -using Genocs.HTTP.Options; +using Genocs.HTTP.Configurations; namespace Genocs.Discovery.Consul.Http; internal sealed class ConsulHttpClient : GenocsHttpClient, IConsulHttpClient { - public ConsulHttpClient(HttpClient client, HttpClientSettings options, IHttpClientSerializer serializer, - ICorrelationContextFactory correlationContextFactory, ICorrelationIdFactory correlationIdFactory) + public ConsulHttpClient( + HttpClient client, + HttpClientOptions options, + IHttpClientSerializer serializer, + ICorrelationContextFactory correlationContextFactory, + ICorrelationIdFactory correlationIdFactory) : base(client, options, serializer, correlationContextFactory, correlationIdFactory) { } diff --git a/src/Genocs.Discovery.Consul/IConsulService.cs b/src/Genocs.Discovery.Consul/IConsulService.cs index d9209754..9c405e4e 100644 --- a/src/Genocs.Discovery.Consul/IConsulService.cs +++ b/src/Genocs.Discovery.Consul/IConsulService.cs @@ -6,5 +6,5 @@ public interface IConsulService { Task RegisterServiceAsync(ServiceRegistration registration); Task DeregisterServiceAsync(string id); - Task> GetServiceAgentsAsync(string service = null); + Task> GetServiceAgentsAsync(string? service = null); } \ No newline at end of file diff --git a/src/Genocs.Discovery.Consul/IConsulServicesRegistry.cs b/src/Genocs.Discovery.Consul/IConsulServicesRegistry.cs index 33e7fac7..5cbaebe8 100644 --- a/src/Genocs.Discovery.Consul/IConsulServicesRegistry.cs +++ b/src/Genocs.Discovery.Consul/IConsulServicesRegistry.cs @@ -4,5 +4,5 @@ namespace Genocs.Discovery.Consul; public interface IConsulServicesRegistry { - Task GetAsync(string name); + Task GetAsync(string name); } \ No newline at end of file diff --git a/src/Genocs.Discovery.Consul/MessageHandlers/ConsulServiceDiscoveryMessageHandler.cs b/src/Genocs.Discovery.Consul/MessageHandlers/ConsulServiceDiscoveryMessageHandler.cs index f9b9e9d4..481db53f 100644 --- a/src/Genocs.Discovery.Consul/MessageHandlers/ConsulServiceDiscoveryMessageHandler.cs +++ b/src/Genocs.Discovery.Consul/MessageHandlers/ConsulServiceDiscoveryMessageHandler.cs @@ -1,33 +1,42 @@ -using Genocs.Discovery.Consul.Options; +using Genocs.Discovery.Consul.Configurations; namespace Genocs.Discovery.Consul.MessageHandlers; internal sealed class ConsulServiceDiscoveryMessageHandler : DelegatingHandler { private readonly IConsulServicesRegistry _servicesRegistry; - private readonly ConsulSettings _options; - private readonly string _serviceName; + private readonly ConsulOptions _options; + private readonly string? _serviceName; private readonly bool? _overrideRequestUri; - public ConsulServiceDiscoveryMessageHandler(IConsulServicesRegistry servicesRegistry, - ConsulSettings options, string serviceName = null, bool? overrideRequestUri = null) + public ConsulServiceDiscoveryMessageHandler( + IConsulServicesRegistry servicesRegistry, + ConsulOptions options, + string? serviceName = null, + bool? overrideRequestUri = null) { - if (string.IsNullOrWhiteSpace(options.Url)) - { - throw new InvalidOperationException("Consul URL was not provided."); - } - _servicesRegistry = servicesRegistry; _options = options; _serviceName = serviceName; _overrideRequestUri = overrideRequestUri; + + if (!options.Enabled) + { + return; + } + + if (string.IsNullOrWhiteSpace(options.Url)) + { + throw new InvalidOperationException("Consul URL was not provided."); + } } - protected override async Task SendAsync(HttpRequestMessage request, - CancellationToken cancellationToken) + protected override async Task SendAsync( + HttpRequestMessage request, + CancellationToken cancellationToken) { var uri = GetUri(request); - var serviceName = string.IsNullOrWhiteSpace(_serviceName) ? uri.Host : _serviceName; + string serviceName = string.IsNullOrWhiteSpace(_serviceName) ? uri.Host : _serviceName; return await SendAsync(request, serviceName, uri, cancellationToken); } @@ -40,8 +49,11 @@ private Uri GetUri(HttpRequestMessage request) $"{request.RequestUri.Scheme}://{_serviceName}/{request.RequestUri.Host}{request.RequestUri.PathAndQuery}") : request.RequestUri; - private async Task SendAsync(HttpRequestMessage request, - string serviceName, Uri uri, CancellationToken cancellationToken) + private async Task SendAsync( + HttpRequestMessage request, + string serviceName, + Uri uri, + CancellationToken cancellationToken) { if (!_options.Enabled) { @@ -53,15 +65,13 @@ private async Task SendAsync(HttpRequestMessage request, return await base.SendAsync(request, cancellationToken); } - private async Task GetRequestUriAsync(HttpRequestMessage request, - string serviceName, Uri uri) + private async Task GetRequestUriAsync( + HttpRequestMessage request, + string serviceName, + Uri uri) { - var service = await _servicesRegistry.GetAsync(serviceName); - if (service is null) - { - throw new ConsulServiceNotFoundException($"Consul service: '{serviceName}' was not found.", - serviceName); - } + var service = await _servicesRegistry.GetAsync(serviceName) + ?? throw new ConsulServiceNotFoundException($"Consul service: '{serviceName}' was not found.", serviceName); if (!_options.SkipLocalhostDockerDnsReplace) { diff --git a/src/Genocs.Discovery.Consul/MessageHandlers/ConsulServiceNotFoundException.cs b/src/Genocs.Discovery.Consul/MessageHandlers/ConsulServiceNotFoundException.cs index 4e8d151a..4fa720f8 100644 --- a/src/Genocs.Discovery.Consul/MessageHandlers/ConsulServiceNotFoundException.cs +++ b/src/Genocs.Discovery.Consul/MessageHandlers/ConsulServiceNotFoundException.cs @@ -1,16 +1,16 @@ -using System; - namespace Genocs.Discovery.Consul.MessageHandlers; internal sealed class ConsulServiceNotFoundException : Exception { public string ServiceName { get; set; } - public ConsulServiceNotFoundException(string serviceName) : this(string.Empty, serviceName) + public ConsulServiceNotFoundException(string serviceName) + : this(string.Empty, serviceName) { } - public ConsulServiceNotFoundException(string message, string serviceName) : base(message) + public ConsulServiceNotFoundException(string message, string serviceName) + : base(message) { ServiceName = serviceName; } diff --git a/src/Genocs.Discovery.Consul/Options/ConsulSettings.cs b/src/Genocs.Discovery.Consul/Options/ConsulSettings.cs deleted file mode 100644 index df0c848b..00000000 --- a/src/Genocs.Discovery.Consul/Options/ConsulSettings.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Genocs.Discovery.Consul.Options; - -public class ConsulSettings -{ - public bool Enabled { get; set; } - public string Url { get; set; } - public string Service { get; set; } - public string Address { get; set; } - public int Port { get; set; } - public bool PingEnabled { get; set; } - public string PingEndpoint { get; set; } - public string PingInterval { get; set; } - public string RemoveAfterInterval { get; set; } - public List Tags { get; set; } - public IDictionary Meta { get; set; } - public bool EnableTagOverride { get; set; } - public bool SkipLocalhostDockerDnsReplace { get; set; } - public ConnectSettings Connect { get; set; } - - public class ConnectSettings - { - public bool Enabled { get; set; } - } -} \ No newline at end of file diff --git a/src/Genocs.QueryBuilder/README.md b/src/Genocs.Discovery.Consul/README_NUGET.md similarity index 60% rename from src/Genocs.QueryBuilder/README.md rename to src/Genocs.Discovery.Consul/README_NUGET.md index 945bb03b..6ffbc96c 100644 --- a/src/Genocs.QueryBuilder/README.md +++ b/src/Genocs.Discovery.Consul/README_NUGET.md @@ -15,7 +15,35 @@ Please check the GitHub repository getting more info. ## Release notes -### [2023-03-12] 5.0.0-preview.5.0 +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 - Implemented MongoDB repository interfaces ### [2023-03-12] 5.0.0 diff --git a/src/Genocs.Discovery.Consul/Services/ConsulService.cs b/src/Genocs.Discovery.Consul/Services/ConsulService.cs index 4ca5ae45..0f32f333 100644 --- a/src/Genocs.Discovery.Consul/Services/ConsulService.cs +++ b/src/Genocs.Discovery.Consul/Services/ConsulService.cs @@ -6,8 +6,8 @@ namespace Genocs.Discovery.Consul.Services; internal sealed class ConsulService : IConsulService { - private static readonly StringContent EmptyRequest = GetPayload(new { }); private const string Version = "v1"; + private static readonly StringContent EmptyRequest = GetPayload(new { }); private readonly HttpClient _client; public ConsulService(HttpClient client) @@ -21,16 +21,16 @@ public Task RegisterServiceAsync(ServiceRegistration regist public Task DeregisterServiceAsync(string id) => _client.PutAsync(GetEndpoint($"agent/service/deregister/{id}"), EmptyRequest); - public async Task> GetServiceAgentsAsync(string service = null) + public async Task> GetServiceAgentsAsync(string? service = null) { - var filter = string.IsNullOrWhiteSpace(service) ? string.Empty : $"?filter=Service==\"{service}\""; + string filter = string.IsNullOrWhiteSpace(service) ? string.Empty : $"?filter=Service==\"{service}\""; var response = await _client.GetAsync(GetEndpoint($"agent/services{filter}")); if (!response.IsSuccessStatusCode) { return new Dictionary(); } - var content = await response.Content.ReadAsStringAsync(); + string content = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize>(content); } diff --git a/src/Genocs.Discovery.Consul/Services/ConsulServicesRegistry.cs b/src/Genocs.Discovery.Consul/Services/ConsulServicesRegistry.cs index c8e3e277..f498ca44 100644 --- a/src/Genocs.Discovery.Consul/Services/ConsulServicesRegistry.cs +++ b/src/Genocs.Discovery.Consul/Services/ConsulServicesRegistry.cs @@ -13,7 +13,7 @@ public ConsulServicesRegistry(IConsulService consulService) _consulService = consulService; } - public async Task GetAsync(string name) + public async Task GetAsync(string name) { var services = await _consulService.GetServiceAgentsAsync(name); if (!services.Any()) @@ -33,17 +33,14 @@ public async Task GetAsync(string name) return GetService(services, name); } - private ServiceAgent GetService(IDictionary services, string name) + private ServiceAgent? GetService(IDictionary services, string name) { - switch (services.Count) + return services.Count switch { - case 0: - return null; - case 1: - return services.First().Value; - default: - return ChooseService(services, name); - } + 0 => null, + 1 => services.First().Value, + _ => ChooseService(services, name), + }; } private ServiceAgent ChooseService(IDictionary services, string name) diff --git a/src/Genocs.HTTP.RestEase/Builders/RestEaseBuilders.cs b/src/Genocs.HTTP.RestEase/Builders/RestEaseOptionsBuilder.cs similarity index 62% rename from src/Genocs.HTTP.RestEase/Builders/RestEaseBuilders.cs rename to src/Genocs.HTTP.RestEase/Builders/RestEaseOptionsBuilder.cs index da1d0b69..55bb2367 100644 --- a/src/Genocs.HTTP.RestEase/Builders/RestEaseBuilders.cs +++ b/src/Genocs.HTTP.RestEase/Builders/RestEaseOptionsBuilder.cs @@ -1,26 +1,26 @@ -using Genocs.HTTP.RestEase.Options; +using Genocs.HTTP.RestEase.Configurations; namespace Genocs.HTTP.RestEase.Builders; -internal sealed class RestEaseSettingsBuilder : IRestEaseSettingsBuilder +internal sealed class RestEaseOptionsBuilder : IRestEaseOptionsBuilder { - private readonly RestEaseSettings _options = new(); - private readonly List _services = new(); + private readonly RestEaseOptions _options = new(); + private readonly List _services = new(); - public IRestEaseSettingsBuilder WithLoadBalancer(string loadBalancer) + public IRestEaseOptionsBuilder WithLoadBalancer(string loadBalancer) { _options.LoadBalancer = loadBalancer; return this; } - public IRestEaseSettingsBuilder WithService(Func buildService) + public IRestEaseOptionsBuilder WithService(Func buildService) { var service = buildService(new RestEaseServiceBuilder()).Build(); _services.Add(service); return this; } - public RestEaseSettings Build() + public RestEaseOptions Build() { _options.Services = _services; return _options; @@ -28,7 +28,7 @@ public RestEaseSettings Build() private class RestEaseServiceBuilder : IRestEaseServiceBuilder { - private readonly RestEaseSettings.Service _service = new(); + private readonly RestEaseOptions.Service _service = new(); public IRestEaseServiceBuilder WithName(string name) { @@ -54,6 +54,6 @@ public IRestEaseServiceBuilder WithPort(int port) return this; } - public RestEaseSettings.Service Build() => _service; + public RestEaseOptions.Service Build() => _service; } } \ No newline at end of file diff --git a/src/Genocs.HTTP.RestEase/Configurations/IRestEaseOptionsBuilder.cs b/src/Genocs.HTTP.RestEase/Configurations/IRestEaseOptionsBuilder.cs new file mode 100644 index 00000000..f013109a --- /dev/null +++ b/src/Genocs.HTTP.RestEase/Configurations/IRestEaseOptionsBuilder.cs @@ -0,0 +1,8 @@ +namespace Genocs.HTTP.RestEase.Configurations; + +public interface IRestEaseOptionsBuilder +{ + IRestEaseOptionsBuilder WithLoadBalancer(string loadBalancer); + IRestEaseOptionsBuilder WithService(Func buildService); + RestEaseOptions Build(); +} \ No newline at end of file diff --git a/src/Genocs.HTTP.RestEase/Configurations/RestEaseOptions.cs b/src/Genocs.HTTP.RestEase/Configurations/RestEaseOptions.cs new file mode 100644 index 00000000..87f927bc --- /dev/null +++ b/src/Genocs.HTTP.RestEase/Configurations/RestEaseOptions.cs @@ -0,0 +1,15 @@ +namespace Genocs.HTTP.RestEase.Configurations; + +public class RestEaseOptions +{ + public string? LoadBalancer { get; set; } + public IEnumerable? Services { get; set; } + + public class Service + { + public string? Name { get; set; } + public string? Scheme { get; set; } + public string? Host { get; set; } + public int Port { get; set; } + } +} \ No newline at end of file diff --git a/src/Genocs.HTTP.RestEase/Extensions.cs b/src/Genocs.HTTP.RestEase/Extensions.cs index 26591664..81610509 100644 --- a/src/Genocs.HTTP.RestEase/Extensions.cs +++ b/src/Genocs.HTTP.RestEase/Extensions.cs @@ -1,12 +1,12 @@ using Genocs.Core.Builders; using Genocs.Discovery.Consul; -using Genocs.Discovery.Consul.Options; -using Genocs.HTTP.Options; +using Genocs.Discovery.Consul.Configurations; +using Genocs.HTTP.Configurations; using Genocs.HTTP.RestEase.Builders; -using Genocs.HTTP.RestEase.Options; +using Genocs.HTTP.RestEase.Configurations; using Genocs.HTTP.RestEase.Serializers; using Genocs.LoadBalancing.Fabio; -using Genocs.LoadBalancing.Fabio.Options; +using Genocs.LoadBalancing.Fabio.Configurations; using Microsoft.Extensions.DependencyInjection; using RestEase; @@ -17,9 +17,13 @@ public static class Extensions private const string SectionName = "restEase"; private const string RegistryName = "http.restEase"; - public static IGenocsBuilder AddServiceClient(this IGenocsBuilder builder, string serviceName, - string sectionName = SectionName, string consulSectionName = "consul", string fabioSectionName = "fabio", - string httpClientSectionName = "httpClient") + public static IGenocsBuilder AddServiceClient( + this IGenocsBuilder builder, + string serviceName, + string sectionName = SectionName, + string consulSectionName = "consul", + string fabioSectionName = "fabio", + string httpClientSectionName = "httpClient") where T : class { if (string.IsNullOrWhiteSpace(sectionName)) @@ -27,32 +31,44 @@ public static IGenocsBuilder AddServiceClient(this IGenocsBuilder builder, st sectionName = SectionName; } - var restEaseOptions = builder.GetOptions(sectionName); - return builder.AddServiceClient(serviceName, restEaseOptions, - b => b.AddFabio(fabioSectionName, consulSectionName, httpClientSectionName)); + var restEaseOptions = builder.GetOptions(sectionName); + return builder.AddServiceClient( + serviceName, + restEaseOptions, + b => b.AddFabio(fabioSectionName, consulSectionName, httpClientSectionName)); } - public static IGenocsBuilder AddServiceClient(this IGenocsBuilder builder, string serviceName, - Func buildOptions, - Func buildConsulOptions, - Func buildFabioOptions, - HttpClientSettings httpClientOptions) + public static IGenocsBuilder AddServiceClient( + this IGenocsBuilder builder, + string serviceName, + Func buildOptions, + Func buildConsulOptions, + Func buildFabioOptions, + HttpClientOptions httpClientOptions) where T : class { - var options = buildOptions(new RestEaseSettingsBuilder()).Build(); - return builder.AddServiceClient(serviceName, options, - b => b.AddFabio(buildFabioOptions, buildConsulOptions, httpClientOptions)); + var options = buildOptions(new RestEaseOptionsBuilder()).Build(); + return builder.AddServiceClient( + serviceName, + options, + b => b.AddFabio(buildFabioOptions, buildConsulOptions, httpClientOptions)); } - public static IGenocsBuilder AddServiceClient(this IGenocsBuilder builder, string serviceName, - RestEaseSettings options, ConsulSettings consulOptions, FabioSettings fabioOptions, - HttpClientSettings httpClientOptions) + public static IGenocsBuilder AddServiceClient( + this IGenocsBuilder builder, + string serviceName, + RestEaseOptions options, + ConsulOptions consulOptions, + FabioOptions fabioOptions, + HttpClientOptions httpClientOptions) where T : class - => builder.AddServiceClient(serviceName, options, - b => b.AddFabio(fabioOptions, consulOptions, httpClientOptions)); + => builder.AddServiceClient(serviceName, options, b => b.AddFabio(fabioOptions, consulOptions, httpClientOptions)); - private static IGenocsBuilder AddServiceClient(this IGenocsBuilder builder, string serviceName, - RestEaseSettings options, Action registerFabio) + private static IGenocsBuilder AddServiceClient( + this IGenocsBuilder builder, + string serviceName, + RestEaseOptions options, + Action registerFabio) where T : class { if (!builder.TryRegister(RegistryName)) @@ -60,7 +76,7 @@ private static IGenocsBuilder AddServiceClient(this IGenocsBuilder builder, s return builder; } - var clientName = typeof(T).ToString(); + string clientName = typeof(T).ToString(); switch (options.LoadBalancer?.ToLowerInvariant()) { @@ -82,17 +98,19 @@ private static IGenocsBuilder AddServiceClient(this IGenocsBuilder builder, s return builder; } - private static void ConfigureDefaultClient(IServiceCollection services, string clientName, - string serviceName, RestEaseSettings options) + private static void ConfigureDefaultClient( + IServiceCollection services, + string clientName, + string serviceName, + RestEaseOptions options) { services.AddHttpClient(clientName, client => { - var service = options.Services.SingleOrDefault(s => s.Name.Equals(serviceName, - StringComparison.InvariantCultureIgnoreCase)); + var service = options.Services?.SingleOrDefault(s => s.Name.Equals(serviceName, StringComparison.InvariantCultureIgnoreCase)); + if (service is null) { - throw new RestEaseServiceNotFoundException($"RestEase service: '{serviceName}' was not found.", - serviceName); + throw new RestEaseServiceNotFoundException($"RestEase service: '{serviceName}' was not found.", serviceName); } client.BaseAddress = new UriBuilder @@ -104,7 +122,8 @@ private static void ConfigureDefaultClient(IServiceCollection services, string c }); } - private static void ConfigureForwarder(IServiceCollection services, string clientName) where T : class + private static void ConfigureForwarder(IServiceCollection services, string clientName) + where T : class { services.AddTransient(c => new RestClient(c.GetRequiredService().CreateClient(clientName)) { diff --git a/src/Genocs.HTTP.RestEase/Genocs.HTTP.RestEase.csproj b/src/Genocs.HTTP.RestEase/Genocs.HTTP.RestEase.csproj index 4f988395..1110507f 100644 --- a/src/Genocs.HTTP.RestEase/Genocs.HTTP.RestEase.csproj +++ b/src/Genocs.HTTP.RestEase/Genocs.HTTP.RestEase.csproj @@ -1,65 +1,37 @@  - - - net6.0;net7.0 - enable - enable - Genocs.HTTP.RestEase - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The http support library useful to build .NET Core projects. - The http support library useful to build .NET Core projects. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - - - - - True - \ - - - True - \ - - - True - \ - - - - - - - - - - - - - - - - - - - - - - + + net8.0;net7.0;net6.0 + Genocs.HTTP.RestEase + Genocs.HTTP.RestEase + Genocs.HTTP.RestEase + The http support library useful to build .NET Core projects. + The http support library useful to build .NET Core projects. + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + + + + + + + + + + + + + + + + + + + diff --git a/src/Genocs.HTTP.RestEase/IRestEaseServiceBuilder.cs b/src/Genocs.HTTP.RestEase/IRestEaseServiceBuilder.cs index 680728ac..bcbdae00 100644 --- a/src/Genocs.HTTP.RestEase/IRestEaseServiceBuilder.cs +++ b/src/Genocs.HTTP.RestEase/IRestEaseServiceBuilder.cs @@ -1,4 +1,4 @@ -using Genocs.HTTP.RestEase.Options; +using Genocs.HTTP.RestEase.Configurations; namespace Genocs.HTTP.RestEase; @@ -8,5 +8,5 @@ public interface IRestEaseServiceBuilder IRestEaseServiceBuilder WithScheme(string scheme); IRestEaseServiceBuilder WithHost(string host); IRestEaseServiceBuilder WithPort(int port); - RestEaseSettings.Service Build(); + RestEaseOptions.Service Build(); } \ No newline at end of file diff --git a/src/Genocs.HTTP.RestEase/IRestEaseSettingsBuilder.cs b/src/Genocs.HTTP.RestEase/IRestEaseSettingsBuilder.cs deleted file mode 100644 index 15353665..00000000 --- a/src/Genocs.HTTP.RestEase/IRestEaseSettingsBuilder.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Genocs.HTTP.RestEase.Options; - -namespace Genocs.HTTP.RestEase; - -public interface IRestEaseSettingsBuilder -{ - IRestEaseSettingsBuilder WithLoadBalancer(string loadBalancer); - IRestEaseSettingsBuilder WithService(Func buildService); - RestEaseSettings Build(); -} \ No newline at end of file diff --git a/src/Genocs.HTTP.RestEase/Options/RestEaseSettings.cs b/src/Genocs.HTTP.RestEase/Options/RestEaseSettings.cs deleted file mode 100644 index 5c14911b..00000000 --- a/src/Genocs.HTTP.RestEase/Options/RestEaseSettings.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Genocs.HTTP.RestEase.Options; - -public class RestEaseSettings -{ - public string LoadBalancer { get; set; } - public IEnumerable Services { get; set; } - - public class Service - { - public string Name { get; set; } - public string Scheme { get; set; } - public string Host { get; set; } - public int Port { get; set; } - } -} \ No newline at end of file diff --git a/src/Genocs.HTTP.RestEase/README.md b/src/Genocs.HTTP.RestEase/README_NUGET.md similarity index 60% rename from src/Genocs.HTTP.RestEase/README.md rename to src/Genocs.HTTP.RestEase/README_NUGET.md index 945bb03b..6ffbc96c 100644 --- a/src/Genocs.HTTP.RestEase/README.md +++ b/src/Genocs.HTTP.RestEase/README_NUGET.md @@ -15,7 +15,35 @@ Please check the GitHub repository getting more info. ## Release notes -### [2023-03-12] 5.0.0-preview.5.0 +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 - Implemented MongoDB repository interfaces ### [2023-03-12] 5.0.0 diff --git a/src/Genocs.HTTP.RestEase/RestEaseServiceNotFoundException.cs b/src/Genocs.HTTP.RestEase/RestEaseServiceNotFoundException.cs index 24964dbd..a20743ca 100644 --- a/src/Genocs.HTTP.RestEase/RestEaseServiceNotFoundException.cs +++ b/src/Genocs.HTTP.RestEase/RestEaseServiceNotFoundException.cs @@ -4,11 +4,13 @@ public class RestEaseServiceNotFoundException : Exception { public string ServiceName { get; set; } - public RestEaseServiceNotFoundException(string serviceName) : this(string.Empty, serviceName) + public RestEaseServiceNotFoundException(string serviceName) + : this(string.Empty, serviceName) { } - public RestEaseServiceNotFoundException(string message, string serviceName) : base(message) + public RestEaseServiceNotFoundException(string message, string serviceName) + : base(message) { ServiceName = serviceName; } diff --git a/src/Genocs.HTTP.RestEase/Serializers/QueryParamSerializer.cs b/src/Genocs.HTTP.RestEase/Serializers/QueryParamSerializer.cs index 46159da2..cffa804d 100644 --- a/src/Genocs.HTTP.RestEase/Serializers/QueryParamSerializer.cs +++ b/src/Genocs.HTTP.RestEase/Serializers/QueryParamSerializer.cs @@ -33,9 +33,9 @@ private IEnumerable> Serialize(string name, T va } } - private Dictionary GetPropertiesDeepRecursive(object obj, string name) + private Dictionary GetPropertiesDeepRecursive(object? obj, string name) { - var dict = new Dictionary(); + var dict = new Dictionary(); if (obj is null) { @@ -52,24 +52,27 @@ private Dictionary GetPropertiesDeepRecursive(object obj, string if (obj is IEnumerable collection) { int i = 0; - foreach (var item in collection) + foreach (object? item in collection) { dict = dict.Concat(GetPropertiesDeepRecursive(item, $"{name}[{i++}]")).ToDictionary(e => e.Key, e => e.Value); } + return dict; } var properties = obj.GetType().GetProperties(); - //If the prefix won't be empty, then it is needed to specify [Query(null)]. - //Otherwise, the query string will contain the query name e.g. 'query.page' instead of just 'page'. - //var prefix = string.IsNullOrWhiteSpace(name) ? string.Empty : $"{name}."; - var prefix = string.Empty; + + // If the prefix won't be empty, then it is needed to specify [Query(null)]. + // Otherwise, the query string will contain the query name e.g. 'query.page' instead of just 'page'. + // var prefix = string.IsNullOrWhiteSpace(name) ? string.Empty : $"{name}."; + string prefix = string.Empty; foreach (var prop in properties) { dict = dict .Concat(GetPropertiesDeepRecursive(prop.GetValue(obj, null), $"{prefix}{prop.Name}")) .ToDictionary(e => e.Key, e => e.Value); } + return dict; } } \ No newline at end of file diff --git a/src/Genocs.HTTP/Configurations/HttpClientOptions.cs b/src/Genocs.HTTP/Configurations/HttpClientOptions.cs new file mode 100644 index 00000000..91bd72e7 --- /dev/null +++ b/src/Genocs.HTTP/Configurations/HttpClientOptions.cs @@ -0,0 +1,44 @@ +namespace Genocs.HTTP.Configurations; + +/// +/// The HttpClient settings. +/// +public class HttpClientOptions +{ + /// + /// Default section name. + /// + public const string Position = "httpClient"; + + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + /// + /// It defines if set consul as service discovery or Fabio as load balancer. + /// Allowed values are: consul, Fabio. + /// + public string? Type { get; set; } + + /// + /// It defines the number of retries for each request. + /// + public int Retries { get; set; } + + /// + /// It defines the list of services to be registered. + /// + public IDictionary? Services { get; set; } + public RequestMaskingOptions? RequestMasking { get; set; } + public bool RemoveCharsetFromContentType { get; set; } + public string? CorrelationContextHeader { get; set; } + public string? CorrelationIdHeader { get; set; } + + public class RequestMaskingOptions + { + public bool Enabled { get; set; } + public IEnumerable? UrlParts { get; set; } + public string? MaskTemplate { get; set; } + } +} \ No newline at end of file diff --git a/src/Genocs.HTTP/EmptyCorrelationContextFactory.cs b/src/Genocs.HTTP/EmptyCorrelationContextFactory.cs index aa3b6133..cc9cbb7b 100644 --- a/src/Genocs.HTTP/EmptyCorrelationContextFactory.cs +++ b/src/Genocs.HTTP/EmptyCorrelationContextFactory.cs @@ -2,5 +2,5 @@ namespace Genocs.HTTP; internal class EmptyCorrelationContextFactory : ICorrelationContextFactory { - public string Create() => default; + public string Create() => default!; } \ No newline at end of file diff --git a/src/Genocs.HTTP/EmptyCorrelationIdFactory.cs b/src/Genocs.HTTP/EmptyCorrelationIdFactory.cs index 75f26d6f..574c9375 100644 --- a/src/Genocs.HTTP/EmptyCorrelationIdFactory.cs +++ b/src/Genocs.HTTP/EmptyCorrelationIdFactory.cs @@ -3,7 +3,7 @@ namespace Genocs.HTTP; internal class EmptyCorrelationIdFactory : ICorrelationIdFactory { /// - /// CorrelationIdFactory, empty implementation + /// CorrelationIdFactory, empty implementation. /// /// public string? Create() => default; diff --git a/src/Genocs.HTTP/Extensions.cs b/src/Genocs.HTTP/Extensions.cs index 699a5819..908bb7d1 100644 --- a/src/Genocs.HTTP/Extensions.cs +++ b/src/Genocs.HTTP/Extensions.cs @@ -1,5 +1,5 @@ using Genocs.Core.Builders; -using Genocs.HTTP.Options; +using Genocs.HTTP.Configurations; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Http; @@ -8,7 +8,7 @@ namespace Genocs.HTTP; /// -/// The HTTP client extensions +/// The HTTP client extensions. /// public static class Extensions { @@ -16,10 +16,12 @@ public static class Extensions private const string RegistryName = "http.client"; private const string ClientName = "genocs"; - - public static IGenocsBuilder AddHttpClient(this IGenocsBuilder builder, string clientName = ClientName, - IEnumerable? maskedRequestUrlParts = null, string sectionName = SectionName, - Action? httpClientBuilder = null) + public static IGenocsBuilder AddHttpClient( + this IGenocsBuilder builder, + string clientName = ClientName, + IEnumerable? maskedRequestUrlParts = null, + string sectionName = SectionName, + Action? httpClientBuilder = null) { if (string.IsNullOrWhiteSpace(sectionName)) { @@ -36,7 +38,7 @@ public static IGenocsBuilder AddHttpClient(this IGenocsBuilder builder, string c throw new ArgumentException("HTTP client name cannot be empty.", nameof(clientName)); } - var options = builder.GetOptions(sectionName); + var options = builder.GetOptions(sectionName); if (maskedRequestUrlParts is not null && options.RequestMasking is not null) { options.RequestMasking.UrlParts = maskedRequestUrlParts; @@ -79,7 +81,8 @@ public static void RemoveHttpClient(this IGenocsBuilder builder) { var registryType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()) .SingleOrDefault(t => t.Name == "HttpClientMappingRegistry"); - var registry = builder.Services.SingleOrDefault(s => s.ServiceType == registryType)?.ImplementationInstance; + + object? registry = builder.Services.SingleOrDefault(s => s.ServiceType == registryType)?.ImplementationInstance; var registrations = registry?.GetType().GetProperty("TypedClientRegistrations"); var clientRegistrations = registrations?.GetValue(registry) as IDictionary; clientRegistrations?.Remove(typeof(IHttpClient)); diff --git a/src/Genocs.HTTP/Genocs.HTTP.csproj b/src/Genocs.HTTP/Genocs.HTTP.csproj index 43309689..f7aa5933 100644 --- a/src/Genocs.HTTP/Genocs.HTTP.csproj +++ b/src/Genocs.HTTP/Genocs.HTTP.csproj @@ -1,58 +1,33 @@  - - net6.0;net7.0 - enable - enable - Genocs.HTTP - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The http support library useful to build .NET Core projects. - The http support library useful to build .NET Core projects. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.HTTP + Genocs.HTTP + Genocs.HTTP + The http support library useful to build .NET Core projects. + The http support library useful to build .NET Core projects. + true + 5.0.0 + Nocco Giovanni Emanuele + microservice microservices solid solid-principles genocs http-client + README_NUGET.md + Aligned to the ecosystem + True + latest + - - - True - \ - - - True - \ - - - True - \ - - + + + - - - + + + - - - - - - - - + + + + diff --git a/src/Genocs.HTTP/GenocsHttpClient.cs b/src/Genocs.HTTP/GenocsHttpClient.cs index 6437f934..63d02470 100644 --- a/src/Genocs.HTTP/GenocsHttpClient.cs +++ b/src/Genocs.HTTP/GenocsHttpClient.cs @@ -1,4 +1,4 @@ -using Genocs.HTTP.Options; +using Genocs.HTTP.Configurations; using Polly; using System.Net.Http.Headers; using System.Text; @@ -6,131 +6,128 @@ namespace Genocs.HTTP; /// -/// The Genocs Http Client +/// The Genocs Http Client. /// public class GenocsHttpClient : IHttpClient { private const string JsonContentType = "application/json"; private readonly HttpClient _client; - private readonly HttpClientSettings _options; + private readonly HttpClientOptions _settings; private readonly IHttpClientSerializer _serializer; - public GenocsHttpClient(HttpClient client, HttpClientSettings options, IHttpClientSerializer serializer, - ICorrelationContextFactory correlationContextFactory, ICorrelationIdFactory correlationIdFactory) + public GenocsHttpClient( + HttpClient client, + HttpClientOptions settings, + IHttpClientSerializer serializer, + ICorrelationContextFactory correlationContextFactory, + ICorrelationIdFactory correlationIdFactory) { _client = client; - _options = options; + _settings = settings; _serializer = serializer; - if (!string.IsNullOrWhiteSpace(_options.CorrelationContextHeader)) + if (!string.IsNullOrWhiteSpace(_settings.CorrelationContextHeader)) { - var correlationContext = correlationContextFactory.Create(); - _client.DefaultRequestHeaders.TryAddWithoutValidation(_options.CorrelationContextHeader, - correlationContext); + string correlationContext = correlationContextFactory.Create(); + _client.DefaultRequestHeaders.TryAddWithoutValidation( + _settings.CorrelationContextHeader, + correlationContext); } - if (!string.IsNullOrWhiteSpace(_options.CorrelationIdHeader)) + if (!string.IsNullOrWhiteSpace(_settings.CorrelationIdHeader)) { - var correlationId = correlationIdFactory.Create(); - _client.DefaultRequestHeaders.TryAddWithoutValidation(_options.CorrelationIdHeader, - correlationId); + string? correlationId = correlationIdFactory.Create(); + _client.DefaultRequestHeaders.TryAddWithoutValidation( + _settings.CorrelationIdHeader, + correlationId); } } public virtual Task GetAsync(string uri) => SendAsync(uri, Method.Get); - public virtual Task GetAsync(string uri, IHttpClientSerializer serializer = null) + public virtual Task GetAsync(string uri, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Get, serializer: serializer); - public Task> GetResultAsync(string uri, IHttpClientSerializer serializer = null) + public Task> GetResultAsync(string uri, IHttpClientSerializer? serializer = null) => SendResultAsync(uri, Method.Get, serializer: serializer); - public virtual Task PostAsync(string uri, object data = null, - IHttpClientSerializer serializer = null) + public virtual Task PostAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Post, GetJsonPayload(data, serializer)); public Task PostAsync(string uri, HttpContent content) => SendAsync(uri, Method.Post, content); - public virtual Task PostAsync(string uri, object data = null, IHttpClientSerializer serializer = null) + public virtual Task PostAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Post, GetJsonPayload(data, serializer)); - public Task PostAsync(string uri, HttpContent content, IHttpClientSerializer serializer = null) + public Task PostAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Post, content, serializer); - public Task> PostResultAsync(string uri, object data = null, - IHttpClientSerializer serializer = null) + public Task> PostResultAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) => SendResultAsync(uri, Method.Post, GetJsonPayload(data, serializer), serializer); - public Task> PostResultAsync(string uri, HttpContent content, - IHttpClientSerializer serializer = null) + public Task> PostResultAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null) => SendResultAsync(uri, Method.Post, content, serializer); - public virtual Task PutAsync(string uri, object data = null, - IHttpClientSerializer serializer = null) + public virtual Task PutAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Put, GetJsonPayload(data, serializer)); public Task PutAsync(string uri, HttpContent content) => SendAsync(uri, Method.Put, content); - public virtual Task PutAsync(string uri, object data = null, IHttpClientSerializer serializer = null) + public virtual Task PutAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Put, GetJsonPayload(data, serializer), serializer); - public Task PutAsync(string uri, HttpContent content, IHttpClientSerializer serializer = null) + public Task PutAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Put, content, serializer); - public Task> PutResultAsync(string uri, object data = null, - IHttpClientSerializer serializer = null) + public Task> PutResultAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) => SendResultAsync(uri, Method.Put, GetJsonPayload(data, serializer), serializer); - public Task> PutResultAsync(string uri, HttpContent content, - IHttpClientSerializer serializer = null) + public Task> PutResultAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null) => SendResultAsync(uri, Method.Put, content, serializer); - public Task PatchAsync(string uri, object data = null, - IHttpClientSerializer serializer = null) + public Task PatchAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Patch, GetJsonPayload(data, serializer)); public Task PatchAsync(string uri, HttpContent content) => SendAsync(uri, Method.Patch, content); - public Task PatchAsync(string uri, object data = null, IHttpClientSerializer serializer = null) + public Task PatchAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Patch, GetJsonPayload(data, serializer)); - public Task PatchAsync(string uri, HttpContent content, IHttpClientSerializer serializer = null) + public Task PatchAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Patch, content, serializer); - public Task> PatchResultAsync(string uri, object data = null, - IHttpClientSerializer serializer = null) + public Task> PatchResultAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) => SendResultAsync(uri, Method.Patch, GetJsonPayload(data, serializer)); - public Task> PatchResultAsync(string uri, HttpContent content, - IHttpClientSerializer serializer = null) + public Task> PatchResultAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null) => SendResultAsync(uri, Method.Patch, content, serializer); public virtual Task DeleteAsync(string uri) => SendAsync(uri, Method.Delete); - public Task DeleteAsync(string uri, IHttpClientSerializer serializer = null) + public Task DeleteAsync(string uri, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Delete, serializer: serializer); - public Task> DeleteResultAsync(string uri, IHttpClientSerializer serializer = null) + public Task> DeleteResultAsync(string uri, IHttpClientSerializer? serializer = null) => SendResultAsync(uri, Method.Delete, serializer: serializer); public Task SendAsync(HttpRequestMessage request) => Policy.Handle() - .WaitAndRetryAsync(_options.Retries, r => TimeSpan.FromSeconds(Math.Pow(2, r))) + .WaitAndRetryAsync(_settings.Retries, r => TimeSpan.FromSeconds(Math.Pow(2, r))) .ExecuteAsync(() => _client.SendAsync(request)); - public Task SendAsync(HttpRequestMessage request, IHttpClientSerializer serializer = null) + public Task SendAsync(HttpRequestMessage request, IHttpClientSerializer? serializer = null) => Policy.Handle() - .WaitAndRetryAsync(_options.Retries, r => TimeSpan.FromSeconds(Math.Pow(2, r))) + .WaitAndRetryAsync(_settings.Retries, r => TimeSpan.FromSeconds(Math.Pow(2, r))) .ExecuteAsync(async () => { var response = await _client.SendAsync(request); if (!response.IsSuccessStatusCode) { - return default; + return default!; } var stream = await response.Content.ReadAsStreamAsync(); @@ -138,16 +135,15 @@ public Task SendAsync(HttpRequestMessage request, IHttpClientSerializer se return await DeserializeJsonFromStream(stream, serializer); }); - public Task> SendResultAsync(HttpRequestMessage request, - IHttpClientSerializer serializer = null) + public Task> SendResultAsync(HttpRequestMessage request, IHttpClientSerializer? serializer = null) => Policy.Handle() - .WaitAndRetryAsync(_options.Retries, r => TimeSpan.FromSeconds(Math.Pow(2, r))) + .WaitAndRetryAsync(_settings.Retries, r => TimeSpan.FromSeconds(Math.Pow(2, r))) .ExecuteAsync(async () => { var response = await _client.SendAsync(request); if (!response.IsSuccessStatusCode) { - return new HttpResult(default, response); + return new HttpResult(default!, response); } var stream = await response.Content.ReadAsStreamAsync(); @@ -176,13 +172,12 @@ public void SetHeaders(IDictionary headers) public void SetHeaders(Action headers) => headers?.Invoke(_client.DefaultRequestHeaders); - protected virtual async Task SendAsync(string uri, Method method, HttpContent content = null, - IHttpClientSerializer serializer = null) + protected virtual async Task SendAsync(string uri, Method method, HttpContent? content = null, IHttpClientSerializer? serializer = null) { var response = await SendAsync(uri, method, content); if (!response.IsSuccessStatusCode) { - return default; + return default!; } var stream = await response.Content.ReadAsStreamAsync(); @@ -190,13 +185,16 @@ protected virtual async Task SendAsync(string uri, Method method, HttpCont return await DeserializeJsonFromStream(stream, serializer); } - protected virtual async Task> SendResultAsync(string uri, Method method, - HttpContent content = null, IHttpClientSerializer serializer = null) + protected virtual async Task> SendResultAsync( + string uri, + Method method, + HttpContent? content = null, + IHttpClientSerializer? serializer = null) { var response = await SendAsync(uri, method, content); if (!response.IsSuccessStatusCode) { - return new HttpResult(default, response); + return new HttpResult(default!, response); } var stream = await response.Content.ReadAsStreamAsync(); @@ -205,18 +203,17 @@ protected virtual async Task> SendResultAsync(string uri, Metho return new HttpResult(result, response); } - protected virtual Task SendAsync(string uri, Method method, HttpContent content = null) + protected virtual Task SendAsync(string uri, Method method, HttpContent? content = null) => Policy.Handle() - .WaitAndRetryAsync(_options.Retries, r => TimeSpan.FromSeconds(Math.Pow(2, r))) + .WaitAndRetryAsync(_settings.Retries, r => TimeSpan.FromSeconds(Math.Pow(2, r))) .ExecuteAsync(() => { - var requestUri = uri.StartsWith("http") ? uri : $"http://{uri}"; + string requestUri = uri.StartsWith("http") ? uri : $"http://{uri}"; return GetResponseAsync(requestUri, method, content); }); - protected virtual Task GetResponseAsync(string uri, Method method, - HttpContent content = null) + protected virtual Task GetResponseAsync(string uri, Method method, HttpContent? content = null) => method switch { Method.Get => _client.GetAsync(uri), @@ -227,7 +224,7 @@ protected virtual Task GetResponseAsync(string uri, Method _ => throw new InvalidOperationException($"Unsupported HTTP method: {method}") }; - protected StringContent GetJsonPayload(object data, IHttpClientSerializer serializer = null) + protected StringContent? GetJsonPayload(object? data, IHttpClientSerializer? serializer = null) { if (data is null) { @@ -236,7 +233,7 @@ protected StringContent GetJsonPayload(object data, IHttpClientSerializer serial serializer ??= _serializer; var content = new StringContent(serializer.Serialize(data), Encoding.UTF8, JsonContentType); - if (_options.RemoveCharsetFromContentType && content.Headers.ContentType is not null) + if (_settings.RemoveCharsetFromContentType && content.Headers.ContentType is not null) { content.Headers.ContentType.CharSet = null; } @@ -244,11 +241,11 @@ protected StringContent GetJsonPayload(object data, IHttpClientSerializer serial return content; } - protected async Task DeserializeJsonFromStream(Stream stream, IHttpClientSerializer serializer = null) + protected async Task DeserializeJsonFromStream(Stream stream, IHttpClientSerializer? serializer = null) { if (stream is null || stream.CanRead is false) { - return default; + return default!; } serializer ??= _serializer; diff --git a/src/Genocs.HTTP/GenocsHttpLoggingFilter.cs b/src/Genocs.HTTP/GenocsHttpLoggingFilter.cs index 7d30997a..8ba7d583 100644 --- a/src/Genocs.HTTP/GenocsHttpLoggingFilter.cs +++ b/src/Genocs.HTTP/GenocsHttpLoggingFilter.cs @@ -1,4 +1,4 @@ -using Genocs.HTTP.Options; +using Genocs.HTTP.Configurations; using Microsoft.Extensions.Http; using Microsoft.Extensions.Logging; @@ -8,9 +8,9 @@ namespace Genocs.HTTP; internal sealed class GenocsHttpLoggingFilter : IHttpMessageHandlerBuilderFilter { private readonly ILoggerFactory _loggerFactory; - private readonly HttpClientSettings _options; + private readonly HttpClientOptions _options; - public GenocsHttpLoggingFilter(ILoggerFactory loggerFactory, HttpClientSettings options) + public GenocsHttpLoggingFilter(ILoggerFactory loggerFactory, HttpClientOptions options) { _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); _options = options; diff --git a/src/Genocs.HTTP/GenocsLoggingScopeHttpMessageHandler.cs b/src/Genocs.HTTP/GenocsLoggingScopeHttpMessageHandler.cs index 69231530..468ab49d 100644 --- a/src/Genocs.HTTP/GenocsLoggingScopeHttpMessageHandler.cs +++ b/src/Genocs.HTTP/GenocsLoggingScopeHttpMessageHandler.cs @@ -1,4 +1,4 @@ -using Genocs.HTTP.Options; +using Genocs.HTTP.Configurations; using Microsoft.Extensions.Logging; using System.Net; @@ -10,19 +10,18 @@ internal sealed class GenocsLoggingScopeHttpMessageHandler : DelegatingHandler private readonly HashSet _maskedRequestUrlParts; private readonly string _maskTemplate; - public GenocsLoggingScopeHttpMessageHandler(ILogger logger, HttpClientSettings options) + public GenocsLoggingScopeHttpMessageHandler(ILogger logger, HttpClientOptions settings) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _ = options ?? throw new ArgumentNullException(nameof(options)); + _ = settings ?? throw new ArgumentNullException(nameof(settings)); _maskedRequestUrlParts = - new HashSet(options.RequestMasking?.UrlParts ?? Enumerable.Empty()); - _maskTemplate = string.IsNullOrWhiteSpace(options.RequestMasking?.MaskTemplate) + new HashSet(settings.RequestMasking?.UrlParts ?? Enumerable.Empty()); + _maskTemplate = string.IsNullOrWhiteSpace(settings.RequestMasking?.MaskTemplate) ? "*****" - : options.RequestMasking.MaskTemplate; + : settings.RequestMasking.MaskTemplate; } - protected override async Task SendAsync(HttpRequestMessage request, - CancellationToken cancellationToken) + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request is null) { @@ -47,31 +46,30 @@ private static class EventIds public static readonly EventId PipelineEnd = new(101, "RequestPipelineEnd"); } - private static readonly Func _beginRequestPipelineScope = - LoggerMessage.DefineScope( - "HTTP {HttpMethod} {Uri}"); + private static readonly Func _beginRequestPipelineScope = + LoggerMessage.DefineScope("HTTP {HttpMethod} {Uri}"); private static readonly Action _requestPipelineStart = - LoggerMessage.Define( - LogLevel.Information, - EventIds.PipelineStart, - "Start processing HTTP request {HttpMethod} {Uri}"); - - private static readonly Action _requestPipelineEnd = - LoggerMessage.Define( - LogLevel.Information, - EventIds.PipelineEnd, - "End processing HTTP request - {StatusCode}"); - - public static IDisposable BeginRequestPipelineScope(ILogger logger, HttpRequestMessage request, - ISet maskedRequestUrlParts, string maskTemplate) + LoggerMessage.Define(LogLevel.Information, EventIds.PipelineStart, "Start processing HTTP request {HttpMethod} {Uri}"); + + private static readonly Action _requestPipelineEnd = + LoggerMessage.Define(LogLevel.Information, EventIds.PipelineEnd, "End processing HTTP request - {StatusCode}"); + + public static IDisposable BeginRequestPipelineScope( + ILogger logger, + HttpRequestMessage request, + ISet maskedRequestUrlParts, + string maskTemplate) { var uri = MaskUri(request.RequestUri, maskedRequestUrlParts, maskTemplate); return _beginRequestPipelineScope(logger, request.Method, uri); } - public static void RequestPipelineStart(ILogger logger, HttpRequestMessage request, - ISet maskedRequestUrlParts, string maskTemplate) + public static void RequestPipelineStart( + ILogger logger, + HttpRequestMessage request, + ISet maskedRequestUrlParts, + string maskTemplate) { var uri = MaskUri(request.RequestUri, maskedRequestUrlParts, maskTemplate); _requestPipelineStart(logger, request.Method, uri, null); @@ -82,16 +80,22 @@ public static void RequestPipelineEnd(ILogger logger, HttpResponseMessage respon _requestPipelineEnd(logger, response.StatusCode, null); } - private static Uri MaskUri(Uri uri, ISet maskedRequestUrlParts, string maskTemplate) + private static Uri? MaskUri(Uri? uri, ISet maskedRequestUrlParts, string maskTemplate) { if (!maskedRequestUrlParts.Any()) { return uri; } - var requestUri = uri.OriginalString; - var hasMatch = false; - foreach (var part in maskedRequestUrlParts) + string? requestUri = uri?.OriginalString; + + if (string.IsNullOrWhiteSpace(requestUri)) + { + return uri; + } + + bool hasMatch = false; + foreach (string part in maskedRequestUrlParts) { if (string.IsNullOrWhiteSpace(part)) { diff --git a/src/Genocs.HTTP/ICorrelationIdFactory.cs b/src/Genocs.HTTP/ICorrelationIdFactory.cs index d516564f..099e8075 100644 --- a/src/Genocs.HTTP/ICorrelationIdFactory.cs +++ b/src/Genocs.HTTP/ICorrelationIdFactory.cs @@ -1,12 +1,12 @@ namespace Genocs.HTTP; /// -/// Generic CorrelationId Factory interface +/// Generic CorrelationId Factory interface. /// public interface ICorrelationIdFactory { /// - /// Create a correlationId + /// Create a correlationId. /// /// string? Create(); diff --git a/src/Genocs.HTTP/IHttpClient.cs b/src/Genocs.HTTP/IHttpClient.cs index 9438b666..568bae1e 100644 --- a/src/Genocs.HTTP/IHttpClient.cs +++ b/src/Genocs.HTTP/IHttpClient.cs @@ -2,9 +2,8 @@ namespace Genocs.HTTP; - /// -/// The Genocs HTTP client +/// The Genocs HTTP client. /// public interface IHttpClient { @@ -36,23 +35,23 @@ public interface IHttpClient Task SendAsync(HttpRequestMessage request, IHttpClientSerializer? serializer = null); /// - /// Send the request and return the result + /// Send the request and return the result. /// - /// + /// The type to be send. /// /// /// Task> SendResultAsync(HttpRequestMessage request, IHttpClientSerializer? serializer = null); /// - /// Set the headers for the HTTP client + /// Set the headers for the HTTP client. /// - /// The headers dictionary + /// The headers dictionary. void SetHeaders(IDictionary headers); /// - /// Set the headers for the HTTP client + /// Set the headers for the HTTP client. /// - /// The headers action delegate + /// The headers action delegate. void SetHeaders(Action headers); } \ No newline at end of file diff --git a/src/Genocs.HTTP/IHttpClientSerializer.cs b/src/Genocs.HTTP/IHttpClientSerializer.cs index f95e6517..2bba0792 100644 --- a/src/Genocs.HTTP/IHttpClientSerializer.cs +++ b/src/Genocs.HTTP/IHttpClientSerializer.cs @@ -3,5 +3,5 @@ public interface IHttpClientSerializer { string Serialize(T value); - ValueTask DeserializeAsync(Stream stream); + ValueTask DeserializeAsync(Stream stream); } \ No newline at end of file diff --git a/src/Genocs.HTTP/Options/HttpClientSettings.cs b/src/Genocs.HTTP/Options/HttpClientSettings.cs deleted file mode 100644 index d42d35ef..00000000 --- a/src/Genocs.HTTP/Options/HttpClientSettings.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace Genocs.HTTP.Options; - -/// -/// The HttpClientSettings options class. -/// -public class HttpClientSettings -{ - /// - /// It defines if set consul as service discovery or fabio as load balancer. - /// Allowed values are: consul, fabio - /// - public string? Type { get; set; } - - /// - /// It defines the number of retries for each request. - /// - public int Retries { get; set; } - - /// - /// It defines the list of services to be registered. - /// - public IDictionary Services { get; set; } - public RequestMaskingSettings RequestMasking { get; set; } - public bool RemoveCharsetFromContentType { get; set; } - public string CorrelationContextHeader { get; set; } - public string CorrelationIdHeader { get; set; } - - public class RequestMaskingSettings - { - public bool Enabled { get; set; } - public IEnumerable UrlParts { get; set; } - public string MaskTemplate { get; set; } - } -} \ No newline at end of file diff --git a/src/Genocs.HTTP/README.md b/src/Genocs.HTTP/README_NUGET.md similarity index 82% rename from src/Genocs.HTTP/README.md rename to src/Genocs.HTTP/README_NUGET.md index 53efd407..3a471548 100644 --- a/src/Genocs.HTTP/README.md +++ b/src/Genocs.HTTP/README_NUGET.md @@ -33,7 +33,16 @@ Following are the project settings needed to enable monitoring ## Release notes -### [2023-03-12] 5.0.0-preview.5.0 +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 - Implemented MongoDB repository interfaces ### [2023-03-12] 5.0.0 diff --git a/src/Genocs.HTTP/SystemTextJsonHttpClientSerializer.cs b/src/Genocs.HTTP/SystemTextJsonHttpClientSerializer.cs index c66665e2..82e5e522 100644 --- a/src/Genocs.HTTP/SystemTextJsonHttpClientSerializer.cs +++ b/src/Genocs.HTTP/SystemTextJsonHttpClientSerializer.cs @@ -7,8 +7,9 @@ public class SystemTextJsonHttpClientSerializer : IHttpClientSerializer { private readonly JsonSerializerOptions _options; - public SystemTextJsonHttpClientSerializer(JsonSerializerOptions options = null) + public SystemTextJsonHttpClientSerializer(JsonSerializerOptions? options = null) { + // Default options _options = options ?? new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, @@ -18,7 +19,9 @@ public SystemTextJsonHttpClientSerializer(JsonSerializerOptions options = null) }; } - public string Serialize(T value) => JsonSerializer.Serialize(value, _options); + public string Serialize(T value) + => JsonSerializer.Serialize(value, _options); - public ValueTask DeserializeAsync(Stream stream) => JsonSerializer.DeserializeAsync(stream, _options); + public ValueTask DeserializeAsync(Stream stream) + => JsonSerializer.DeserializeAsync(stream, _options); } \ No newline at end of file diff --git a/src/Genocs.LoadBalancing.Fabio/Builders/FabioOptionsBuilder.cs b/src/Genocs.LoadBalancing.Fabio/Builders/FabioOptionsBuilder.cs index ec923b1d..ac786db6 100644 --- a/src/Genocs.LoadBalancing.Fabio/Builders/FabioOptionsBuilder.cs +++ b/src/Genocs.LoadBalancing.Fabio/Builders/FabioOptionsBuilder.cs @@ -1,50 +1,51 @@ -using Genocs.LoadBalancing.Fabio.Options; +using Genocs.LoadBalancing.Fabio.Configurations; namespace Genocs.LoadBalancing.Fabio.Builders; /// -/// The Fabio options builder +/// The Fabio options builder. /// public class FabioOptionsBuilder : IFabioOptionsBuilder { - private readonly FabioSettings _options = new(); + private readonly FabioOptions _settings = new(); /// - /// Enable or disable the Fabio load balancer + /// Enable or disable the Fabio load balancer. /// /// - /// The option builder to be used for chain the build + /// The option builder to be used for chain the build. public IFabioOptionsBuilder Enable(bool enabled) { - _options.Enabled = enabled; + _settings.Enabled = enabled; return this; } /// - /// Set the Fabio url + /// Set the Fabio url. /// - /// The url - /// The option builder to be used for chain the build + /// The url. + /// The option builder to be used for chain the build. public IFabioOptionsBuilder WithUrl(string url) { - _options.Url = url; + _settings.Url = url; return this; } /// - /// Set the Fabio service name + /// Set the Fabio service name. /// /// The service name - /// The option builder to be used for chain the build + /// The option builder to be used for chain the build. public IFabioOptionsBuilder WithService(string service) { - _options.Service = service; + _settings.Service = service; return this; } /// - /// Build the Fabio options + /// Build the Fabio options. /// - /// The Fabio options - public FabioSettings Build() => _options; + /// The Fabio options. + public FabioOptions Build() + => _settings; } \ No newline at end of file diff --git a/src/Genocs.LoadBalancing.Fabio/Options/FabioSettings.cs b/src/Genocs.LoadBalancing.Fabio/Configurations/FabioOptions.cs similarity index 61% rename from src/Genocs.LoadBalancing.Fabio/Options/FabioSettings.cs rename to src/Genocs.LoadBalancing.Fabio/Configurations/FabioOptions.cs index 8753d710..69b78ceb 100644 --- a/src/Genocs.LoadBalancing.Fabio/Options/FabioSettings.cs +++ b/src/Genocs.LoadBalancing.Fabio/Configurations/FabioOptions.cs @@ -1,22 +1,22 @@ -namespace Genocs.LoadBalancing.Fabio.Options; +namespace Genocs.LoadBalancing.Fabio.Configurations; /// -/// The Fabio settings +/// The Fabio settings. /// -public class FabioSettings +public class FabioOptions { /// - /// The default Fabio section name + /// Default section name. /// public const string Position = "fabio"; /// - /// Gets or sets a value indicating whether this is enabled. + /// It defines whether the section is enabled or not. /// public bool Enabled { get; set; } /// - /// + /// The service url. /// public string? Url { get; set; } diff --git a/src/Genocs.LoadBalancing.Fabio/IFabioOptionsBuilder.cs b/src/Genocs.LoadBalancing.Fabio/Configurations/IFabioOptionsBuilder.cs similarity index 55% rename from src/Genocs.LoadBalancing.Fabio/IFabioOptionsBuilder.cs rename to src/Genocs.LoadBalancing.Fabio/Configurations/IFabioOptionsBuilder.cs index 478c1cb3..e0f711fe 100644 --- a/src/Genocs.LoadBalancing.Fabio/IFabioOptionsBuilder.cs +++ b/src/Genocs.LoadBalancing.Fabio/Configurations/IFabioOptionsBuilder.cs @@ -1,36 +1,34 @@ -using Genocs.LoadBalancing.Fabio.Options; - -namespace Genocs.LoadBalancing.Fabio; +namespace Genocs.LoadBalancing.Fabio.Configurations; /// -/// The Fabio options builder interface definition +/// The Fabio options builder interface definition. /// public interface IFabioOptionsBuilder { /// - /// Enable or disable the Fabio load balancer + /// Enable or disable the Fabio load balancer. /// /// - /// The option builder to be used for chain the build + /// The option builder to be used for chain the build. IFabioOptionsBuilder Enable(bool enabled); /// - /// Set the Fabio url + /// Set the Fabio url. /// - /// The url - /// The option builder to be used for chain the build + /// The url. + /// The option builder to be used for chain the build. IFabioOptionsBuilder WithUrl(string url); /// - /// Set the Fabio service name + /// Set the Fabio service name. /// - /// The service name - /// The option builder to be used for chain the build + /// The service name. + /// The option builder to be used for chain the build. IFabioOptionsBuilder WithService(string service); /// - /// Build the Fabio options + /// Build the Fabio options. /// - /// The Fabio options - FabioSettings Build(); + /// The Fabio options. + FabioOptions Build(); } \ No newline at end of file diff --git a/src/Genocs.LoadBalancing.Fabio/Extensions.cs b/src/Genocs.LoadBalancing.Fabio/Extensions.cs index 96c2b1a3..ba3ded7a 100644 --- a/src/Genocs.LoadBalancing.Fabio/Extensions.cs +++ b/src/Genocs.LoadBalancing.Fabio/Extensions.cs @@ -1,13 +1,13 @@ using Genocs.Core.Builders; using Genocs.Discovery.Consul; +using Genocs.Discovery.Consul.Configurations; using Genocs.Discovery.Consul.Models; -using Genocs.Discovery.Consul.Options; using Genocs.HTTP; -using Genocs.HTTP.Options; +using Genocs.HTTP.Configurations; using Genocs.LoadBalancing.Fabio.Builders; +using Genocs.LoadBalancing.Fabio.Configurations; using Genocs.LoadBalancing.Fabio.Http; using Genocs.LoadBalancing.Fabio.MessageHandlers; -using Genocs.LoadBalancing.Fabio.Options; using Microsoft.Extensions.DependencyInjection; namespace Genocs.LoadBalancing.Fabio; @@ -16,37 +16,43 @@ public static class Extensions { private const string RegistryName = "loadBalancing.fabio"; - public static IGenocsBuilder AddFabio(this IGenocsBuilder builder, string sectionName = FabioSettings.Position, - string consulSectionName = "consul", string httpClientSectionName = "httpClient") + public static IGenocsBuilder AddFabio( + this IGenocsBuilder builder, + string sectionName = FabioOptions.Position, + string consulSectionName = ConsulOptions.Position, + string httpClientSectionName = HttpClientOptions.Position) { if (string.IsNullOrWhiteSpace(sectionName)) { - sectionName = FabioSettings.Position; + sectionName = FabioOptions.Position; } - var fabioOptions = builder.GetOptions(sectionName); - var consulOptions = builder.GetOptions(consulSectionName); - var httpClientOptions = builder.GetOptions(httpClientSectionName); - return builder.AddFabio(fabioOptions, httpClientOptions, - b => b.AddConsul(consulOptions, httpClientOptions)); + var fabioOptions = builder.GetOptions(sectionName); + var consulOptions = builder.GetOptions(consulSectionName); + var httpClientOptions = builder.GetOptions(httpClientSectionName); + + return builder.AddFabio( + fabioOptions, + httpClientOptions, + b => b.AddConsul(consulOptions, httpClientOptions)); } public static IGenocsBuilder AddFabio(this IGenocsBuilder builder, Func buildOptions, Func buildConsulOptions, - HttpClientSettings httpClientOptions) + HttpClientOptions httpClientOptions) { var fabioOptions = buildOptions(new FabioOptionsBuilder()).Build(); return builder.AddFabio(fabioOptions, httpClientOptions, b => b.AddConsul(buildConsulOptions, httpClientOptions)); } - public static IGenocsBuilder AddFabio(this IGenocsBuilder builder, FabioSettings fabioOptions, - ConsulSettings consulOptions, HttpClientSettings httpClientOptions) + public static IGenocsBuilder AddFabio(this IGenocsBuilder builder, FabioOptions fabioOptions, + ConsulOptions consulOptions, HttpClientOptions httpClientOptions) => builder.AddFabio(fabioOptions, httpClientOptions, b => b.AddConsul(consulOptions, httpClientOptions)); - private static IGenocsBuilder AddFabio(this IGenocsBuilder builder, FabioSettings fabioOptions, - HttpClientSettings httpClientOptions, Action registerConsul) + private static IGenocsBuilder AddFabio(this IGenocsBuilder builder, FabioOptions fabioOptions, + HttpClientOptions httpClientOptions, Action registerConsul) { registerConsul(builder); builder.Services.AddSingleton(fabioOptions); @@ -87,7 +93,7 @@ private static IGenocsBuilder AddFabio(this IGenocsBuilder builder, FabioSetting public static void AddFabioHttpClient(this IGenocsBuilder builder, string clientName, string serviceName) => builder.Services.AddHttpClient(clientName) - .AddHttpMessageHandler(c => new FabioMessageHandler(c.GetRequiredService(), serviceName)); + .AddHttpMessageHandler(c => new FabioMessageHandler(c.GetRequiredService(), serviceName)); private static void UpdateConsulRegistration(this IServiceCollection services, ServiceRegistration registration) @@ -99,7 +105,7 @@ private static void UpdateConsulRegistration(this IServiceCollection services, private static List GetFabioTags(string consulService, string fabioService) { - var service = (string.IsNullOrWhiteSpace(fabioService) ? consulService : fabioService) + string service = (string.IsNullOrWhiteSpace(fabioService) ? consulService : fabioService) .ToLowerInvariant(); return new List { $"urlprefix-/{service} strip=/{service}" }; diff --git a/src/Genocs.LoadBalancing.Fabio/Genocs.LoadBalancing.Fabio.csproj b/src/Genocs.LoadBalancing.Fabio/Genocs.LoadBalancing.Fabio.csproj index 295946a7..0f2c4d4b 100644 --- a/src/Genocs.LoadBalancing.Fabio/Genocs.LoadBalancing.Fabio.csproj +++ b/src/Genocs.LoadBalancing.Fabio/Genocs.LoadBalancing.Fabio.csproj @@ -1,58 +1,32 @@  - - net6.0;net7.0 - enable - enable - Genocs.LoadBalancing.Fabio - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The load balacer based on Fabio library useful to build .NET Core projects. - The load balacer based on Fabio library useful to build .NET Core projects. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.LoadBalancing.Fabio + Genocs.LoadBalancing.Fabio + Genocs.LoadBalancing.Fabio + The load balacer based on Fabio library useful to build .NET Core projects. + The load balacer based on Fabio library useful to build .NET Core projects. + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + - - - True - \ - - - True - \ - - - True - \ - - + + + + + - - - - - - - - - - - - + + + + + diff --git a/src/Genocs.LoadBalancing.Fabio/Http/FabioHttpClient.cs b/src/Genocs.LoadBalancing.Fabio/Http/FabioHttpClient.cs index cb622682..f3517a66 100644 --- a/src/Genocs.LoadBalancing.Fabio/Http/FabioHttpClient.cs +++ b/src/Genocs.LoadBalancing.Fabio/Http/FabioHttpClient.cs @@ -1,12 +1,16 @@ using Genocs.HTTP; -using Genocs.HTTP.Options; +using Genocs.HTTP.Configurations; namespace Genocs.LoadBalancing.Fabio.Http; internal sealed class FabioHttpClient : GenocsHttpClient, IFabioHttpClient { - public FabioHttpClient(HttpClient client, HttpClientSettings options, IHttpClientSerializer serializer, - ICorrelationContextFactory correlationContextFactory, ICorrelationIdFactory correlationIdFactory) + public FabioHttpClient( + HttpClient client, + HttpClientOptions options, + IHttpClientSerializer serializer, + ICorrelationContextFactory correlationContextFactory, + ICorrelationIdFactory correlationIdFactory) : base(client, options, serializer, correlationContextFactory, correlationIdFactory) { } diff --git a/src/Genocs.LoadBalancing.Fabio/MessageHandlers/FabioMessageHandler.cs b/src/Genocs.LoadBalancing.Fabio/MessageHandlers/FabioMessageHandler.cs index 416fb18a..6a735170 100644 --- a/src/Genocs.LoadBalancing.Fabio/MessageHandlers/FabioMessageHandler.cs +++ b/src/Genocs.LoadBalancing.Fabio/MessageHandlers/FabioMessageHandler.cs @@ -1,27 +1,28 @@ -using Genocs.LoadBalancing.Fabio.Options; +using Genocs.LoadBalancing.Fabio.Configurations; namespace Genocs.LoadBalancing.Fabio.MessageHandlers; internal sealed class FabioMessageHandler : DelegatingHandler { - private readonly FabioSettings _options; + private readonly FabioOptions _settings; private readonly string _servicePath; - public FabioMessageHandler(FabioSettings options, string? serviceName = null) + public FabioMessageHandler(FabioOptions settings, string? serviceName = null) { - if (string.IsNullOrWhiteSpace(options.Url)) + if (string.IsNullOrWhiteSpace(settings.Url)) { throw new InvalidOperationException("Fabio URL was not provided."); } - _options = options; + _settings = settings; _servicePath = string.IsNullOrWhiteSpace(serviceName) ? string.Empty : $"{serviceName}/"; } - protected override Task SendAsync(HttpRequestMessage request, - CancellationToken cancellationToken) + protected override Task SendAsync( + HttpRequestMessage request, + CancellationToken cancellationToken) { - if (!_options.Enabled) + if (!_settings.Enabled) { return base.SendAsync(request, cancellationToken); } @@ -32,5 +33,5 @@ protected override Task SendAsync(HttpRequestMessage reques } private Uri GetRequestUri(HttpRequestMessage request) - => new($"{_options.Url}/{_servicePath}{request.RequestUri.Host}{request.RequestUri.PathAndQuery}"); + => new($"{_settings.Url}/{_servicePath}{request.RequestUri?.Host}{request.RequestUri?.PathAndQuery}"); } \ No newline at end of file diff --git a/src/Genocs.Discovery.Consul/README.md b/src/Genocs.LoadBalancing.Fabio/README_NUGET.md similarity index 60% rename from src/Genocs.Discovery.Consul/README.md rename to src/Genocs.LoadBalancing.Fabio/README_NUGET.md index 945bb03b..6ffbc96c 100644 --- a/src/Genocs.Discovery.Consul/README.md +++ b/src/Genocs.LoadBalancing.Fabio/README_NUGET.md @@ -15,7 +15,35 @@ Please check the GitHub repository getting more info. ## Release notes -### [2023-03-12] 5.0.0-preview.5.0 +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 - Implemented MongoDB repository interfaces ### [2023-03-12] 5.0.0 diff --git a/src/Genocs.Logging/CQRS/Decorators/CommandHandlerLoggingDecorator.cs b/src/Genocs.Logging/CQRS/Decorators/CommandHandlerLoggingDecorator.cs index 2b9b9f18..2c0182a0 100644 --- a/src/Genocs.Logging/CQRS/Decorators/CommandHandlerLoggingDecorator.cs +++ b/src/Genocs.Logging/CQRS/Decorators/CommandHandlerLoggingDecorator.cs @@ -66,6 +66,7 @@ private void Log(TCommand command, string message, bool isError = false) private class EmptyMessageToLogTemplateMapper : IMessageToLogTemplateMapper { - public HandlerLogTemplate? Map(TMessage message) where TMessage : class => null; + public HandlerLogTemplate? Map(TMessage message) + where TMessage : class => null; } } \ No newline at end of file diff --git a/src/Genocs.Logging/CQRS/Extensions.cs b/src/Genocs.Logging/CQRS/Extensions.cs index 4a76a8bc..05eed6e0 100644 --- a/src/Genocs.Logging/CQRS/Extensions.cs +++ b/src/Genocs.Logging/CQRS/Extensions.cs @@ -11,14 +11,13 @@ namespace Genocs.Logging.CQRS; public static class Extensions { - public static IGenocsBuilder AddCommandHandlersLogging(this IGenocsBuilder builder, Assembly assembly = null) + public static IGenocsBuilder AddCommandHandlersLogging(this IGenocsBuilder builder, Assembly? assembly = null) => builder.AddHandlerLogging(typeof(ICommandHandler<>), typeof(CommandHandlerLoggingDecorator<>), assembly); - public static IGenocsBuilder AddEventHandlersLogging(this IGenocsBuilder builder, Assembly assembly = null) + public static IGenocsBuilder AddEventHandlersLogging(this IGenocsBuilder builder, Assembly? assembly = null) => builder.AddHandlerLogging(typeof(IEventHandler<>), typeof(EventHandlerLoggingDecorator<>), assembly); - private static IGenocsBuilder AddHandlerLogging(this IGenocsBuilder builder, Type handlerType, - Type decoratorType, Assembly? assembly = null) + private static IGenocsBuilder AddHandlerLogging(this IGenocsBuilder builder, Type handlerType, Type decoratorType, Assembly? assembly = null) { assembly ??= Assembly.GetCallingAssembly(); diff --git a/src/Genocs.Logging/CQRS/HandlerLogTemplate.cs b/src/Genocs.Logging/CQRS/HandlerLogTemplate.cs index 12ac2957..757cab15 100644 --- a/src/Genocs.Logging/CQRS/HandlerLogTemplate.cs +++ b/src/Genocs.Logging/CQRS/HandlerLogTemplate.cs @@ -15,6 +15,6 @@ public sealed class HandlerLogTemplate return null; } - return OnError.TryGetValue(exceptionType, out var template) ? template : null; + return OnError.TryGetValue(exceptionType, out string? template) ? template : null; } } \ No newline at end of file diff --git a/src/Genocs.Logging/CQRS/IMessageToLogTemplateMapper.cs b/src/Genocs.Logging/CQRS/IMessageToLogTemplateMapper.cs index 8785149a..cb71eb87 100644 --- a/src/Genocs.Logging/CQRS/IMessageToLogTemplateMapper.cs +++ b/src/Genocs.Logging/CQRS/IMessageToLogTemplateMapper.cs @@ -1,15 +1,16 @@ namespace Genocs.Logging.CQRS; /// -/// Interface used to log messages using a template +/// Interface used to log messages using a template. /// public interface IMessageToLogTemplateMapper { /// - /// Map the message using the template + /// Map the message using the template. /// - /// The type of the message - /// The message instance - /// The LogTemplate - HandlerLogTemplate? Map(TMessage message) where TMessage : class; + /// The type of the message. + /// The message instance. + /// The LogTemplate. + HandlerLogTemplate? Map(TMessage message) + where TMessage : class; } \ No newline at end of file diff --git a/src/Genocs.Logging/Configurations/AzureOptions.cs b/src/Genocs.Logging/Configurations/AzureOptions.cs new file mode 100644 index 00000000..dc2f326a --- /dev/null +++ b/src/Genocs.Logging/Configurations/AzureOptions.cs @@ -0,0 +1,17 @@ +namespace Genocs.Logging.Configurations; + +/// +/// Azure application insights logging settings. +/// +public class AzureOptions +{ + /// + /// It define whether the Azure application insights logger and tracing are enabled or not. + /// + public bool Enabled { get; set; } + + /// + /// The Azure application insights connection string. + /// + public string? ConnectionString { get; set; } +} \ No newline at end of file diff --git a/src/Genocs.Logging/Options/ConsoleSettings.cs b/src/Genocs.Logging/Configurations/ConsoleOptions.cs similarity index 62% rename from src/Genocs.Logging/Options/ConsoleSettings.cs rename to src/Genocs.Logging/Configurations/ConsoleOptions.cs index b41102ff..4289e516 100644 --- a/src/Genocs.Logging/Options/ConsoleSettings.cs +++ b/src/Genocs.Logging/Configurations/ConsoleOptions.cs @@ -1,12 +1,12 @@ -namespace Genocs.Logging.Options; +namespace Genocs.Logging.Configurations; /// -/// Console Settings +/// Console Settings. /// -public class ConsoleSettings +public class ConsoleOptions { /// - /// It define whether the console logger and tracing are enabled or not + /// It define whether the console logger and tracing are enabled or not. /// public bool Enabled { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Logging/Configurations/ElkOptions.cs b/src/Genocs.Logging/Configurations/ElkOptions.cs new file mode 100644 index 00000000..b090e0ed --- /dev/null +++ b/src/Genocs.Logging/Configurations/ElkOptions.cs @@ -0,0 +1,25 @@ +namespace Genocs.Logging.Configurations; + +/// +/// Elasticsearch Settings. +/// +public class ElkOptions +{ + /// + /// It define whether the Elasticsearch logger and tracing are enabled or not. + /// + public bool Enabled { get; set; } + + /// + /// It define whether the Elasticsearch authentication is enabled or not. + /// + public bool BasicAuthEnabled { get; set; } + + /// + /// The Elasticsearch Url. + /// + public string? Url { get; set; } + public string? Username { get; set; } + public string? Password { get; set; } + public string? IndexFormat { get; set; } +} \ No newline at end of file diff --git a/src/Genocs.Logging/Configurations/LocalFileOptions.cs b/src/Genocs.Logging/Configurations/LocalFileOptions.cs new file mode 100644 index 00000000..5b956ae1 --- /dev/null +++ b/src/Genocs.Logging/Configurations/LocalFileOptions.cs @@ -0,0 +1,22 @@ +namespace Genocs.Logging.Configurations; + +/// +/// File settings for local file logging. +/// +public class LocalFileOptions +{ + /// + /// If enabled, local file logging is enabled. + /// + public bool Enabled { get; set; } + + /// + /// The path to the local file. + /// + public string? Path { get; set; } + + /// + /// The interval to roll the file. it uses the same values as Serilog.Sinks.File. + /// + public string? Interval { get; set; } +} \ No newline at end of file diff --git a/src/Genocs.Logging/Configurations/LoggerOptions.cs b/src/Genocs.Logging/Configurations/LoggerOptions.cs new file mode 100644 index 00000000..59afbad2 --- /dev/null +++ b/src/Genocs.Logging/Configurations/LoggerOptions.cs @@ -0,0 +1,47 @@ +namespace Genocs.Logging.Configurations; + +/// +/// Logger Settings. +/// +public class LoggerOptions +{ + /// + /// Default section name. + /// + public const string Position = "logger"; + + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + public string? Level { get; set; } + + /// + /// The Console Logging and tracing Settings. + /// + public ConsoleOptions? Console { get; set; } + public LocalFileOptions? File { get; set; } + public ElkOptions? Elk { get; set; } + public SeqOptions? Seq { get; set; } + + /// + /// Loki logging settings. + /// + public LokiOptions? Loki { get; set; } + + /// + /// Azure application insights logging settings. + /// + public AzureOptions? Azure { get; set; } + + /// + /// MongoDb logging settings. + /// + public MongoOptions? Mongo { get; set; } + + public IDictionary? MinimumLevelOverrides { get; set; } + public IEnumerable? ExcludePaths { get; set; } + public IEnumerable? ExcludeProperties { get; set; } + public IDictionary? Tags { get; set; } +} \ No newline at end of file diff --git a/src/Genocs.Logging/Options/LokiSettings.cs b/src/Genocs.Logging/Configurations/LokiOptions.cs similarity index 82% rename from src/Genocs.Logging/Options/LokiSettings.cs rename to src/Genocs.Logging/Configurations/LokiOptions.cs index 264f3876..0910a170 100644 --- a/src/Genocs.Logging/Options/LokiSettings.cs +++ b/src/Genocs.Logging/Configurations/LokiOptions.cs @@ -1,7 +1,7 @@ -namespace Genocs.Logging.Options; +namespace Genocs.Logging.Configurations; /// -/// Relevant options for the Serilog Loki sink +/// Relevant options for the Serilog Loki sink. /// /// Not all options have been included as some have been covered by Genocs.Logging already. /// The sink has reasonable defaults for the unused options. @@ -22,38 +22,42 @@ /// TextFormatter /// HttpClient /// CreateLevelLabel -/// -/// +/// /// -public class LokiSettings +public class LokiOptions { /// - /// Whether or not to enable Loki Logging + /// Whether or not to enable Loki Logging. /// public bool Enabled { get; set; } /// - /// The Uri at which the Loki instance can be found + /// The Uri at which the Loki instance can be found. /// public string? Url { get; set; } + /// /// The maximum number of events to post in a single batch. Default value is 1000. /// public int? BatchPostingLimit { get; set; } + /// - /// The maximum number of events stored in the queue in memory, waiting to be posted over + /// The maximum number of events stored in the queue in memory, waiting to be posted over. /// public int? QueueLimit { get; set; } + /// /// The time to wait between checking for event batches. Default value is 2 seconds. /// public TimeSpan? Period { get; set; } + /// - /// Username used for Grafana Loki authorization + /// Username used for Grafana Loki authorization. /// public string? LokiUsername { get; set; } + /// - /// Password used for Grafana Loki authorization + /// Password used for Grafana Loki authorization. /// public string? LokiPassword { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Logging/Configurations/MongoOptions.cs b/src/Genocs.Logging/Configurations/MongoOptions.cs new file mode 100644 index 00000000..c2750e18 --- /dev/null +++ b/src/Genocs.Logging/Configurations/MongoOptions.cs @@ -0,0 +1,12 @@ +namespace Genocs.Logging.Configurations; + +/// +/// MongoDb logging settings. +/// +public class MongoOptions +{ + /// + /// It define whether the MongoDb logger and tracing are enabled or not. + /// + public bool Enabled { get; set; } +} \ No newline at end of file diff --git a/src/Genocs.Logging/Configurations/SeqOptions.cs b/src/Genocs.Logging/Configurations/SeqOptions.cs new file mode 100644 index 00000000..7e20ccb1 --- /dev/null +++ b/src/Genocs.Logging/Configurations/SeqOptions.cs @@ -0,0 +1,22 @@ +namespace Genocs.Logging.Configurations; + +/// +/// Seq Settings. +/// +public class SeqOptions +{ + /// + /// It define whether the Seq logger and tracing are enabled or not. + /// + public bool Enabled { get; set; } + + /// + /// The Seq Url. + /// + public string? Url { get; set; } + + /// + /// The Seq ApiKey. + /// + public string? ApiKey { get; set; } +} \ No newline at end of file diff --git a/src/Genocs.Logging/Extensions.cs b/src/Genocs.Logging/Extensions.cs index 9654982f..41997920 100644 --- a/src/Genocs.Logging/Extensions.cs +++ b/src/Genocs.Logging/Extensions.cs @@ -1,6 +1,6 @@ -using Genocs.Common.Options; +using Genocs.Common.Configurations; using Genocs.Core.Builders; -using Genocs.Logging.Options; +using Genocs.Logging.Configurations; using Microsoft.ApplicationInsights.Extensibility; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; @@ -20,38 +20,42 @@ public static class Extensions { internal static LoggingLevelSwitch LoggingLevelSwitch = new(); - public static IHostBuilder UseLogging(this IHostBuilder hostBuilder, - Action? configure = null, - string? loggerSectionName = null, - string? appSectionName = null) + public static IHostBuilder UseLogging( + this IHostBuilder hostBuilder, + Action? configure = null, + string? loggerSectionName = LoggerOptions.Position, + string? appSectionName = AppOptions.Position) => hostBuilder .ConfigureServices(services => services.AddSingleton()) .UseSerilog((context, loggerConfiguration) => { if (string.IsNullOrWhiteSpace(loggerSectionName)) { - loggerSectionName = LoggerSettings.Position; + loggerSectionName = LoggerOptions.Position; } if (string.IsNullOrWhiteSpace(appSectionName)) { - appSectionName = AppSettings.Position; + appSectionName = AppOptions.Position; } - var loggerOptions = context.Configuration.GetOptions(loggerSectionName); - var appOptions = context.Configuration.GetOptions(appSectionName); + var loggerOptions = context.Configuration.GetOptions(loggerSectionName); + var appOptions = context.Configuration.GetOptions(appSectionName); MapOptions(loggerOptions, appOptions, loggerConfiguration, context.HostingEnvironment.EnvironmentName); configure?.Invoke(context, loggerConfiguration); }); - - public static IEndpointConventionBuilder MapLogLevelHandler(this IEndpointRouteBuilder builder, - string endpointRoute = "~/logging/level") + public static IEndpointConventionBuilder MapLogLevelHandler( + this IEndpointRouteBuilder builder, + string endpointRoute = "~/logging/level") => builder.MapPost(endpointRoute, LevelSwitch); - private static void MapOptions(LoggerSettings loggerOptions, AppSettings appOptions, - LoggerConfiguration loggerConfiguration, string environmentName) + private static void MapOptions( + LoggerOptions loggerOptions, + AppOptions appOptions, + LoggerConfiguration loggerConfiguration, + string environmentName) { LoggingLevelSwitch.MinimumLevel = GetLogEventLevel(loggerOptions.Level); @@ -82,14 +86,14 @@ private static void MapOptions(LoggerSettings loggerOptions, AppSettings appOpti Configure(loggerConfiguration, loggerOptions); } - private static void Configure(LoggerConfiguration loggerConfiguration, LoggerSettings options) + private static void Configure(LoggerConfiguration loggerConfiguration, LoggerOptions options) { - var consoleOptions = options.Console ?? new ConsoleSettings(); - var fileOptions = options.File ?? new LocalFileSettings(); - var elkOptions = options.Elk ?? new ElkSettings(); - var seqOptions = options.Seq ?? new SeqSettings(); - var lokiOptions = options.Loki ?? new LokiSettings(); - var azureOptions = options.Azure ?? new AzureSettings(); + var consoleOptions = options.Console ?? new ConsoleOptions(); + var fileOptions = options.File ?? new LocalFileOptions(); + var elkOptions = options.Elk ?? new ElkOptions(); + var seqOptions = options.Seq ?? new SeqOptions(); + var lokiOptions = options.Loki ?? new LokiOptions(); + var azureOptions = options.Azure ?? new AzureOptions(); // console if (consoleOptions.Enabled) @@ -100,7 +104,7 @@ private static void Configure(LoggerConfiguration loggerConfiguration, LoggerSet // local file system if (fileOptions.Enabled) { - var path = string.IsNullOrWhiteSpace(fileOptions.Path) ? "logs/logs.txt" : fileOptions.Path; + string path = string.IsNullOrWhiteSpace(fileOptions.Path) ? "logs/logs.txt" : fileOptions.Path; if (!Enum.TryParse(fileOptions.Interval, true, out var interval)) { interval = RollingInterval.Day; @@ -129,7 +133,7 @@ private static void Configure(LoggerConfiguration loggerConfiguration, LoggerSet // seq if (seqOptions.Enabled) { - loggerConfiguration.WriteTo.Seq(seqOptions.Url, apiKey: seqOptions.ApiKey); + loggerConfiguration.WriteTo.Seq(seqOptions.Url!, apiKey: seqOptions.ApiKey); } // loki @@ -163,10 +167,12 @@ private static void Configure(LoggerConfiguration loggerConfiguration, LoggerSet // azure application insights if (azureOptions.Enabled) { - loggerConfiguration.WriteTo.ApplicationInsights(new TelemetryConfiguration - { - ConnectionString = azureOptions.ConnectionString, - }, TelemetryConverter.Traces); + loggerConfiguration.WriteTo.ApplicationInsights( + new TelemetryConfiguration + { + ConnectionString = azureOptions.ConnectionString, + }, + TelemetryConverter.Traces); } } @@ -199,7 +205,7 @@ private static async Task LevelSwitch(HttpContext context) return; } - var level = context.Request.Query["level"].ToString(); + string level = context.Request.Query["level"].ToString(); if (string.IsNullOrEmpty(level)) { diff --git a/src/Genocs.Logging/Genocs.Logging.csproj b/src/Genocs.Logging/Genocs.Logging.csproj index 04079462..68b271d5 100644 --- a/src/Genocs.Logging/Genocs.Logging.csproj +++ b/src/Genocs.Logging/Genocs.Logging.csproj @@ -1,67 +1,42 @@  - - net6.0;net7.0 - enable - enable - Genocs.Logging - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The logging library useful to build .NET Core projects. - The logging library useful to build .NET Core projects. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.Logging + Genocs.Logging + Genocs.Logging + The logging library useful to build .NET Core projects. + The logging library useful to build .NET Core projects. + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + + + + + - - - True - \ - - - True - \ - - - True - \ - - + + + - - - + + + + + + + + + - - - - - - - - - - - - - - - - - + + + diff --git a/src/Genocs.Logging/LoggingService.cs b/src/Genocs.Logging/LoggingService.cs index c9b6b3f8..bd535b59 100644 --- a/src/Genocs.Logging/LoggingService.cs +++ b/src/Genocs.Logging/LoggingService.cs @@ -6,5 +6,6 @@ public void SetLoggingLevel(string logEventLevel) => Extensions.LoggingLevelSwitch.MinimumLevel = Extensions.GetLogEventLevel(logEventLevel); } - -public class LoggingService : ILoggingService { } \ No newline at end of file +public class LoggingService : ILoggingService +{ +} \ No newline at end of file diff --git a/src/Genocs.Logging/Options/AzureSettings.cs b/src/Genocs.Logging/Options/AzureSettings.cs deleted file mode 100644 index ae3d43ec..00000000 --- a/src/Genocs.Logging/Options/AzureSettings.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Genocs.Logging.Options; - - -/// -/// Azure application insights logging settings -/// -public class AzureSettings -{ - public bool Enabled { get; set; } - public string? ConnectionString { get; set; } -} \ No newline at end of file diff --git a/src/Genocs.Logging/Options/ElkSettings.cs b/src/Genocs.Logging/Options/ElkSettings.cs deleted file mode 100644 index ce911d24..00000000 --- a/src/Genocs.Logging/Options/ElkSettings.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Genocs.Logging.Options; - -public class ElkSettings -{ - public bool Enabled { get; set; } - public bool BasicAuthEnabled { get; set; } - public string? Url { get; set; } - public string? Username { get; set; } - public string? Password { get; set; } - public string? IndexFormat { get; set; } -} \ No newline at end of file diff --git a/src/Genocs.Logging/Options/LocalFileSettings.cs b/src/Genocs.Logging/Options/LocalFileSettings.cs deleted file mode 100644 index 9d362ff9..00000000 --- a/src/Genocs.Logging/Options/LocalFileSettings.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Genocs.Logging.Options; - -public class LocalFileSettings -{ - public bool Enabled { get; set; } - public string? Path { get; set; } - public string? Interval { get; set; } -} \ No newline at end of file diff --git a/src/Genocs.Logging/Options/LoggerSettings.cs b/src/Genocs.Logging/Options/LoggerSettings.cs deleted file mode 100644 index 8d69c4e6..00000000 --- a/src/Genocs.Logging/Options/LoggerSettings.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Genocs.Logging.Options; - - -/// -/// Logger Settings -/// -public class LoggerSettings -{ - /// - /// Default section name - /// - public const string Position = "logger"; - - public string? Level { get; set; } - - /// - /// The Console Logging and tracing Settings - /// - public ConsoleSettings? Console { get; set; } - public LocalFileSettings? File { get; set; } - public ElkSettings? Elk { get; set; } - public SeqSettings? Seq { get; set; } - public LokiSettings? Loki { get; set; } - public AzureSettings? Azure { get; set; } - - public IDictionary? MinimumLevelOverrides { get; set; } - public IEnumerable? ExcludePaths { get; set; } - public IEnumerable? ExcludeProperties { get; set; } - public IDictionary? Tags { get; set; } -} \ No newline at end of file diff --git a/src/Genocs.Logging/Options/SeqSettings.cs b/src/Genocs.Logging/Options/SeqSettings.cs deleted file mode 100644 index 68512b91..00000000 --- a/src/Genocs.Logging/Options/SeqSettings.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Genocs.Logging.Options; - -public class SeqSettings -{ - public bool Enabled { get; set; } - public string Url { get; set; } - public string ApiKey { get; set; } -} \ No newline at end of file diff --git a/src/Genocs.Logging/README.md b/src/Genocs.Logging/README_NUGET.md similarity index 65% rename from src/Genocs.Logging/README.md rename to src/Genocs.Logging/README_NUGET.md index 4fd1372a..3c23f07b 100644 --- a/src/Genocs.Logging/README.md +++ b/src/Genocs.Logging/README_NUGET.md @@ -22,7 +22,35 @@ Following are the project settings needed to enable monitoring ## Release notes -### [2023-03-12] 5.0.0-preview.5.0 +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 - Implemented MongoDB repository interfaces ### [2023-03-12] 5.0.0 diff --git a/src/Genocs.Logging/Startup.cs b/src/Genocs.Logging/Startup.cs new file mode 100644 index 00000000..d12034a6 --- /dev/null +++ b/src/Genocs.Logging/Startup.cs @@ -0,0 +1,29 @@ +using Serilog.Events; +using Serilog; + +namespace Genocs.Logging; + +/// +/// This helper function ensures that the logger is initialized. +/// +public static class StaticLogger +{ + /// + /// This helper function ensures that the logger is initialized. + /// Call this function before using the logger. + /// Generally in the Program.cs file at the beginning of the application. + /// + public static void EnsureInitialized() + { + if (Log.Logger is not Serilog.Core.Logger) + { + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .MinimumLevel.Override("MassTransit", LogEventLevel.Information) + .Enrich.FromLogContext() + .WriteTo.Console() + .CreateLogger(); + } + } +} diff --git a/src/Genocs.MessageBrokers.Outbox.MongoDB/Extensions.cs b/src/Genocs.MessageBrokers.Outbox.MongoDB/Extensions.cs index 4206a9c3..b59a4a51 100644 --- a/src/Genocs.MessageBrokers.Outbox.MongoDB/Extensions.cs +++ b/src/Genocs.MessageBrokers.Outbox.MongoDB/Extensions.cs @@ -13,11 +13,11 @@ public static IMessageOutboxConfigurator AddMongo(this IMessageOutboxConfigurato var builder = configurator.Builder; var options = configurator.Options; - var inboxCollection = string.IsNullOrWhiteSpace(options.InboxCollection) + string inboxCollection = string.IsNullOrWhiteSpace(options.InboxCollection) ? "inbox" : options.InboxCollection; - var outboxCollection = string.IsNullOrWhiteSpace(options.OutboxCollection) + string outboxCollection = string.IsNullOrWhiteSpace(options.OutboxCollection) ? "outbox" : options.OutboxCollection; diff --git a/src/Genocs.MessageBrokers.Outbox.MongoDB/Genocs.MessageBrokers.Outbox.MongoDB.csproj b/src/Genocs.MessageBrokers.Outbox.MongoDB/Genocs.MessageBrokers.Outbox.MongoDB.csproj index 8ef8a32e..55003bab 100644 --- a/src/Genocs.MessageBrokers.Outbox.MongoDB/Genocs.MessageBrokers.Outbox.MongoDB.csproj +++ b/src/Genocs.MessageBrokers.Outbox.MongoDB/Genocs.MessageBrokers.Outbox.MongoDB.csproj @@ -1,54 +1,30 @@  - - net6.0;net7.0 - enable - enable - Genocs.MessageBrokers.Outbox.MongoDB - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The message brokers outbox MongoDB interface library useful to build .NET Core projects. - The message brokers MongoDB outbox interface library useful to build .NET Core projects. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.MessageBrokers.Outbox.MongoDB + Genocs.MessageBrokers.Outbox.MongoDB + Genocs.MessageBrokers.Outbox.MongoDB + The message brokers outbox MongoDB interface library useful to build .NET Core projects. + The message brokers MongoDB outbox interface library useful to build .NET Core projects. + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + - - - True - \ - - - True - \ - - - True - \ - - + + + + - - - - + + + + - - - - diff --git a/src/Genocs.MessageBrokers.Outbox.MongoDB/Internals/MongoMessageOutbox.cs b/src/Genocs.MessageBrokers.Outbox.MongoDB/Internals/MongoMessageOutbox.cs index 86ddfc79..cc3c8bd3 100644 --- a/src/Genocs.MessageBrokers.Outbox.MongoDB/Internals/MongoMessageOutbox.cs +++ b/src/Genocs.MessageBrokers.Outbox.MongoDB/Internals/MongoMessageOutbox.cs @@ -1,5 +1,6 @@ +using Genocs.MessageBrokers.Outbox.Configurations; using Genocs.MessageBrokers.Outbox.Messages; -using Genocs.MessageBrokers.Outbox.Options; +using Genocs.Persistence.MongoDb.Domain.Repositories; using Genocs.Persistence.MongoDb.Repositories; using Microsoft.Extensions.Logging; using MongoDB.Driver; @@ -10,6 +11,8 @@ namespace Genocs.MessageBrokers.Outbox.MongoDB.Internals; internal sealed class MongoMessageOutbox : IMessageOutbox, IMessageOutboxAccessor { + private const string EmptyJsonObject = "{}"; + private static readonly JsonSerializerOptions SerializerOptions = new() { PropertyNameCaseInsensitive = true, @@ -18,19 +21,20 @@ internal sealed class MongoMessageOutbox : IMessageOutbox, IMessageOutboxAccesso Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) } }; - private const string EmptyJsonObject = "{}"; private readonly IMongoSessionFactory _sessionFactory; - private readonly IMongoRepository _inboxRepository; - private readonly IMongoRepository _outboxRepository; + private readonly IMongoDbBaseRepository _inboxRepository; + private readonly IMongoDbBaseRepository _outboxRepository; private readonly ILogger _logger; private readonly bool _transactionsEnabled; public bool Enabled { get; } - public MongoMessageOutbox(IMongoSessionFactory sessionFactory, - IMongoRepository inboxRepository, - IMongoRepository outboxRepository, - OutboxSettings options, ILogger logger) + public MongoMessageOutbox( + IMongoSessionFactory sessionFactory, + IMongoDbBaseRepository inboxRepository, + IMongoDbBaseRepository outboxRepository, + OutboxOptions options, + ILogger logger) { _sessionFactory = sessionFactory; _inboxRepository = inboxRepository; @@ -100,9 +104,15 @@ await _inboxRepository.AddAsync(new InboxMessage } } - public async Task SendAsync(T message, string originatedMessageId = null, string messageId = null, - string correlationId = null, string spanContext = null, object messageContext = null, - IDictionary headers = null) where T : class + public async Task SendAsync( + T message, + string? originatedMessageId = null, + string? messageId = null, + string? correlationId = null, + string? spanContext = null, + object? messageContext = null, + IDictionary? headers = null) + where T : class { if (!Enabled) { diff --git a/src/Genocs.MessageBrokers.Outbox.MongoDB/Internals/MongoOutboxInitializer.cs b/src/Genocs.MessageBrokers.Outbox.MongoDB/Internals/MongoOutboxInitializer.cs index 05d67b59..ea69c74b 100644 --- a/src/Genocs.MessageBrokers.Outbox.MongoDB/Internals/MongoOutboxInitializer.cs +++ b/src/Genocs.MessageBrokers.Outbox.MongoDB/Internals/MongoOutboxInitializer.cs @@ -1,6 +1,6 @@ using Genocs.Common.Types; +using Genocs.MessageBrokers.Outbox.Configurations; using Genocs.MessageBrokers.Outbox.Messages; -using Genocs.MessageBrokers.Outbox.Options; using MongoDB.Driver; namespace Genocs.MessageBrokers.Outbox.MongoDB.Internals; @@ -8,9 +8,9 @@ namespace Genocs.MessageBrokers.Outbox.MongoDB.Internals; internal sealed class MongoOutboxInitializer : IInitializer { private readonly IMongoDatabase _database; - private readonly OutboxSettings _options; + private readonly OutboxOptions _options; - public MongoOutboxInitializer(IMongoDatabase database, OutboxSettings options) + public MongoOutboxInitializer(IMongoDatabase database, OutboxOptions options) { _database = database; _options = options; @@ -28,30 +28,33 @@ public async Task InitializeAsync() return; } - var inboxCollection = string.IsNullOrWhiteSpace(_options.InboxCollection) + string inboxCollection = string.IsNullOrWhiteSpace(_options.InboxCollection) ? "inbox" : _options.InboxCollection; var inboxBuilder = Builders.IndexKeys; await _database.GetCollection(inboxCollection) .Indexes.CreateOneAsync( - new CreateIndexModel(inboxBuilder.Ascending(i => i.ProcessedAt), - new CreateIndexOptions - { - ExpireAfter = TimeSpan.FromSeconds(_options.Expiry) - })); - - var outboxCollection = string.IsNullOrWhiteSpace(_options.OutboxCollection) + new CreateIndexModel( + inboxBuilder.Ascending(i => i.ProcessedAt), + new CreateIndexOptions + { + ExpireAfter = TimeSpan.FromSeconds(_options.Expiry) + }) + ); + + string outboxCollection = string.IsNullOrWhiteSpace(_options.OutboxCollection) ? "outbox" : _options.OutboxCollection; var outboxBuilder = Builders.IndexKeys; await _database.GetCollection(outboxCollection) .Indexes.CreateOneAsync( - new CreateIndexModel(outboxBuilder.Ascending(i => i.ProcessedAt), - new CreateIndexOptions - { - ExpireAfter = TimeSpan.FromSeconds(_options.Expiry) - })); + new CreateIndexModel( + outboxBuilder.Ascending(i => i.ProcessedAt), + new CreateIndexOptions + { + ExpireAfter = TimeSpan.FromSeconds(_options.Expiry) + })); } } \ No newline at end of file diff --git a/src/Genocs.Persistence.Redis/README.md b/src/Genocs.MessageBrokers.Outbox.MongoDB/README_NUGET.md similarity index 57% rename from src/Genocs.Persistence.Redis/README.md rename to src/Genocs.MessageBrokers.Outbox.MongoDB/README_NUGET.md index c96381ac..cf06bc9b 100644 --- a/src/Genocs.Persistence.Redis/README.md +++ b/src/Genocs.MessageBrokers.Outbox.MongoDB/README_NUGET.md @@ -16,7 +16,35 @@ Please check the GitHub repository getting more info. ## Release notes -### [2023-03-12] 5.0.0-preview.5.0 +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 - Implemented MongoDB repository interfaces ### [2023-03-12] 5.0.0 diff --git a/src/Genocs.MessageBrokers.Outbox/Configurations/OutboxOptions.cs b/src/Genocs.MessageBrokers.Outbox/Configurations/OutboxOptions.cs new file mode 100644 index 00000000..da6aaec7 --- /dev/null +++ b/src/Genocs.MessageBrokers.Outbox/Configurations/OutboxOptions.cs @@ -0,0 +1,12 @@ +namespace Genocs.MessageBrokers.Outbox.Configurations; + +public class OutboxOptions +{ + public bool Enabled { get; set; } + public int Expiry { get; set; } + public double IntervalMilliseconds { get; set; } + public string? InboxCollection { get; set; } + public string? OutboxCollection { get; set; } + public string? Type { get; set; } + public bool DisableTransactions { get; set; } +} \ No newline at end of file diff --git a/src/Genocs.MessageBrokers.Outbox/Configurators/MessageOutboxConfigurator.cs b/src/Genocs.MessageBrokers.Outbox/Configurators/MessageOutboxConfigurator.cs index 18aeb4dc..c5db113f 100644 --- a/src/Genocs.MessageBrokers.Outbox/Configurators/MessageOutboxConfigurator.cs +++ b/src/Genocs.MessageBrokers.Outbox/Configurators/MessageOutboxConfigurator.cs @@ -1,14 +1,14 @@ using Genocs.Core.Builders; -using Genocs.MessageBrokers.Outbox.Options; +using Genocs.MessageBrokers.Outbox.Configurations; namespace Genocs.MessageBrokers.Outbox.Configurators; internal sealed class MessageOutboxConfigurator : IMessageOutboxConfigurator { public IGenocsBuilder Builder { get; } - public OutboxSettings Options { get; } + public OutboxOptions Options { get; } - public MessageOutboxConfigurator(IGenocsBuilder builder, OutboxSettings options) + public MessageOutboxConfigurator(IGenocsBuilder builder, OutboxOptions options) { Builder = builder; Options = options; diff --git a/src/Genocs.MessageBrokers.Outbox/Extensions.cs b/src/Genocs.MessageBrokers.Outbox/Extensions.cs index 21ea730a..2a966f11 100644 --- a/src/Genocs.MessageBrokers.Outbox/Extensions.cs +++ b/src/Genocs.MessageBrokers.Outbox/Extensions.cs @@ -1,6 +1,6 @@ using Genocs.Core.Builders; +using Genocs.MessageBrokers.Outbox.Configurations; using Genocs.MessageBrokers.Outbox.Configurators; -using Genocs.MessageBrokers.Outbox.Options; using Genocs.MessageBrokers.Outbox.Outbox; using Genocs.MessageBrokers.Outbox.Processors; using Microsoft.Extensions.DependencyInjection; @@ -12,8 +12,10 @@ public static class Extensions private const string SectionName = "outbox"; private const string RegistryName = "messageBrokers.outbox"; - public static IGenocsBuilder AddMessageOutbox(this IGenocsBuilder builder, - Action configure = null, string sectionName = SectionName) + public static IGenocsBuilder AddMessageOutbox( + this IGenocsBuilder builder, + Action? configure = null, + string sectionName = SectionName) { if (string.IsNullOrWhiteSpace(sectionName)) { @@ -25,7 +27,7 @@ public static IGenocsBuilder AddMessageOutbox(this IGenocsBuilder builder, return builder; } - var options = builder.GetOptions(sectionName); + var options = builder.GetOptions(sectionName); builder.Services.AddSingleton(options); var configurator = new MessageOutboxConfigurator(builder, options); @@ -48,7 +50,7 @@ public static IGenocsBuilder AddMessageOutbox(this IGenocsBuilder builder, return builder; } - public static IMessageOutboxConfigurator AddInMemory(this IMessageOutboxConfigurator configurator, string? mongoSectionName = null) + public static IMessageOutboxConfigurator AddInMemory(this IMessageOutboxConfigurator configurator) { configurator.Builder.Services.AddTransient(); diff --git a/src/Genocs.MessageBrokers.Outbox/Genocs.MessageBrokers.Outbox.csproj b/src/Genocs.MessageBrokers.Outbox/Genocs.MessageBrokers.Outbox.csproj index 06446e83..40a7ffe0 100644 --- a/src/Genocs.MessageBrokers.Outbox/Genocs.MessageBrokers.Outbox.csproj +++ b/src/Genocs.MessageBrokers.Outbox/Genocs.MessageBrokers.Outbox.csproj @@ -1,52 +1,28 @@  - - net6.0;net7.0 - enable - enable - Genocs.MessageBrokers.Outbox - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The message brokers outbox interface library useful to build .NET Core projects. - The message brokers outbox interface library useful to build .NET Core projects. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.MessageBrokers.Outbox + Genocs.MessageBrokers.Outbox + Genocs.MessageBrokers.Outbox + The message brokers outbox interface library useful to build .NET Core projects. + The message brokers outbox interface library useful to build .NET Core projects. + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + - - - True - \ - - - True - \ - - - True - \ - - + + + - - - + + + - - - diff --git a/src/Genocs.MessageBrokers.Outbox/IMessageOutbox.cs b/src/Genocs.MessageBrokers.Outbox/IMessageOutbox.cs index 4561d56c..b5b699e4 100644 --- a/src/Genocs.MessageBrokers.Outbox/IMessageOutbox.cs +++ b/src/Genocs.MessageBrokers.Outbox/IMessageOutbox.cs @@ -1,7 +1,7 @@ namespace Genocs.MessageBrokers.Outbox; /// -/// The Message Outbox interface definition +/// The Message Outbox interface definition. /// public interface IMessageOutbox { @@ -9,11 +9,13 @@ public interface IMessageOutbox Task HandleAsync(string messageId, Func handler); - Task SendAsync(T message, - string? originatedMessageId = null, - string? messageId = null, - string? correlationId = null, - string? spanContext = null, - object? messageContext = null, - IDictionary? headers = null) where T : class; + Task SendAsync( + T message, + string? originatedMessageId = null, + string? messageId = null, + string? correlationId = null, + string? spanContext = null, + object? messageContext = null, + IDictionary? headers = null) + where T : class; } \ No newline at end of file diff --git a/src/Genocs.MessageBrokers.Outbox/IMessageOutboxConfigurator.cs b/src/Genocs.MessageBrokers.Outbox/IMessageOutboxConfigurator.cs index dfd18932..ddf94a6f 100644 --- a/src/Genocs.MessageBrokers.Outbox/IMessageOutboxConfigurator.cs +++ b/src/Genocs.MessageBrokers.Outbox/IMessageOutboxConfigurator.cs @@ -1,10 +1,10 @@ using Genocs.Core.Builders; -using Genocs.MessageBrokers.Outbox.Options; +using Genocs.MessageBrokers.Outbox.Configurations; namespace Genocs.MessageBrokers.Outbox; public interface IMessageOutboxConfigurator { IGenocsBuilder Builder { get; } - OutboxSettings Options { get; } + OutboxOptions Options { get; } } \ No newline at end of file diff --git a/src/Genocs.MessageBrokers.Outbox/Messages/InboxMessage.cs b/src/Genocs.MessageBrokers.Outbox/Messages/InboxMessage.cs index 8ffc774f..93402645 100644 --- a/src/Genocs.MessageBrokers.Outbox/Messages/InboxMessage.cs +++ b/src/Genocs.MessageBrokers.Outbox/Messages/InboxMessage.cs @@ -1,8 +1,8 @@ -using Genocs.Common.Types; +using Genocs.Core.Domain.Entities; namespace Genocs.MessageBrokers.Outbox.Messages; -public sealed class InboxMessage : IIdentifiable +public sealed class InboxMessage : IEntity { public string Id { get; set; } public DateTime ProcessedAt { get; set; } diff --git a/src/Genocs.MessageBrokers.Outbox/Messages/OutboxMessage.cs b/src/Genocs.MessageBrokers.Outbox/Messages/OutboxMessage.cs index 8d251cc4..f557f795 100644 --- a/src/Genocs.MessageBrokers.Outbox/Messages/OutboxMessage.cs +++ b/src/Genocs.MessageBrokers.Outbox/Messages/OutboxMessage.cs @@ -1,20 +1,20 @@ -using Genocs.Common.Types; +using Genocs.Core.Domain.Entities; namespace Genocs.MessageBrokers.Outbox.Messages; -public sealed class OutboxMessage : IIdentifiable +public sealed class OutboxMessage : IEntity { public string Id { get; set; } - public string OriginatedMessageId { get; set; } - public string CorrelationId { get; set; } - public string SpanContext { get; set; } + public string? OriginatedMessageId { get; set; } + public string? CorrelationId { get; set; } + public string? SpanContext { get; set; } public Dictionary Headers { get; set; } = new(); - public string MessageType { get; set; } - public string MessageContextType { get; set; } - public object Message { get; set; } - public object MessageContext { get; set; } - public string SerializedMessage { get; set; } - public string SerializedMessageContext { get; set; } + public string? MessageType { get; set; } + public string? MessageContextType { get; set; } + public object? Message { get; set; } + public object? MessageContext { get; set; } + public string? SerializedMessage { get; set; } + public string? SerializedMessageContext { get; set; } public DateTime SentAt { get; set; } public DateTime? ProcessedAt { get; set; } diff --git a/src/Genocs.MessageBrokers.Outbox/Options/OutboxSettings.cs b/src/Genocs.MessageBrokers.Outbox/Options/OutboxSettings.cs deleted file mode 100644 index 9904c770..00000000 --- a/src/Genocs.MessageBrokers.Outbox/Options/OutboxSettings.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Genocs.MessageBrokers.Outbox.Options; - -public class OutboxSettings -{ - public bool Enabled { get; set; } - public int Expiry { get; set; } - public double IntervalMilliseconds { get; set; } - public string InboxCollection { get; set; } - public string OutboxCollection { get; set; } - public string Type { get; set; } - public bool DisableTransactions { get; set; } -} \ No newline at end of file diff --git a/src/Genocs.MessageBrokers.Outbox/Outbox/InMemoryMessageOutbox.cs b/src/Genocs.MessageBrokers.Outbox/Outbox/InMemoryMessageOutbox.cs index fc6e60cb..82624406 100644 --- a/src/Genocs.MessageBrokers.Outbox/Outbox/InMemoryMessageOutbox.cs +++ b/src/Genocs.MessageBrokers.Outbox/Outbox/InMemoryMessageOutbox.cs @@ -1,5 +1,5 @@ +using Genocs.MessageBrokers.Outbox.Configurations; using Genocs.MessageBrokers.Outbox.Messages; -using Genocs.MessageBrokers.Outbox.Options; using Microsoft.Extensions.Logging; using System.Collections.Concurrent; @@ -7,16 +7,14 @@ namespace Genocs.MessageBrokers.Outbox.Outbox; internal sealed class InMemoryMessageOutbox : IMessageOutbox, IMessageOutboxAccessor { - private readonly ConcurrentDictionary _inboxMessages = - new(); + private readonly ConcurrentDictionary _inboxMessages = new(); - private readonly ConcurrentDictionary _outboxMessages = - new(); + private readonly ConcurrentDictionary _outboxMessages = new(); private readonly ILogger _logger; private readonly int _expiry; - public InMemoryMessageOutbox(OutboxSettings options, ILogger logger) + public InMemoryMessageOutbox(OutboxOptions options, ILogger logger) { _logger = logger; _expiry = options.Expiry; @@ -58,9 +56,15 @@ public async Task HandleAsync(string messageId, Func handler) _logger.LogTrace($"Processed a message with id: '{messageId}'."); } - public Task SendAsync(T message, string originatedMessageId = null, string messageId = null, - string correlationId = null, string spanContext = null, object messageContext = null, - IDictionary headers = null) where T : class + public Task SendAsync( + T message, + string? originatedMessageId = null, + string? messageId = null, + string? correlationId = null, + string? spanContext = null, + object? messageContext = null, + IDictionary? headers = null) + where T : class { if (!Enabled) { diff --git a/src/Genocs.MessageBrokers.Outbox/Processors/OutboxProcessor.cs b/src/Genocs.MessageBrokers.Outbox/Processors/OutboxProcessor.cs index 67f5b005..4f36445b 100644 --- a/src/Genocs.MessageBrokers.Outbox/Processors/OutboxProcessor.cs +++ b/src/Genocs.MessageBrokers.Outbox/Processors/OutboxProcessor.cs @@ -1,4 +1,4 @@ -using Genocs.MessageBrokers.Outbox.Options; +using Genocs.MessageBrokers.Outbox.Configurations; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -10,14 +10,17 @@ internal sealed class OutboxProcessor : IHostedService { private readonly IServiceProvider _serviceProvider; private readonly IBusPublisher _publisher; - private readonly OutboxSettings _options; + private readonly OutboxOptions _options; private readonly ILogger _logger; private readonly TimeSpan _interval; private readonly OutboxType _type; private Timer _timer; - public OutboxProcessor(IServiceProvider serviceProvider, IBusPublisher publisher, OutboxSettings options, - ILogger logger) + public OutboxProcessor( + IServiceProvider serviceProvider, + IBusPublisher publisher, + OutboxOptions options, + ILogger logger) { if (options.Enabled && options.IntervalMilliseconds <= 0) { @@ -79,7 +82,7 @@ private void SendOutboxMessages(object state) private async Task SendOutboxMessagesAsync() { - var jobId = Guid.NewGuid().ToString("N"); + string jobId = Guid.NewGuid().ToString("N"); _logger.LogTrace($"Started processing outbox messages... [job id: '{jobId}']"); var stopwatch = new Stopwatch(); stopwatch.Start(); diff --git a/src/Genocs.MessageBrokers.Outbox/README.md b/src/Genocs.MessageBrokers.Outbox/README_NUGET.md similarity index 65% rename from src/Genocs.MessageBrokers.Outbox/README.md rename to src/Genocs.MessageBrokers.Outbox/README_NUGET.md index 301959bb..f1a3ae5a 100644 --- a/src/Genocs.MessageBrokers.Outbox/README.md +++ b/src/Genocs.MessageBrokers.Outbox/README_NUGET.md @@ -26,7 +26,35 @@ Following are the project settings needed to enable monitoring ## Release notes -### [2023-03-12] 5.0.0-preview.5.0 +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 - Implemented MongoDB repository interfaces ### [2023-03-12] 5.0.0 diff --git a/src/Genocs.MessageBrokers.RabbitMQ/Extensions.cs b/src/Genocs.MessageBrokers.RabbitMQ/Extensions.cs index 48a2158c..15c93d89 100644 --- a/src/Genocs.MessageBrokers.RabbitMQ/Extensions.cs +++ b/src/Genocs.MessageBrokers.RabbitMQ/Extensions.cs @@ -17,9 +17,8 @@ namespace Genocs.MessageBrokers.RabbitMQ; - /// -/// RabbitMQ support helper +/// RabbitMQ support helper. /// public static class Extensions { @@ -27,18 +26,21 @@ public static class Extensions private const string RegistryName = "messageBrokers.rabbitmq"; /// - /// AddRabbitMq extension method + /// AddRabbitMq extension method. /// - /// - /// - /// - /// - /// + /// The builder. + /// the default section name. + /// The plugin action method. + /// The configurator. + /// The serializer. /// - /// - public static IGenocsBuilder AddRabbitMq(this IGenocsBuilder builder, string sectionName = SectionName, - Func? plugins = null, - Action? connectionFactoryConfigurator = null, IRabbitMqSerializer? serializer = null) + /// Raised when configuration is incorrect. + public static IGenocsBuilder AddRabbitMq( + this IGenocsBuilder builder, + string sectionName = SectionName, + Func? plugins = null, + Action? connectionFactoryConfigurator = null, + IRabbitMqSerializer? serializer = null) { if (string.IsNullOrWhiteSpace(sectionName)) { @@ -57,7 +59,6 @@ public static IGenocsBuilder AddRabbitMq(this IGenocsBuilder builder, string sec throw new ArgumentException("RabbitMQ hostnames are not specified.", nameof(options.HostNames)); } - ILogger logger; using (var serviceProvider = builder.Services.BuildServiceProvider()) { @@ -129,8 +130,10 @@ public static IGenocsBuilder AddRabbitMq(this IGenocsBuilder builder, string sec return builder; } - private static void ConfigureSsl(ConnectionFactory connectionFactory, RabbitMQOptions options, - ILogger logger) + private static void ConfigureSsl( + ConnectionFactory connectionFactory, + RabbitMQOptions options, + ILogger logger) { if (options.Ssl is null || string.IsNullOrWhiteSpace(options.Ssl.ServerName)) { @@ -138,8 +141,10 @@ private static void ConfigureSsl(ConnectionFactory connectionFactory, RabbitMQOp return; } - connectionFactory.Ssl = new SslOption(options.Ssl.ServerName, options.Ssl.CertificatePath, - options.Ssl.Enabled); + connectionFactory.Ssl = new SslOption( + options.Ssl.ServerName, + options.Ssl.CertificatePath, + options.Ssl.Enabled); logger.LogDebug($"RabbitMQ SSL is: {(options.Ssl.Enabled ? "enabled" : "disabled")}, " + $"server: '{options.Ssl.ServerName}', client certificate: '{options.Ssl.CertificatePath}', " + @@ -180,12 +185,13 @@ private static void ConfigureSsl(ConnectionFactory connectionFactory, RabbitMQOp logger.LogDebug("Received X509 certificate chain statuses: " + $"{string.Join(", ", statuses.Select(x => x.Status))}"); - var isValid = statuses.All(chainStatus => chainStatus.Status == X509ChainStatusFlags.NoError + bool isValid = statuses.All(chainStatus => chainStatus.Status == X509ChainStatusFlags.NoError || ignoredStatuses.Contains(chainStatus.Status)); if (!isValid) { - logger.LogError(string.Join(Environment.NewLine, - statuses.Select(s => $"{s.Status} - {s.StatusInformation}"))); + logger.LogError(string.Join( + Environment.NewLine, + statuses.Select(s => $"{s.Status} - {s.StatusInformation}"))); } return isValid; diff --git a/src/Genocs.MessageBrokers.RabbitMQ/FailedMessage.cs b/src/Genocs.MessageBrokers.RabbitMQ/FailedMessage.cs index 6af3ed3a..e3ac8ea1 100644 --- a/src/Genocs.MessageBrokers.RabbitMQ/FailedMessage.cs +++ b/src/Genocs.MessageBrokers.RabbitMQ/FailedMessage.cs @@ -4,19 +4,19 @@ namespace Genocs.MessageBrokers.RabbitMQ; public class FailedMessage { - public object Message { get; } + public object? Message { get; } public bool ShouldRetry { get; } [Description("This will only work if 'deadLetter' is enabled in RabbitMQ options." + "For more information, see https://www.rabbitmq.com/dlx.html")] public bool MoveToDeadLetter { get; } - public FailedMessage(bool shouldRetry = true, bool moveToDeadLetter = true) : this(null, shouldRetry, - moveToDeadLetter) + public FailedMessage(bool shouldRetry = true, bool moveToDeadLetter = true) + : this(null, shouldRetry, moveToDeadLetter) { } - public FailedMessage(object message, bool shouldRetry = true, bool moveToDeadLetter = true) + public FailedMessage(object? message, bool shouldRetry = true, bool moveToDeadLetter = true) { Message = message; ShouldRetry = shouldRetry; diff --git a/src/Genocs.MessageBrokers.RabbitMQ/Genocs.MessageBrokers.RabbitMQ.csproj b/src/Genocs.MessageBrokers.RabbitMQ/Genocs.MessageBrokers.RabbitMQ.csproj index b3baf369..7ebfcf64 100644 --- a/src/Genocs.MessageBrokers.RabbitMQ/Genocs.MessageBrokers.RabbitMQ.csproj +++ b/src/Genocs.MessageBrokers.RabbitMQ/Genocs.MessageBrokers.RabbitMQ.csproj @@ -1,59 +1,35 @@  - - net6.0;net7.0 - enable - enable - Genocs.MessageBrokers.RabbitMQ - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The message brokers RabbitMQ interface library useful to build .NET Core projects. - The message brokers RabbitMQ outbox interface library useful to build .NET Core projects. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.MessageBrokers.RabbitMQ + Genocs.MessageBrokers.RabbitMQ + Genocs.MessageBrokers.RabbitMQ + The message brokers RabbitMQ interface library useful to build .NET Core projects. + The message brokers RabbitMQ outbox interface library useful to build .NET Core projects. + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + - - - True - \ - - - True - \ - - - True - \ - - + + + - - - + + + + + + + + + - - - - - - - - - diff --git a/src/Genocs.MessageBrokers.RabbitMQ/IConventionsBuilder.cs b/src/Genocs.MessageBrokers.RabbitMQ/IConventionsBuilder.cs index 78a3e9b5..6d90b2a1 100644 --- a/src/Genocs.MessageBrokers.RabbitMQ/IConventionsBuilder.cs +++ b/src/Genocs.MessageBrokers.RabbitMQ/IConventionsBuilder.cs @@ -1,5 +1,3 @@ -using System; - namespace Genocs.MessageBrokers.RabbitMQ; public interface IConventionsBuilder diff --git a/src/Genocs.MessageBrokers.RabbitMQ/IRabbitMqClient.cs b/src/Genocs.MessageBrokers.RabbitMQ/IRabbitMqClient.cs index f9c773e6..b7d4e687 100644 --- a/src/Genocs.MessageBrokers.RabbitMQ/IRabbitMqClient.cs +++ b/src/Genocs.MessageBrokers.RabbitMQ/IRabbitMqClient.cs @@ -2,6 +2,12 @@ namespace Genocs.MessageBrokers.RabbitMQ; public interface IRabbitMQClient { - void Send(object message, IConventions conventions, string? messageId = null, string? correlationId = null, - string? spanContext = null, object? messageContext = null, IDictionary headers = null); + void Send( + object? message, + IConventions conventions, + string? messageId = null, + string? correlationId = null, + string? spanContext = null, + object? messageContext = null, + IDictionary? headers = null); } \ No newline at end of file diff --git a/src/Genocs.MessageBrokers.RabbitMQ/Initializers/RabbitMqExchangeInitializer.cs b/src/Genocs.MessageBrokers.RabbitMQ/Initializers/RabbitMqExchangeInitializer.cs index 8aba9fe2..d80af9f3 100644 --- a/src/Genocs.MessageBrokers.RabbitMQ/Initializers/RabbitMqExchangeInitializer.cs +++ b/src/Genocs.MessageBrokers.RabbitMQ/Initializers/RabbitMqExchangeInitializer.cs @@ -36,17 +36,24 @@ public Task InitializeAsync() if (_options.Exchange?.Declare == true) { Log(_options.Exchange.Name, _options.Exchange.Type); - channel.ExchangeDeclare(_options.Exchange.Name, _options.Exchange.Type, _options.Exchange.Durable, - _options.Exchange.AutoDelete); + + channel.ExchangeDeclare( + _options.Exchange.Name, + _options.Exchange.Type, + _options.Exchange.Durable, + _options.Exchange.AutoDelete); if (_options.DeadLetter?.Enabled is true && _options.DeadLetter?.Declare is true) { - channel.ExchangeDeclare($"{_options.DeadLetter.Prefix}{_options.Exchange.Name}{_options.DeadLetter.Suffix}", - ExchangeType.Direct, _options.Exchange.Durable, _options.Exchange.AutoDelete); + channel.ExchangeDeclare( + $"{_options.DeadLetter.Prefix}{_options.Exchange.Name}{_options.DeadLetter.Suffix}", + ExchangeType.Direct, + _options.Exchange.Durable, + _options.Exchange.AutoDelete); } } - foreach (var exchange in exchanges) + foreach (string? exchange in exchanges) { if (exchange.Equals(_options.Exchange?.Name, StringComparison.InvariantCultureIgnoreCase)) { diff --git a/src/Genocs.MessageBrokers.Outbox.MongoDB/README.md b/src/Genocs.MessageBrokers.RabbitMQ/README_NUGET.md similarity index 57% rename from src/Genocs.MessageBrokers.Outbox.MongoDB/README.md rename to src/Genocs.MessageBrokers.RabbitMQ/README_NUGET.md index c96381ac..cf06bc9b 100644 --- a/src/Genocs.MessageBrokers.Outbox.MongoDB/README.md +++ b/src/Genocs.MessageBrokers.RabbitMQ/README_NUGET.md @@ -16,7 +16,35 @@ Please check the GitHub repository getting more info. ## Release notes -### [2023-03-12] 5.0.0-preview.5.0 +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 - Implemented MongoDB repository interfaces ### [2023-03-12] 5.0.0 diff --git a/src/Genocs.MessageBrokers.RabbitMQ/Subscribers/MessageSubscriber.cs b/src/Genocs.MessageBrokers.RabbitMQ/Subscribers/MessageSubscriber.cs index fc8128cf..d1432617 100644 --- a/src/Genocs.MessageBrokers.RabbitMQ/Subscribers/MessageSubscriber.cs +++ b/src/Genocs.MessageBrokers.RabbitMQ/Subscribers/MessageSubscriber.cs @@ -4,10 +4,12 @@ internal class MessageSubscriber : IMessageSubscriber { public MessageSubscriberAction Action { get; } public Type Type { get; } - public Func Handle { get; } + public Func? Handle { get; } - private MessageSubscriber(MessageSubscriberAction action, Type type, - Func handle = null) + private MessageSubscriber( + MessageSubscriberAction action, + Type type, + Func? handle = null) { Action = action; Type = type; diff --git a/src/Genocs.MessageBrokers/CQRS/Dispatchers/ServiceBusMessageDispatcher.cs b/src/Genocs.MessageBrokers/CQRS/Dispatchers/ServiceBusMessageDispatcher.cs index 4a7138b0..98a558c0 100644 --- a/src/Genocs.MessageBrokers/CQRS/Dispatchers/ServiceBusMessageDispatcher.cs +++ b/src/Genocs.MessageBrokers/CQRS/Dispatchers/ServiceBusMessageDispatcher.cs @@ -14,9 +14,11 @@ public ServiceBusMessageDispatcher(IBusPublisher busPublisher, ICorrelationConte _accessor = accessor; } - public Task SendAsync(T command, CancellationToken cancellationToken = default) where T : class, ICommand + public Task SendAsync(T command, CancellationToken cancellationToken = default) + where T : class, ICommand => _busPublisher.SendAsync(command, _accessor.CorrelationContext); - public Task PublishAsync(T @event, CancellationToken cancellationToken = default) where T : class, IEvent + public Task PublishAsync(T @event, CancellationToken cancellationToken = default) + where T : class, IEvent => _busPublisher.PublishAsync(@event, _accessor.CorrelationContext); } \ No newline at end of file diff --git a/src/Genocs.MessageBrokers/CQRS/Extensions.cs b/src/Genocs.MessageBrokers/CQRS/Extensions.cs index 364b0839..f84c6a26 100644 --- a/src/Genocs.MessageBrokers/CQRS/Extensions.cs +++ b/src/Genocs.MessageBrokers/CQRS/Extensions.cs @@ -16,14 +16,16 @@ public static Task PublishAsync(this IBusPublisher busPublisher, TEvent where TEvent : class, IEvent => busPublisher.PublishAsync(@event, messageContext: messageContext); - public static IBusSubscriber SubscribeCommand(this IBusSubscriber busSubscriber) where T : class, ICommand + public static IBusSubscriber SubscribeCommand(this IBusSubscriber busSubscriber) + where T : class, ICommand => busSubscriber.Subscribe(async (serviceProvider, command, _) => { using var scope = serviceProvider.CreateScope(); await scope.ServiceProvider.GetRequiredService>().HandleAsync(command); }); - public static IBusSubscriber SubscribeEvent(this IBusSubscriber busSubscriber) where T : class, IEvent + public static IBusSubscriber SubscribeEvent(this IBusSubscriber busSubscriber) + where T : class, IEvent => busSubscriber.Subscribe(async (serviceProvider, @event, _) => { using var scope = serviceProvider.CreateScope(); diff --git a/src/Genocs.MessageBrokers/CorrelationContextAccessor.cs b/src/Genocs.MessageBrokers/CorrelationContextAccessor.cs index 333b9e1c..682492d9 100644 --- a/src/Genocs.MessageBrokers/CorrelationContextAccessor.cs +++ b/src/Genocs.MessageBrokers/CorrelationContextAccessor.cs @@ -5,7 +5,7 @@ public class CorrelationContextAccessor : ICorrelationContextAccessor private static readonly AsyncLocal Holder = new(); - public object CorrelationContext + public object? CorrelationContext { get => Holder.Value?.Context; set diff --git a/src/Genocs.MessageBrokers/Genocs.MessageBrokers.csproj b/src/Genocs.MessageBrokers/Genocs.MessageBrokers.csproj index 4e480cd1..8cb7ec2c 100644 --- a/src/Genocs.MessageBrokers/Genocs.MessageBrokers.csproj +++ b/src/Genocs.MessageBrokers/Genocs.MessageBrokers.csproj @@ -1,54 +1,28 @@  - - net6.0;net7.0 - enable - enable - Genocs.MessageBrokers - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The message brokers interface library useful to build .NET Core projects. - The message brokers interface library useful to build .NET Core projects. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.MessageBrokers + Genocs.MessageBrokers + Genocs.MessageBrokers + The message brokers interface library useful to build .NET Core projects. + The message brokers interface library useful to build .NET Core projects. + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + - - - True - \ - - - True - \ - - - True - \ - - + + + - - - - - - - - + + + diff --git a/src/Genocs.MessageBrokers/IBusPublisher.cs b/src/Genocs.MessageBrokers/IBusPublisher.cs index 5fc4da13..c0e995c8 100644 --- a/src/Genocs.MessageBrokers/IBusPublisher.cs +++ b/src/Genocs.MessageBrokers/IBusPublisher.cs @@ -2,6 +2,12 @@ namespace Genocs.MessageBrokers; public interface IBusPublisher { - Task PublishAsync(T message, string? messageId = null, string? correlationId = null, string? spanContext = null, - object? messageContext = null, IDictionary? headers = null) where T : class; + Task PublishAsync( + T message, + string? messageId = null, + string? correlationId = null, + string? spanContext = null, + object? messageContext = null, + IDictionary? headers = null) + where T : class; } \ No newline at end of file diff --git a/src/Genocs.MessageBrokers/IBusSubscriber.cs b/src/Genocs.MessageBrokers/IBusSubscriber.cs index 0b392b26..012a50aa 100644 --- a/src/Genocs.MessageBrokers/IBusSubscriber.cs +++ b/src/Genocs.MessageBrokers/IBusSubscriber.cs @@ -2,5 +2,6 @@ public interface IBusSubscriber : IDisposable { - IBusSubscriber Subscribe(Func handle) where T : class; + IBusSubscriber Subscribe(Func handle) + where T : class; } \ No newline at end of file diff --git a/src/Genocs.MessageBrokers/ICorrelationContextAccessor.cs b/src/Genocs.MessageBrokers/ICorrelationContextAccessor.cs index 9d78cf5e..0d45ba79 100644 --- a/src/Genocs.MessageBrokers/ICorrelationContextAccessor.cs +++ b/src/Genocs.MessageBrokers/ICorrelationContextAccessor.cs @@ -2,5 +2,5 @@ namespace Genocs.MessageBrokers; public interface ICorrelationContextAccessor { - object CorrelationContext { get; set; } + object? CorrelationContext { get; set; } } \ No newline at end of file diff --git a/src/Genocs.MessageBrokers/IMessageProperties.cs b/src/Genocs.MessageBrokers/IMessageProperties.cs index bf1161d9..16f800ef 100644 --- a/src/Genocs.MessageBrokers/IMessageProperties.cs +++ b/src/Genocs.MessageBrokers/IMessageProperties.cs @@ -2,8 +2,8 @@ namespace Genocs.MessageBrokers; public interface IMessageProperties { - string MessageId { get; } - string CorrelationId { get; } + string? MessageId { get; } + string? CorrelationId { get; } long Timestamp { get; } - IDictionary Headers { get; } + IDictionary? Headers { get; } } \ No newline at end of file diff --git a/src/Genocs.MessageBrokers/IMessagePropertiesAccessor.cs b/src/Genocs.MessageBrokers/IMessagePropertiesAccessor.cs index de2de052..3f3515d8 100644 --- a/src/Genocs.MessageBrokers/IMessagePropertiesAccessor.cs +++ b/src/Genocs.MessageBrokers/IMessagePropertiesAccessor.cs @@ -2,5 +2,5 @@ namespace Genocs.MessageBrokers; public interface IMessagePropertiesAccessor { - IMessageProperties MessageProperties { get; set; } + IMessageProperties? MessageProperties { get; set; } } \ No newline at end of file diff --git a/src/Genocs.MessageBrokers/MessageAttribute.cs b/src/Genocs.MessageBrokers/MessageAttribute.cs index 2d30ccd6..fa5c6fae 100644 --- a/src/Genocs.MessageBrokers/MessageAttribute.cs +++ b/src/Genocs.MessageBrokers/MessageAttribute.cs @@ -1,40 +1,43 @@ namespace Genocs.MessageBrokers; /// -/// Message Attribute +/// Message Attribute. /// [AttributeUsage(AttributeTargets.Class)] public class MessageAttribute : Attribute { /// - /// The Exchange name + /// The Exchange name. /// public string? Exchange { get; } /// - /// The Routing Key + /// The Routing Key. /// public string? RoutingKey { get; } /// - /// The Queue name + /// The Queue name. /// public string? Queue { get; } /// - /// External + /// External. /// public bool External { get; } /// - /// ctor + /// ctor. /// /// /// /// /// - public MessageAttribute(string? exchange = null, string? routingKey = null, string? queue = null, - bool external = false) + public MessageAttribute( + string? exchange = null, + string? routingKey = null, + string? queue = null, + bool external = false) { Exchange = exchange; RoutingKey = routingKey; diff --git a/src/Genocs.MessageBrokers/MessageProperties.cs b/src/Genocs.MessageBrokers/MessageProperties.cs index e22baab9..f50360f8 100644 --- a/src/Genocs.MessageBrokers/MessageProperties.cs +++ b/src/Genocs.MessageBrokers/MessageProperties.cs @@ -1,11 +1,9 @@ -using System.Collections.Generic; - namespace Genocs.MessageBrokers; public class MessageProperties : IMessageProperties { - public string MessageId { get; set; } - public string CorrelationId { get; set; } + public string? MessageId { get; set; } + public string? CorrelationId { get; set; } public long Timestamp { get; set; } - public IDictionary Headers { get; set; } + public IDictionary? Headers { get; set; } } \ No newline at end of file diff --git a/src/Genocs.MessageBrokers/MessagePropertiesAccessor.cs b/src/Genocs.MessageBrokers/MessagePropertiesAccessor.cs index 64967ca8..dd289b85 100644 --- a/src/Genocs.MessageBrokers/MessagePropertiesAccessor.cs +++ b/src/Genocs.MessageBrokers/MessagePropertiesAccessor.cs @@ -2,10 +2,9 @@ namespace Genocs.MessageBrokers; public class MessagePropertiesAccessor : IMessagePropertiesAccessor { - private static readonly AsyncLocal - Holder = new(); + private static readonly AsyncLocal Holder = new(); - public IMessageProperties MessageProperties + public IMessageProperties? MessageProperties { get => Holder.Value?.Properties; set @@ -25,6 +24,6 @@ public IMessageProperties MessageProperties private class MessageContextHolder { - public IMessageProperties Properties; + public IMessageProperties? Properties; } } \ No newline at end of file diff --git a/src/Genocs.MessageBrokers/README.md b/src/Genocs.MessageBrokers/README_NUGET.md similarity index 74% rename from src/Genocs.MessageBrokers/README.md rename to src/Genocs.MessageBrokers/README_NUGET.md index bc2fb3cb..38b40df2 100644 --- a/src/Genocs.MessageBrokers/README.md +++ b/src/Genocs.MessageBrokers/README_NUGET.md @@ -33,7 +33,35 @@ Following are the project settings needed to enable monitoring ## Release notes -### [2023-03-12] 5.0.0-preview.5.0 +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 - Implemented MongoDB repository interfaces ### [2023-03-12] 5.0.0 diff --git a/src/Genocs.Metrics/AppMetrics/Builders/MetricsOptionsBuilder.cs b/src/Genocs.Metrics/AppMetrics/Builders/MetricsOptionsBuilder.cs index d3ab3fc7..a28c97f2 100644 --- a/src/Genocs.Metrics/AppMetrics/Builders/MetricsOptionsBuilder.cs +++ b/src/Genocs.Metrics/AppMetrics/Builders/MetricsOptionsBuilder.cs @@ -1,57 +1,59 @@ +using Genocs.Metrics.AppMetrics.Configurations; + namespace Genocs.Metrics.AppMetrics.Builders; internal sealed class MetricsOptionsBuilder : IMetricsOptionsBuilder { - private readonly MetricsOptions _options = new(); + private readonly MetricsOptions _settings = new(); public IMetricsOptionsBuilder Enable(bool enabled) { - _options.Enabled = enabled; + _settings.Enabled = enabled; return this; } public IMetricsOptionsBuilder WithInfluxEnabled(bool influxEnabled) { - _options.InfluxEnabled = influxEnabled; + _settings.InfluxEnabled = influxEnabled; return this; } public IMetricsOptionsBuilder WithPrometheusEnabled(bool prometheusEnabled) { - _options.PrometheusEnabled = prometheusEnabled; + _settings.PrometheusEnabled = prometheusEnabled; return this; } public IMetricsOptionsBuilder WithPrometheusFormatter(string prometheusFormatter) { - _options.PrometheusFormatter = prometheusFormatter; + _settings.PrometheusFormatter = prometheusFormatter; return this; } public IMetricsOptionsBuilder WithInfluxUrl(string influxUrl) { - _options.InfluxUrl = influxUrl; + _settings.InfluxUrl = influxUrl; return this; } public IMetricsOptionsBuilder WithDatabase(string database) { - _options.Database = database; + _settings.Database = database; return this; } public IMetricsOptionsBuilder WithInterval(int interval) { - _options.Interval = interval; + _settings.Interval = interval; return this; } public IMetricsOptionsBuilder WithTags(IDictionary tags) { - _options.Tags = tags; + _settings.Tags = tags; return this; } public MetricsOptions Build() - => _options; + => _settings; } \ No newline at end of file diff --git a/src/Genocs.Metrics/AppMetrics/IMetricsOptionsBuilder.cs b/src/Genocs.Metrics/AppMetrics/Configurations/IMetricsOptionsBuilder.cs similarity index 91% rename from src/Genocs.Metrics/AppMetrics/IMetricsOptionsBuilder.cs rename to src/Genocs.Metrics/AppMetrics/Configurations/IMetricsOptionsBuilder.cs index cfbaf375..750b1d4b 100644 --- a/src/Genocs.Metrics/AppMetrics/IMetricsOptionsBuilder.cs +++ b/src/Genocs.Metrics/AppMetrics/Configurations/IMetricsOptionsBuilder.cs @@ -1,4 +1,4 @@ -namespace Genocs.Metrics.AppMetrics; +namespace Genocs.Metrics.AppMetrics.Configurations; public interface IMetricsOptionsBuilder { diff --git a/src/Genocs.Metrics/AppMetrics/MetricsOptions.cs b/src/Genocs.Metrics/AppMetrics/Configurations/MetricsOptions.cs similarity index 61% rename from src/Genocs.Metrics/AppMetrics/MetricsOptions.cs rename to src/Genocs.Metrics/AppMetrics/Configurations/MetricsOptions.cs index 0f8f932a..17de484d 100644 --- a/src/Genocs.Metrics/AppMetrics/MetricsOptions.cs +++ b/src/Genocs.Metrics/AppMetrics/Configurations/MetricsOptions.cs @@ -1,53 +1,53 @@ -namespace Genocs.Metrics.AppMetrics; +namespace Genocs.Metrics.AppMetrics.Configurations; /// -/// The MetricsOptions class +/// The Metrics settings class. /// public class MetricsOptions { /// - /// Default section name + /// Default section name. /// - public const string Position = "Metrics"; + public const string Position = "metrics"; /// - /// It defines whether the section is enabled or not + /// It defines whether the section is enabled or not. /// public bool Enabled { get; set; } /// - /// It defines whether the Influx db is enabled or not + /// It defines whether the Influx db is enabled or not. /// public bool InfluxEnabled { get; set; } /// - /// It defines whether the Prometheus is enabled or not + /// It defines whether the Prometheus is enabled or not. /// public bool PrometheusEnabled { get; set; } /// /// The Prometheus formatter. - /// allowed method are: protobuf or (null) + /// Allowed method are: protobuf or (null). /// public string? PrometheusFormatter { get; set; } /// - /// The InfluxDb url + /// The InfluxDb url. /// public string? InfluxUrl { get; set; } /// - /// The InfluxDb database name + /// The InfluxDb database name. /// public string? Database { get; set; } /// - /// The metrics interval + /// The metrics interval. /// public int Interval { get; set; } = 10; /// - /// List of tags + /// List of tags. /// public IDictionary? Tags { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Metrics/AppMetrics/Extensions.cs b/src/Genocs.Metrics/AppMetrics/Extensions.cs index b2d8f027..94e769c3 100644 --- a/src/Genocs.Metrics/AppMetrics/Extensions.cs +++ b/src/Genocs.Metrics/AppMetrics/Extensions.cs @@ -4,10 +4,11 @@ using App.Metrics.AspNetCore.Health.Endpoints; using App.Metrics.AspNetCore.Tracking; using App.Metrics.Formatters.Prometheus; -using Genocs.Common.Options; +using Genocs.Common.Configurations; using Genocs.Core.Builders; using Genocs.Metrics.AppMetrics; using Genocs.Metrics.AppMetrics.Builders; +using Genocs.Metrics.AppMetrics.Configurations; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; @@ -18,14 +19,16 @@ namespace Genocs.Metrics.AppMetrics; public static class Extensions { - private static bool _initialized; private const string MetricsSectionName = "metrics"; private const string AppSectionName = "app"; private const string RegistryName = "metrics.metrics"; + private static bool _initialized; [Description("For the time being it sets Kestrel's AllowSynchronousIO = true, see https://github.com/AppMetrics/AppMetrics/issues/396")] - public static IGenocsBuilder AddMetrics(this IGenocsBuilder builder, - string metricsSectionName = MetricsSectionName, string appSectionName = AppSectionName) + public static IGenocsBuilder AddMetrics( + this IGenocsBuilder builder, + string metricsSectionName = MetricsSectionName, + string appSectionName = AppSectionName) { if (string.IsNullOrWhiteSpace(metricsSectionName)) { @@ -34,18 +37,20 @@ public static IGenocsBuilder AddMetrics(this IGenocsBuilder builder, if (string.IsNullOrWhiteSpace(appSectionName)) { - appSectionName = AppSettings.Position; + appSectionName = AppOptions.Position; } - var metricsOptions = builder.GetOptions(metricsSectionName); - var appOptions = builder.GetOptions(appSectionName); + var metricsOptions = builder.GetOptions(metricsSectionName); + var appOptions = builder.GetOptions(appSectionName); return builder.AddMetrics(metricsOptions, appOptions); } [Description("For the time being it sets Kestrel's AllowSynchronousIO = true, see https://github.com/AppMetrics/AppMetrics/issues/396")] - public static IGenocsBuilder AddMetrics(this IGenocsBuilder builder, - Func buildOptions, string appSectionName = AppSectionName) + public static IGenocsBuilder AddMetrics( + this IGenocsBuilder builder, + Func buildOptions, + string appSectionName = AppSectionName) { if (string.IsNullOrWhiteSpace(appSectionName)) { @@ -53,46 +58,47 @@ public static IGenocsBuilder AddMetrics(this IGenocsBuilder builder, } var metricsOptions = buildOptions(new MetricsOptionsBuilder()).Build(); - var appOptions = builder.GetOptions(appSectionName); + var appOptions = builder.GetOptions(appSectionName); return builder.AddMetrics(metricsOptions, appOptions); } [Description("For the time being it sets Kestrel's and IIS ServerOptions AllowSynchronousIO = true, see https://github.com/AppMetrics/AppMetrics/issues/396")] - public static IGenocsBuilder AddMetrics(this IGenocsBuilder builder, MetricsOptions metricsOptions, - AppSettings appOptions) + public static IGenocsBuilder AddMetrics( + this IGenocsBuilder builder, + Configurations.MetricsOptions metricsSettings, + AppOptions appSettings) { - builder.Services.AddSingleton(metricsOptions); - if (!builder.TryRegister(RegistryName) || !metricsOptions.Enabled || _initialized) + builder.Services.AddSingleton(metricsSettings); + if (!builder.TryRegister(RegistryName) || !metricsSettings.Enabled || _initialized) { return builder; } _initialized = true; - var metricsBuilder = new MetricsBuilder().Configuration.Configure(cfg => { - var tags = metricsOptions.Tags; + var tags = metricsSettings.Tags; if (tags is null) { return; } - tags.TryGetValue("app", out var app); - tags.TryGetValue("env", out var env); - tags.TryGetValue("server", out var server); - cfg.AddAppTag(string.IsNullOrWhiteSpace(app) ? appOptions.Service : app); + tags.TryGetValue("app", out string? app); + tags.TryGetValue("env", out string? env); + tags.TryGetValue("server", out string? server); + cfg.AddAppTag(string.IsNullOrWhiteSpace(app) ? appSettings.Service : app); cfg.AddEnvTag(string.IsNullOrWhiteSpace(env) ? null : env); cfg.AddServerTag(string.IsNullOrWhiteSpace(server) ? null : server); - if (!string.IsNullOrWhiteSpace(appOptions.Instance)) + if (!string.IsNullOrWhiteSpace(appSettings.Instance)) { - cfg.GlobalTags.Add("instance", appOptions.Instance); + cfg.GlobalTags.Add("instance", appSettings.Instance); } - if (!string.IsNullOrWhiteSpace(appOptions.Version)) + if (!string.IsNullOrWhiteSpace(appSettings.Version)) { - cfg.GlobalTags.Add("version", appOptions.Version); + cfg.GlobalTags.Add("version", appSettings.Version); } foreach (var tag in tags) @@ -109,19 +115,19 @@ public static IGenocsBuilder AddMetrics(this IGenocsBuilder builder, MetricsOpti } }); - if (metricsOptions.InfluxEnabled) + if (metricsSettings.InfluxEnabled) { metricsBuilder.Report.ToInfluxDb(o => { - o.InfluxDb.Database = metricsOptions.Database; - o.InfluxDb.BaseUri = new Uri(metricsOptions.InfluxUrl!); + o.InfluxDb.Database = metricsSettings.Database; + o.InfluxDb.BaseUri = new Uri(metricsSettings.InfluxUrl!); o.InfluxDb.CreateDataBaseIfNotExists = true; - o.FlushInterval = TimeSpan.FromSeconds(metricsOptions.Interval); + o.FlushInterval = TimeSpan.FromSeconds(metricsSettings.Interval); }); } var metrics = metricsBuilder.Build(); - var metricsWebHostOptions = GetMetricsWebHostOptions(metricsOptions); + var metricsWebHostOptions = GetMetricsWebHostOptions(metricsSettings); using var serviceProvider = builder.Services.BuildServiceProvider(); var configuration = builder.Configuration ?? serviceProvider.GetRequiredService(); @@ -140,7 +146,22 @@ public static IGenocsBuilder AddMetrics(this IGenocsBuilder builder, MetricsOpti return builder; } - private static MetricsWebHostOptions GetMetricsWebHostOptions(MetricsOptions metricsOptions) + public static IApplicationBuilder UseMetrics(this IApplicationBuilder app) + { + App.Metrics.MetricsOptions options; + using (var scope = app.ApplicationServices.CreateScope()) + { + options = scope.ServiceProvider.GetRequiredService(); + } + + return !options.Enabled + ? app + : app.UseHealthAllEndpoints() + .UseMetricsAllEndpoints() + .UseMetricsAllMiddleware(); + } + + private static MetricsWebHostOptions GetMetricsWebHostOptions(Configurations.MetricsOptions metricsOptions) { var options = new MetricsWebHostOptions(); @@ -171,19 +192,4 @@ private static MetricsWebHostOptions GetMetricsWebHostOptions(MetricsOptions met return options; } - - public static IApplicationBuilder UseMetrics(this IApplicationBuilder app) - { - MetricsOptions options; - using (var scope = app.ApplicationServices.CreateScope()) - { - options = scope.ServiceProvider.GetRequiredService(); - } - - return !options.Enabled - ? app - : app.UseHealthAllEndpoints() - .UseMetricsAllEndpoints() - .UseMetricsAllMiddleware(); - } } \ No newline at end of file diff --git a/src/Genocs.Metrics/Genocs.Metrics.csproj b/src/Genocs.Metrics/Genocs.Metrics.csproj index 18de27a1..fa0e7a00 100644 --- a/src/Genocs.Metrics/Genocs.Metrics.csproj +++ b/src/Genocs.Metrics/Genocs.Metrics.csproj @@ -1,86 +1,59 @@  - - net6.0;net7.0 - enable - enable - Genocs.Metrics - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The metrics interface library useful to build .NET Core projects. - The metrics interface library useful to build .NET Core projects. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - - - - - True - \ - - - True - \ - - - True - \ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + net8.0;net7.0;net6.0 + Genocs.Metrics + Genocs.Metrics + Genocs.Metrics + The metrics interface library useful to build .NET Core projects. + The metrics interface library useful to build .NET Core projects. + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Genocs.Metrics/Prometheus/Extensions.cs b/src/Genocs.Metrics/Prometheus/Extensions.cs index a5f744ad..022afe56 100644 --- a/src/Genocs.Metrics/Prometheus/Extensions.cs +++ b/src/Genocs.Metrics/Prometheus/Extensions.cs @@ -1,5 +1,6 @@ using Genocs.Core.Builders; using Genocs.Metrics.Prometheus.Internals; +using Genocs.Metrics.Prometheus.Options; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Prometheus; @@ -11,9 +12,9 @@ public static class Extensions { public static IGenocsBuilder AddPrometheus(this IGenocsBuilder builder) { - var prometheusOptions = builder.GetOptions("prometheus"); - builder.Services.AddSingleton(prometheusOptions); - if (!prometheusOptions.Enabled) + PrometheusOptions options = builder.GetOptions(PrometheusOptions.Position); + builder.Services.AddSingleton(options); + if (!options.Enabled) { return builder; } @@ -27,13 +28,13 @@ public static IGenocsBuilder AddPrometheus(this IGenocsBuilder builder) public static IApplicationBuilder UsePrometheus(this IApplicationBuilder app) { - var options = app.ApplicationServices.GetRequiredService(); + PrometheusOptions options = app.ApplicationServices.GetRequiredService(); if (!options.Enabled) { return app; } - var endpoint = string.IsNullOrWhiteSpace(options.Endpoint) ? "/metrics" : + string endpoint = string.IsNullOrWhiteSpace(options.Endpoint) ? "/metrics" : options.Endpoint.StartsWith("/") ? options.Endpoint : $"/{options.Endpoint}"; return app diff --git a/src/Genocs.Metrics/Prometheus/Internals/PrometheusJob.cs b/src/Genocs.Metrics/Prometheus/Internals/PrometheusJob.cs index 5aab3ed1..08232aaf 100644 --- a/src/Genocs.Metrics/Prometheus/Internals/PrometheusJob.cs +++ b/src/Genocs.Metrics/Prometheus/Internals/PrometheusJob.cs @@ -1,27 +1,28 @@ -using Microsoft.Extensions.Hosting; +using Genocs.Metrics.Prometheus.Options; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Prometheus.DotNetRuntime; namespace Genocs.Metrics.Prometheus.Internals; /// -/// The PrometheusJob that fetch metrix for Prometheus +/// The PrometheusJob that fetch metrics for Prometheus. /// internal sealed class PrometheusJob : IHostedService { - private IDisposable? _collector; private readonly ILogger _logger; private readonly bool _enabled; + private IDisposable? _collector; /// - /// Default PrometheusJob Constructor + /// Default PrometheusJob Constructor. /// /// /// public PrometheusJob(PrometheusOptions options, ILogger logger) { _enabled = options.Enabled; - _logger = logger; + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger.LogInformation($"Prometheus integration is {(_enabled ? "enabled" : "disabled")}."); } diff --git a/src/Genocs.Metrics/Prometheus/Internals/PrometheusMiddleware.cs b/src/Genocs.Metrics/Prometheus/Internals/PrometheusMiddleware.cs index 624538eb..1c5464e8 100644 --- a/src/Genocs.Metrics/Prometheus/Internals/PrometheusMiddleware.cs +++ b/src/Genocs.Metrics/Prometheus/Internals/PrometheusMiddleware.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Http; +using Genocs.Metrics.Prometheus.Options; +using Microsoft.AspNetCore.Http; namespace Genocs.Metrics.Prometheus.Internals; @@ -6,7 +7,7 @@ internal sealed class PrometheusMiddleware : IMiddleware { private readonly ISet _allowedHosts; private readonly string _endpoint; - private readonly string _apiKey; + private readonly string? _apiKey; public PrometheusMiddleware(PrometheusOptions options) { @@ -34,7 +35,7 @@ public Task InvokeAsync(HttpContext context, RequestDelegate next) return next(context); } - var host = context.Request.Host.Host; + string host = context.Request.Host.Host; if (_allowedHosts.Contains(host)) { return next(context); diff --git a/src/Genocs.Metrics/Prometheus/Options/PrometheusOptions.cs b/src/Genocs.Metrics/Prometheus/Options/PrometheusOptions.cs new file mode 100644 index 00000000..2b54fc5c --- /dev/null +++ b/src/Genocs.Metrics/Prometheus/Options/PrometheusOptions.cs @@ -0,0 +1,32 @@ +namespace Genocs.Metrics.Prometheus.Options; + +/// +/// The Prometheus Setting definition. +/// +public class PrometheusOptions +{ + /// + /// Default section name. + /// + public const string Position = "prometheus"; + + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + /// + /// The Prometheus endpoint. + /// + public string? Endpoint { get; set; } + + /// + /// The Prometheus ApiKey. + /// + public string? ApiKey { get; set; } + + /// + /// The Prometheus AllowedHosts. + /// + public IEnumerable? AllowedHosts { get; set; } +} \ No newline at end of file diff --git a/src/Genocs.Metrics/Prometheus/PrometheusOptions.cs b/src/Genocs.Metrics/Prometheus/PrometheusOptions.cs deleted file mode 100644 index f962e198..00000000 --- a/src/Genocs.Metrics/Prometheus/PrometheusOptions.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; - -namespace Genocs.Metrics.Prometheus; - -public class PrometheusOptions -{ - public bool Enabled { get; set; } - public string Endpoint { get; set; } - public string ApiKey { get; set; } - public IEnumerable AllowedHosts { get; set; } -} \ No newline at end of file diff --git a/src/Genocs.Metrics/README_NUGET.md b/src/Genocs.Metrics/README_NUGET.md new file mode 100644 index 00000000..98dc7734 --- /dev/null +++ b/src/Genocs.Metrics/README_NUGET.md @@ -0,0 +1,60 @@ +# .NET Core Base library + +This package contains a set of base functionalities designed by Genocs. +The libraries are built using .NET8 + + +## Description + +Genocs.Metrics NuGet package contains general purpose functionalities to be used on DDD services. + + +## Support + +Please check the GitHub repository getting more info. + + +## Release notes + +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 +- Implemented MongoDB repository interfaces + +### [2023-03-12] 5.0.0 +- New Architecture + +### [2023-03-12] 3.1.0 +- Added Builders + +### [2023-03-12] 3.0.0 +- Refactory to implement CQRS pattern + +### [2023-03-04] 2.4.1 +- Updated System.Text.Json diff --git a/src/Genocs.Monitoring/Constants.cs b/src/Genocs.Monitoring/Constants.cs index e8180ba5..ab6a549e 100644 --- a/src/Genocs.Monitoring/Constants.cs +++ b/src/Genocs.Monitoring/Constants.cs @@ -1,9 +1,7 @@ -namespace Genocs.Monitoring +namespace Genocs.Monitoring; + +internal static class Constants { - internal static class Constants - { - public const string ApplicationInsightsConnectionString = "ApplicationInsights"; - public const string MongoDbConnectionString = "MongoDb"; - public const string ServiceName = "AppSettings:ServiceName"; - } + public const string ApplicationInsightsConnectionString = "ApplicationInsights"; + public const string ServiceName = "AppSettings:ServiceName"; } diff --git a/src/Genocs.Monitoring/Genocs.Monitoring.csproj b/src/Genocs.Monitoring/Genocs.Monitoring.csproj index 14e5806f..0d5d7ff6 100644 --- a/src/Genocs.Monitoring/Genocs.Monitoring.csproj +++ b/src/Genocs.Monitoring/Genocs.Monitoring.csproj @@ -1,65 +1,54 @@  - - net6.0;net7.0 - enable - enable - Genocs.Monitoring - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - Telemetry and Tracing library. - Telemetry and Tracing library. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.Monitoring + Genocs.Monitoring + Genocs.Monitoring + Telemetry and Tracing library. + Telemetry and Tracing library. + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + - - - True - \ - - - True - \ - - - True - \ - - + + + True + \ + + + True + \ + + + True + \ + + - - - - + + + + - + - - - - - - - - - - - - + + + + + + + + + + + diff --git a/src/Genocs.Monitoring/OpenTelemetryInitializer.cs b/src/Genocs.Monitoring/OpenTelemetryInitializer.cs index 24e60984..a8ea1f30 100644 --- a/src/Genocs.Monitoring/OpenTelemetryInitializer.cs +++ b/src/Genocs.Monitoring/OpenTelemetryInitializer.cs @@ -6,77 +6,74 @@ using OpenTelemetry.Resources; using OpenTelemetry.Trace; -namespace Genocs.Monitoring +namespace Genocs.Monitoring; + +/// +/// The Open Telemetry and Tracing. +/// +public static class OpenTelemetryInitializer { /// - /// The Open Telemetry and Tracing + /// Custom settings for OpenTelemetry. /// - public static class OpenTelemetryInitializer + /// + /// + /// + public static IServiceCollection AddCustomOpenTelemetry(this IServiceCollection services, IConfiguration configuration) { - /// - /// Custom settings for OpenTelemetry - /// - /// - /// - /// - public static IServiceCollection AddCustomOpenTelemetry(this IServiceCollection services, IConfiguration configuration) - { - // Read Settings - string applicationInsightsConnectionString = configuration.GetConnectionString(Constants.ApplicationInsightsConnectionString); - string serviceName = configuration.GetValue(Constants.ServiceName) ?? "IntegrationsWorker"; - - MonitoringSettings settings = new MonitoringSettings(); - configuration.Bind(MonitoringSettings.Position, settings); - services.AddSingleton(settings); + // Read Settings + string? applicationInsightsConnectionString = configuration.GetConnectionString(Constants.ApplicationInsightsConnectionString); + string serviceName = configuration.GetValue(Constants.ServiceName) ?? "IntegrationsWorker"; + MonitoringSettings settings = new MonitoringSettings(); + configuration.Bind(MonitoringSettings.Position, settings); + services.AddSingleton(settings); - // No OpenTelemetryTracing in case of missing ServiceName - if (string.IsNullOrWhiteSpace(serviceName)) return services; - - // Set Custom Open telemetry - services.AddOpenTelemetry().WithTracing(builder => - { - TracerProviderBuilder provider = builder.SetResourceBuilder(ResourceBuilder.CreateDefault() - .AddService(serviceName) - .AddTelemetrySdk() - .AddEnvironmentVariableDetector()) - .AddSource("*"); + // No OpenTelemetryTracing in case of missing ServiceName + if (string.IsNullOrWhiteSpace(serviceName)) return services; - // Remove comment below to enable tracing on console - // you should add MongoDB.Driver.Core.Extensions.OpenTelemetry nuget package - provider.AddMongoDBInstrumentation(); + // Set Custom Open telemetry + services.AddOpenTelemetry().WithTracing(builder => + { + TracerProviderBuilder provider = builder.SetResourceBuilder(ResourceBuilder.CreateDefault() + .AddService(serviceName) + .AddTelemetrySdk() + .AddEnvironmentVariableDetector()) + .AddSource("*"); - // Remove comment below to enable tracing on console - // you should add OpenTelemetry.Exporter.Console nuget package - provider.AddConsoleExporter(); + // Remove comment below to enable tracing on console + // you should add MongoDB.Driver.Core.Extensions.OpenTelemetry NuGet package + provider.AddMongoDBInstrumentation(); + // Remove comment below to enable tracing on console + // you should add OpenTelemetry.Exporter.Console NuGet package + provider.AddConsoleExporter(); - // Check for Azure ApplicationInsights - if (!string.IsNullOrWhiteSpace(applicationInsightsConnectionString)) + // Check for Azure ApplicationInsights. + if (!string.IsNullOrWhiteSpace(applicationInsightsConnectionString)) + { + provider.AddAzureMonitorTraceExporter(o => { - provider.AddAzureMonitorTraceExporter(o => - { - o.ConnectionString = applicationInsightsConnectionString; - }); - } + o.ConnectionString = applicationInsightsConnectionString; + }); + } - provider.AddJaegerExporter(o => + provider.AddJaegerExporter(o => + { + o.AgentHost = settings.Jaeger; + o.AgentPort = 6831; + o.MaxPayloadSizeInBytes = 4096; + o.ExportProcessorType = ExportProcessorType.Batch; + o.BatchExportProcessorOptions = new BatchExportProcessorOptions { - o.AgentHost = settings.Jaeger; - o.AgentPort = 6831; - o.MaxPayloadSizeInBytes = 4096; - o.ExportProcessorType = ExportProcessorType.Batch; - o.BatchExportProcessorOptions = new BatchExportProcessorOptions - { - MaxQueueSize = 2048, - ScheduledDelayMilliseconds = 5000, - ExporterTimeoutMilliseconds = 30000, - MaxExportBatchSize = 512, - }; - }); + MaxQueueSize = 2048, + ScheduledDelayMilliseconds = 5000, + ExporterTimeoutMilliseconds = 30000, + MaxExportBatchSize = 512, + }; }); + }); - return services; - } + return services; } } diff --git a/src/Genocs.Monitoring/Options/MonitoringSettings.cs b/src/Genocs.Monitoring/Options/MonitoringSettings.cs index cfaa026e..6d0ea9d2 100644 --- a/src/Genocs.Monitoring/Options/MonitoringSettings.cs +++ b/src/Genocs.Monitoring/Options/MonitoringSettings.cs @@ -1,20 +1,17 @@ -namespace Genocs.Monitoring.Options +namespace Genocs.Monitoring.Options; + +/// +/// The monitoring settings. +/// +public class MonitoringSettings { /// - /// The monitoring settings + /// The static position. /// - public class MonitoringSettings - { - /// - /// The static position - /// - public const string Position = "Monitoring"; + public const string Position = "Monitoring"; - /// - /// Jaeger url - /// - public string Jaeger { get; set; } = "localhost"; - } + /// + /// Jaeger url. + /// + public string Jaeger { get; set; } = "localhost"; } - - diff --git a/src/Genocs.Monitoring/README.md b/src/Genocs.Monitoring/README_NUGET.md similarity index 77% rename from src/Genocs.Monitoring/README.md rename to src/Genocs.Monitoring/README_NUGET.md index 8bbebb85..d21b702d 100644 --- a/src/Genocs.Monitoring/README.md +++ b/src/Genocs.Monitoring/README_NUGET.md @@ -35,9 +35,31 @@ Following are the project settings needed to enable monitoring ## Release notes -### [2023-03-04] 2.0.1 +### [2024-01-23] 5.0.6 +- Refactory Settings - Updated nuget packages +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-03-04] 2.0.1 +- Updated NuGet packages + ### [2023-03-04] 2.0.0 - Revisited @@ -51,7 +73,7 @@ Following are the project settings needed to enable monitoring - Moved to netstandart ### [2023-01-01] 1.0.0-rc2.0 -- Refactory and standardization +- Refactor and standardization diff --git a/src/Genocs.Persistence.MongoDb.UnitTests/EncryptionUnitTest.cs b/src/Genocs.Persistence.MongoDb.UnitTests/EncryptionUnitTest.cs index c799988e..abced899 100644 --- a/src/Genocs.Persistence.MongoDb.UnitTests/EncryptionUnitTest.cs +++ b/src/Genocs.Persistence.MongoDb.UnitTests/EncryptionUnitTest.cs @@ -1,9 +1,4 @@ -using Genocs.Persistence.MongoDb.Encryptions; -using Genocs.Persistence.MongoDb.Options; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Options; -using Moq; -using Xunit; namespace Genocs.Persistence.MongoDb.UnitTests; diff --git a/src/Genocs.Persistence.MongoDb.UnitTests/Genocs.Persistence.MongoDB.UnitTests.csproj b/src/Genocs.Persistence.MongoDb.UnitTests/Genocs.Persistence.MongoDB.UnitTests.csproj index fe7c0b2a..b564f04d 100644 --- a/src/Genocs.Persistence.MongoDb.UnitTests/Genocs.Persistence.MongoDB.UnitTests.csproj +++ b/src/Genocs.Persistence.MongoDb.UnitTests/Genocs.Persistence.MongoDB.UnitTests.csproj @@ -1,36 +1,34 @@  - - net7.0 - enable - enable - false - false - bf5e372b-b320-4d11-beb8-a793edae0ad4 - - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - + + net8.0 + false + false + Genocs + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + - - - - - - - + + + diff --git a/src/Genocs.Persistence.MongoDb/Builders/MongoDbOptionsBuilder.cs b/src/Genocs.Persistence.MongoDb/Builders/MongoDbOptionsBuilder.cs index 99e3a861..3e2b8258 100644 --- a/src/Genocs.Persistence.MongoDb/Builders/MongoDbOptionsBuilder.cs +++ b/src/Genocs.Persistence.MongoDb/Builders/MongoDbOptionsBuilder.cs @@ -1,11 +1,11 @@ -using Genocs.Persistence.MongoDb.Options; +using Genocs.Persistence.MongoDb.Configurations; using Genocs.Persistence.MongoDb.Repositories; namespace Genocs.Persistence.MongoDb.Builders; internal sealed class MongoDbOptionsBuilder : IMongoDbOptionsBuilder { - private readonly MongoDbSettings _options = new(); + private readonly MongoDbOptions _options = new(); public IMongoDbOptionsBuilder WithConnectionString(string connectionString) { @@ -25,6 +25,6 @@ public IMongoDbOptionsBuilder WithSeed(bool seed) return this; } - public MongoDbSettings Build() + public MongoDbOptions Build() => _options; } \ No newline at end of file diff --git a/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbOptionsBuilder.cs b/src/Genocs.Persistence.MongoDb/Configurations/IMongoDbOptionsBuilder.cs similarity index 69% rename from src/Genocs.Persistence.MongoDb/Repositories/IMongoDbOptionsBuilder.cs rename to src/Genocs.Persistence.MongoDb/Configurations/IMongoDbOptionsBuilder.cs index a0fbbf6a..b5c08653 100644 --- a/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbOptionsBuilder.cs +++ b/src/Genocs.Persistence.MongoDb/Configurations/IMongoDbOptionsBuilder.cs @@ -1,36 +1,36 @@ -using Genocs.Persistence.MongoDb.Options; +using Genocs.Persistence.MongoDb.Configurations; namespace Genocs.Persistence.MongoDb.Repositories; /// -/// The MongoDB Options Builder +/// The MongoDB Options Builder. /// public interface IMongoDbOptionsBuilder { /// - /// Setup the Connection string + /// Setup the Connection string. /// /// /// IMongoDbOptionsBuilder WithConnectionString(string connectionString); /// - /// Setup the Database name + /// Setup the Database name. /// /// /// IMongoDbOptionsBuilder WithDatabase(string database); /// - /// Setup the database Seed + /// Setup the database Seed. /// /// /// IMongoDbOptionsBuilder WithSeed(bool seed); /// - /// Get the settings + /// Get the settings. /// - /// MongoDbSettings instance - MongoDbSettings Build(); + /// MongoDbSettings instance. + MongoDbOptions Build(); } \ No newline at end of file diff --git a/src/Genocs.Persistence.MongoDb/Configurations/MongoDbEncryptionOptions.cs b/src/Genocs.Persistence.MongoDb/Configurations/MongoDbEncryptionOptions.cs new file mode 100644 index 00000000..dca2d18b --- /dev/null +++ b/src/Genocs.Persistence.MongoDb/Configurations/MongoDbEncryptionOptions.cs @@ -0,0 +1,73 @@ +namespace Genocs.Persistence.MongoDb.Configurations; + +/// +/// MongoDb encryption database Settings. +/// +public class MongoDbEncryptionOptions +{ + /// + /// Default section name. + /// + public const string Position = "mongoDbEncryption"; + + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + /// + /// The Database connection string. + /// + public string ConnectionString { get; set; } = default!; + + /// + /// The shared library used to encrypt. + /// + public string LibPath { get; set; } = default!; + + /// + /// Azure Tenant Id. + /// + public string TenantId { get; set; } = default!; + + /// + /// Azure Client Id. + /// + public string ClientId { get; set; } = default!; + + /// + /// Azure Client Secret. + /// + public string ClientSecret { get; set; } = default!; + + /// + /// Azure Client Secret. + /// + public string KeyName { get; set; } = default!; + + /// + /// Azure Client Secret. + /// + public string KeyVersion { get; set; } = default!; + + /// + /// Azure Client Secret. + /// + public string KeyVaultEndpoint { get; set; } = default!; + + /// + /// Check if the MongoDbSettings object contains valid data. + /// + /// MongoDbSettings object. + /// return true if valid otherwise false. + public static bool IsValid(MongoDbEncryptionOptions settings) + { + if (settings is null) return false; + + if (string.IsNullOrWhiteSpace(settings.ConnectionString)) return false; + if (string.IsNullOrWhiteSpace(settings.LibPath)) return false; + + return true; + + } +} diff --git a/src/Genocs.Persistence.MongoDb/Configurations/MongoDbOptions.cs b/src/Genocs.Persistence.MongoDb/Configurations/MongoDbOptions.cs new file mode 100644 index 00000000..67c2d753 --- /dev/null +++ b/src/Genocs.Persistence.MongoDb/Configurations/MongoDbOptions.cs @@ -0,0 +1,55 @@ +using System.ComponentModel; + +namespace Genocs.Persistence.MongoDb.Configurations; + +/// +/// MongoDb database Settings. +/// +public class MongoDbOptions +{ + /// + /// Default Section name. + /// + public const string Position = "mongoDb"; + + /// + /// The Database connection string. + /// + public string ConnectionString { get; set; } = default!; + + /// + /// The database name where the data will be stored. + /// + public string Database { get; set; } = default!; + + /// + /// Toggle database tracing. + /// + public bool EnableTracing { get; set; } + + /// + /// It defines if the database seed is applied. + /// + public bool Seed { get; set; } + + /// + /// It defines if random database name suffix is added. + /// + [Description("Might be helpful for the integration testing.")] + public bool SetRandomDatabaseSuffix { get; set; } + + /// + /// Check if the MongoDbSettings object contains valid data. + /// + /// MongoDbSettings object. + /// return true if valid otherwise false. + public static bool IsValid(MongoDbOptions settings) + { + if (settings is null) return false; + + if (string.IsNullOrWhiteSpace(settings.ConnectionString)) return false; + if (string.IsNullOrWhiteSpace(settings.Database)) return false; + + return true; + } +} diff --git a/src/Genocs.Persistence.MongoDb/Domain/Entities/IMongoDbEntity.cs b/src/Genocs.Persistence.MongoDb/Domain/Entities/IMongoDbEntity.cs new file mode 100644 index 00000000..0f38ca67 --- /dev/null +++ b/src/Genocs.Persistence.MongoDb/Domain/Entities/IMongoDbEntity.cs @@ -0,0 +1,12 @@ +using Genocs.Core.Domain.Entities; +using MongoDB.Bson; + +namespace Genocs.Persistence.MongoDb.Domain.Entities; + +/// +/// Default MongoDB entity. +/// +public interface IMongoDbEntity : IEntity +{ + +} diff --git a/src/Genocs.Persistence.MongoDb/Repositories/IMongoRepository.cs b/src/Genocs.Persistence.MongoDb/Domain/Repositories/IMongoDbBaseRepository.cs similarity index 69% rename from src/Genocs.Persistence.MongoDb/Repositories/IMongoRepository.cs rename to src/Genocs.Persistence.MongoDb/Domain/Repositories/IMongoDbBaseRepository.cs index 7fdb11df..5cdacef2 100644 --- a/src/Genocs.Persistence.MongoDb/Repositories/IMongoRepository.cs +++ b/src/Genocs.Persistence.MongoDb/Domain/Repositories/IMongoDbBaseRepository.cs @@ -1,13 +1,14 @@ -using Genocs.Common.Types; +using System.Linq.Expressions; using Genocs.Core.CQRS.Queries; +using Genocs.Core.Domain.Entities; using Genocs.Core.Domain.Repositories; using MongoDB.Driver; using MongoDB.Driver.Linq; -using System.Linq.Expressions; -namespace Genocs.Persistence.MongoDb.Repositories; +namespace Genocs.Persistence.MongoDb.Domain.Repositories; -public interface IMongoRepository : IRepositoryOfEntity where TEntity : IIdentifiable +public interface IMongoDbBaseRepository : IRepositoryOfEntity + where TEntity : IEntity { IMongoCollection Collection { get; } @@ -16,10 +17,12 @@ public interface IMongoRepository : IRepositoryOfEntity Task GetAsync(Expression> predicate); Task> FindAsync(Expression> predicate); - Task> BrowseAsync(Expression> predicate, TQuery query) where TQuery : IPagedQuery; + Task> BrowseAsync(Expression> predicate, TQuery query) + where TQuery : IPagedQuery; Task AddAsync(TEntity entity); Task UpdateAsync(TEntity entity, Expression> predicate); + Task ExistsAsync(Expression> predicate); } \ No newline at end of file diff --git a/src/Genocs.Persistence.MongoDb/Domain/Repositories/IMongoDbRepository.cs b/src/Genocs.Persistence.MongoDb/Domain/Repositories/IMongoDbRepository.cs new file mode 100644 index 00000000..c8ff528f --- /dev/null +++ b/src/Genocs.Persistence.MongoDb/Domain/Repositories/IMongoDbRepository.cs @@ -0,0 +1,15 @@ +using Genocs.Persistence.MongoDb.Domain.Entities; +using MongoDB.Bson; + +namespace Genocs.Persistence.MongoDb.Domain.Repositories; + +/// +/// The MongoDb repository interface. +/// This interface is used to define the contract for the MongoDb repositories when the entity has an ObjectId as the primary key. +/// +/// The type of the entity. +public interface IMongoDbRepository : IMongoDbBaseRepository + where TEntity : IMongoDbEntity +{ + +} diff --git a/src/Genocs.Persistence.MongoDb/Repositories/MongoRepository.cs b/src/Genocs.Persistence.MongoDb/Domain/Repositories/MongoDbBaseRepository.cs similarity index 69% rename from src/Genocs.Persistence.MongoDb/Repositories/MongoRepository.cs rename to src/Genocs.Persistence.MongoDb/Domain/Repositories/MongoDbBaseRepository.cs index 98cb51bd..d7f91e79 100644 --- a/src/Genocs.Persistence.MongoDb/Repositories/MongoRepository.cs +++ b/src/Genocs.Persistence.MongoDb/Domain/Repositories/MongoDbBaseRepository.cs @@ -1,17 +1,17 @@ -using Genocs.Common.Types; using Genocs.Core.CQRS.Queries; +using Genocs.Core.Domain.Entities; using Genocs.Core.Domain.Repositories; +using Genocs.Persistence.MongoDb.Repositories; using MongoDB.Driver; using MongoDB.Driver.Linq; -using System; using System.Linq.Expressions; -namespace Genocs.Persistence.MongoDb.Repositories; +namespace Genocs.Persistence.MongoDb.Domain.Repositories; -internal class MongoRepository : IMongoRepository - where TEntity : IIdentifiable +internal class MongoDbBaseRepository : IMongoDbBaseRepository + where TEntity : IEntity { - public MongoRepository(IMongoDatabase database, string collectionName) + public MongoDbBaseRepository(IMongoDatabase database, string collectionName) { Collection = database.GetCollection(collectionName); } @@ -19,7 +19,7 @@ public MongoRepository(IMongoDatabase database, string collectionName) public IMongoCollection Collection { get; } /// - /// It returns the Mongo Collection as Queryable + /// It returns the Mongo Collection as Queryable. /// /// public IMongoQueryable GetMongoQueryable() @@ -27,7 +27,7 @@ public IMongoQueryable GetMongoQueryable() return Collection.AsQueryable(); } - public Task GetAsync(TIdentifiable id) + public Task GetAsync(TKey id) => GetAsync(e => e.Id.Equals(id)); public Task GetAsync(Expression> predicate) @@ -36,12 +36,12 @@ public Task GetAsync(Expression> predicate) public async Task> FindAsync(Expression> predicate) => await Collection.Find(predicate).ToListAsync(); - public Task> BrowseAsync(Expression> predicate, - TQuery query) where TQuery : IPagedQuery + public Task> BrowseAsync(Expression> predicate, TQuery query) + where TQuery : IPagedQuery => Collection.AsQueryable().Where(predicate).PaginateAsync(query); /// - /// It adds an entity to the Mongo Collection + /// It adds an entity to the Mongo Collection. /// /// /// @@ -49,28 +49,28 @@ public Task AddAsync(TEntity entity) => Collection.InsertOneAsync(entity); /// - /// It updates an entity in the Mongo Collection + /// It updates an entity in the Mongo Collection. /// - /// The entity - /// The updated entity + /// The entity. + /// The updated entity. public Task UpdateAsync(TEntity entity) => UpdateAsync(entity, e => e.Id.Equals(entity.Id)); /// - /// It updates an entity in the Mongo Collection in async mode + /// It updates an entity in the Mongo Collection in async mode. /// - /// The entity - /// The predicate - /// The updated entity + /// The entity. + /// The predicate. + /// The updated entity. public Task UpdateAsync(TEntity entity, Expression> predicate) => Collection.ReplaceOneAsync(predicate, entity); /// - /// It deletes an entity from the Mongo Collection in async mode + /// It deletes an entity from the Mongo Collection in async mode. /// /// /// - public Task DeleteAsync(TIdentifiable id) + public Task DeleteAsync(TKey id) => DeleteAsync(e => e.Id.Equals(id)); public Task DeleteAsync(Expression> predicate) @@ -83,9 +83,7 @@ public IQueryable GetAll() => Collection.AsQueryable(); public IQueryable GetAllIncluding(params Expression>[] propertySelectors) - { - throw new NotImplementedException(); - } + => GetAll(); public List GetAllList() => Collection.AsQueryable().ToList(); @@ -118,7 +116,7 @@ public T Query(Func, T> queryMethod) throw new NotImplementedException(); } - public TEntity Get(TIdentifiable id) + public TEntity Get(TKey id) => Collection.Find(c => c.Id.Equals(id)).First(); public TEntity Single(Expression> predicate) @@ -130,10 +128,10 @@ public async Task SingleAsync(Expression> predicate return await result.SingleAsync(); } - public TEntity FirstOrDefault(TIdentifiable id) + public TEntity? FirstOrDefault(TKey id) => Collection.Find(c => c.Id.Equals(id)).FirstOrDefault(); - public async Task FirstOrDefaultAsync(TIdentifiable id) + public async Task FirstOrDefaultAsync(TKey id) { var result = await Collection.FindAsync(c => c.Id.Equals(id)); return await result.FirstOrDefaultAsync(); @@ -142,16 +140,14 @@ public async Task FirstOrDefaultAsync(TIdentifiable id) public TEntity FirstOrDefault(Expression> predicate) => Collection.Find(predicate).FirstOrDefault(); - public async Task FirstOrDefaultAsync(Expression> predicate) + public async Task FirstOrDefaultAsync(Expression> predicate) { var result = await Collection.FindAsync(predicate); return await result.FirstOrDefaultAsync(); } - public TEntity Load(TIdentifiable id) - { - throw new NotImplementedException(); - } + public TEntity? Load(TKey id) + => FirstOrDefault(id); public TEntity Insert(TEntity entity) { @@ -165,52 +161,42 @@ public async Task InsertAsync(TEntity entity) return entity; } - public TIdentifiable InsertAndGetId(TEntity entity) - { - throw new NotImplementedException(); - } + public TKey InsertAndGetId(TEntity entity) + => Insert(entity).Id; - public Task InsertAndGetIdAsync(TEntity entity) - { - throw new NotImplementedException(); - } + public async Task InsertAndGetIdAsync(TEntity entity) + => (await InsertAsync(entity)).Id; public TEntity InsertOrUpdate(TEntity entity) { - Collection.ReplaceOne(c => c.Id.Equals(entity.Id), entity, new ReplaceOptions { IsUpsert = true }); + Collection.ReplaceOne(c => c.Id!.Equals(entity.Id), entity, new ReplaceOptions { IsUpsert = true }); return entity; } public async Task InsertOrUpdateAsync(TEntity entity) { - await Collection.ReplaceOneAsync(c => c.Id.Equals(entity.Id), entity, new ReplaceOptions { IsUpsert = true }); + await Collection.ReplaceOneAsync(c => c.Id!.Equals(entity.Id), entity, new ReplaceOptions { IsUpsert = true }); return entity; } - public TIdentifiable InsertOrUpdateAndGetId(TEntity entity) - { - throw new NotImplementedException(); - } - - public Task InsertOrUpdateAndGetIdAsync(TEntity entity) - { + public TKey InsertOrUpdateAndGetId(TEntity entity) + => InsertOrUpdate(entity).Id; - throw new NotImplementedException(); - } + public async Task InsertOrUpdateAndGetIdAsync(TEntity entity) + => (await InsertOrUpdateAsync(entity)).Id; public TEntity Update(TEntity entity) { - Collection.ReplaceOne(c => c.Id.Equals(entity.Id), entity); + Collection.ReplaceOne(c => c.Id!.Equals(entity.Id), entity); return entity; } - - public TEntity Update(TIdentifiable id, Action updateAction) + public TEntity Update(TKey id, Action updateAction) { throw new NotImplementedException(); } - public Task UpdateAsync(TIdentifiable id, Func updateAction) + public Task UpdateAsync(TKey id, Func updateAction) { throw new NotImplementedException(); } @@ -227,24 +213,24 @@ public Task DeleteAsync(TEntity entity) { throw new ArgumentNullException(nameof(entity)); } + return DeleteAsync(entity.Id); } - public void Delete(TIdentifiable id) + public void Delete(TKey id) { if (id == null) { throw new ArgumentNullException(nameof(id)); } - Collection.DeleteOne(c => c.Id.Equals(id)); + + Collection.DeleteOne(c => c.Id!.Equals(id)); } public void Delete(Expression> predicate) { - if (predicate == null) - { - throw new ArgumentNullException(nameof(predicate)); - } + ArgumentNullException.ThrowIfNull(predicate); + Collection.DeleteMany(predicate); } @@ -272,9 +258,9 @@ public long LongCount(Expression> predicate) public async Task LongCountAsync(Expression> predicate) => await Collection.CountDocumentsAsync(predicate); - async Task IRepositoryOfEntity.UpdateAsync(TEntity entity) + async Task IRepositoryOfEntity.UpdateAsync(TEntity entity) { - await UpdateAsync(entity, e => e.Id.Equals(entity.Id)); + await UpdateAsync(entity, e => e.Id!.Equals(entity.Id)); return entity; } } \ No newline at end of file diff --git a/src/Genocs.Persistence.MongoDb/Domain/Repositories/MongoDbBaseRepositoryOfType.cs b/src/Genocs.Persistence.MongoDb/Domain/Repositories/MongoDbBaseRepositoryOfType.cs new file mode 100644 index 00000000..e498574a --- /dev/null +++ b/src/Genocs.Persistence.MongoDb/Domain/Repositories/MongoDbBaseRepositoryOfType.cs @@ -0,0 +1,175 @@ +using Genocs.Core.CQRS.Queries; +using Genocs.Core.Domain.Entities; +using Genocs.Core.Domain.Repositories; +using Genocs.Persistence.MongoDb.Repositories; +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using System.Linq.Expressions; + +namespace Genocs.Persistence.MongoDb.Domain.Repositories; + +/// +/// Implements IRepository for MongoDB. +/// +/// Type of the Entity for this repository. +/// Primary key of the entity. +public class MongoDbBaseRepositoryOfType : RepositoryBase, IMongoDbBaseRepository + where TEntity : IEntity +{ + private readonly IMongoDatabaseProvider _databaseProvider; + protected IMongoCollection? _collection; + + /// + /// Standard constructor. + /// + /// + public MongoDbBaseRepositoryOfType(IMongoDatabaseProvider databaseProvider) + { + _databaseProvider = databaseProvider; + } + + /// + /// Get the MongoDB database. + /// + public virtual IMongoDatabase Database + { + get { return _databaseProvider.Database; } + } + + /// + /// Get the MongoDB collection from a custom attribute or from the entity name. + /// + public virtual IMongoCollection Collection + { + get + { + if (_collection != null) + { + return _collection; + } + + Attribute[] attrs = Attribute.GetCustomAttributes(typeof(TEntity)); // Reflection. + + // Displaying output. + foreach (var attr in attrs) + { + if (attr != null) + { + if ((attr != null) && attr is TableMappingAttribute tmp) + { + return _databaseProvider.Database.GetCollection(tmp.Name); + } + } + } + + _collection = _databaseProvider.Database.GetCollection(typeof(TEntity).Name); + + return _collection; + } + } + + /// + /// Get all entities as IQueryable. + /// + /// + public override IQueryable GetAll() + => Collection.AsQueryable(); + + /// + /// Get single entity. + /// + /// + /// + /// It is thrown if the entity is not found. + public override TEntity Get(TKey id) + { + var filter = Builders.Filter.Eq(m => m.Id, id); + var entity = Collection.Find(filter).FirstOrDefault(); + return entity == null + ? throw new EntityNotFoundException("There is no such an entity with given primary key. Entity type: " + typeof(TEntity).FullName + ", primary key: " + id) + : entity; + } + + /// + /// First Or Default entity. + /// + /// The domain objjet id. + /// The entity if found otherwise null. + public override TEntity FirstOrDefault(TKey id) + { + var filter = Builders.Filter.Eq(m => m.Id, id); + return Collection.Find(filter).FirstOrDefault(); + } + + /// + /// Insert an entity. + /// + /// The entity to insert. + /// The entity. + public override TEntity Insert(TEntity entity) + { + Collection.InsertOne(entity); + return entity; + } + + /// + /// Update an existing entity. + /// + /// The entity to insert. + /// The entity. + public override TEntity Update(TEntity entity) + { + Collection.ReplaceOneAsync(filter: g => g.Id.Equals(entity.Id), replacement: entity); + return entity; + } + + /// + /// Delete entity, passing the entire object. + /// + /// + public override void Delete(TEntity entity) + => Delete(entity.Id); + + /// + /// Delete entity by primary key. + /// + /// + public override void Delete(TKey id) + { + var query = Builders.Filter.Eq(m => m.Id, id); + var deleteResult = Collection.DeleteOneAsync(query).Result; + } + + /// + /// It returns the Mongo Collection as Queryable. + /// + /// + public IMongoQueryable GetMongoQueryable() + => Collection.AsQueryable(); + + public async Task GetAsync(Expression> predicate) + => await Collection.AsQueryable().Where(predicate).FirstAsync(); + + public async Task> FindAsync(Expression> predicate) + => await Collection.AsQueryable().Where(predicate).ToListAsync(); + + /// + /// Query data from the Mongo Collection and convert it to a PagedResult. + /// + /// The query type. + /// The predicate. + /// The query. + /// The paged result. + public async Task> BrowseAsync(Expression> predicate, TQuery query) + where TQuery : IPagedQuery + => await Collection.AsQueryable().Where(predicate).PaginateAsync(query); + + public async Task AddAsync(TEntity entity) + => await Collection.InsertOneAsync(entity); + + public async Task UpdateAsync(TEntity entity, Expression> predicate) + => await Collection.ReplaceOneAsync(predicate, entity); + + public async Task ExistsAsync(Expression> predicate) + => await Collection.AsQueryable().Where(predicate).AnyAsync(); +} \ No newline at end of file diff --git a/src/Genocs.Persistence.MongoDb/Domain/Repositories/MongoDbRepository.cs b/src/Genocs.Persistence.MongoDb/Domain/Repositories/MongoDbRepository.cs new file mode 100644 index 00000000..67f8a26a --- /dev/null +++ b/src/Genocs.Persistence.MongoDb/Domain/Repositories/MongoDbRepository.cs @@ -0,0 +1,21 @@ +using Genocs.Persistence.MongoDb.Domain.Entities; +using MongoDB.Bson; + +namespace Genocs.Persistence.MongoDb.Domain.Repositories; + +/// +/// Implements IRepository for MongoDB. +/// +/// Type of the Entity for this repository. +public class MongoDbRepository : MongoDbBaseRepositoryOfType, IMongoDbRepository + where TEntity : IMongoDbEntity +{ + /// + /// The standard constructor. + /// + /// The database provider. + public MongoDbRepository(IMongoDatabaseProvider databaseProvider) + : base(databaseProvider) + { + } +} \ No newline at end of file diff --git a/src/Genocs.Persistence.MongoDb/Encryptions/AzureInitializer.cs b/src/Genocs.Persistence.MongoDb/Encryptions/AzureInitializer.cs index ed2200f7..a9e71c14 100644 --- a/src/Genocs.Persistence.MongoDb/Encryptions/AzureInitializer.cs +++ b/src/Genocs.Persistence.MongoDb/Encryptions/AzureInitializer.cs @@ -1,4 +1,4 @@ -using Genocs.Persistence.MongoDb.Options; +using Genocs.Persistence.MongoDb.Configurations; using Microsoft.Extensions.Options; using MongoDB.Bson; using MongoDB.Driver; @@ -15,11 +15,11 @@ public class AzureInitializer /// Setup the client /// /// - public AutoEncryptionOptions EncryptionOptions(IOptions options) + public AutoEncryptionOptions EncryptionOptions(IOptions options) { // Ge settings - MongoDbEncryptionSettings settings = options.Value; - MongoDbEncryptionSettings.IsValid(settings); + MongoDbEncryptionOptions settings = options.Value; + MongoDbEncryptionOptions.IsValid(settings); // start-kmsproviders var kmsProviders = new Dictionary>(); @@ -46,10 +46,12 @@ DataKeyOptions GetDataKeyOptions(List altNames) }); return dataKeyOptions; } + // end-datakeyopts // start-key-vault var keyVaultNamespace = CollectionNamespace.FromFullName("encryption.__keyVault"); + // end-key-vault // start-create-index @@ -58,20 +60,21 @@ DataKeyOptions GetDataKeyOptions(List altNames) { Unique = true, PartialFilterExpression = new BsonDocument - {{"keyAltNames", new BsonDocument {{"$exists", new BsonBoolean(true)}}}} + { { "keyAltNames", new BsonDocument { { "$exists", new BsonBoolean(true) } } } } }; var builder = Builders.IndexKeys; var indexKeysDocument = builder.Ascending("keyAltNames"); var indexModel = new CreateIndexModel(indexKeysDocument, indexOptions); var keyVaultDatabase = keyVaultClient.GetDatabase(keyVaultNamespace.DatabaseNamespace.DatabaseName); + // Drop the Key Vault Collection in case you created this collection // in a previous run of this application. keyVaultDatabase.DropCollection(keyVaultNamespace.CollectionName); var keyVaultCollection = keyVaultDatabase.GetCollection(keyVaultNamespace.CollectionName); keyVaultCollection.Indexes.CreateOne(indexModel); - // end-create-index + // end-create-index // start-create-dek var clientEncryptionOptions = new ClientEncryptionOptions( @@ -85,7 +88,6 @@ DataKeyOptions GetDataKeyOptions(List altNames) var dataKeyOptions3 = GetDataKeyOptions(new List { "dataKey3" }); var dataKeyOptions4 = GetDataKeyOptions(new List { "dataKey4" }); - BsonBinaryData CreateKeyGetID(DataKeyOptions options) { var dateKeyGuid = clientEncryption.CreateDataKey(provider, options, CancellationToken.None); @@ -96,12 +98,12 @@ BsonBinaryData CreateKeyGetID(DataKeyOptions options) var dataKeyId2 = CreateKeyGetID(dataKeyOptions2); var dataKeyId3 = CreateKeyGetID(dataKeyOptions3); var dataKeyId4 = CreateKeyGetID(dataKeyOptions4); + // end-create-dek // start-create-enc-collection var encryptedCollectionNamespace = CollectionNamespace.FromFullName("medicalRecords.patients"); - var encryptedFieldsMap = new Dictionary { { @@ -112,9 +114,9 @@ BsonBinaryData CreateKeyGetID(DataKeyOptions options) { new BsonDocument { - {"keyId", dataKeyId1}, - {"path", new BsonString("patientId")}, - {"bsonType", new BsonString("int")}, + { "keyId", dataKeyId1}, + { "path", new BsonString("patientId")}, + { "bsonType", new BsonString("int")}, { "queries", new BsonDocument { @@ -124,27 +126,27 @@ BsonBinaryData CreateKeyGetID(DataKeyOptions options) }, new BsonDocument { - {"keyId", dataKeyId2}, - {"path", new BsonString("medications")}, - {"bsonType", new BsonString("array")}, + { "keyId", dataKeyId2}, + { "path", new BsonString("medications")}, + { "bsonType", new BsonString("array")}, }, new BsonDocument { - {"keyId", dataKeyId3}, - {"path", new BsonString("patientRecord.ssn")}, - {"bsonType", new BsonString("string")}, + { "keyId", dataKeyId3}, + { "path", new BsonString("patientRecord.ssn")}, + { "bsonType", new BsonString("string")}, { "queries", new BsonDocument { - {"queryType", new BsonString("equality")} + { "queryType", new BsonString("equality")} } } }, new BsonDocument { - {"keyId", dataKeyId4}, - {"path", new BsonString("patientRecord.billing")}, - {"bsonType", new BsonString("object")}, + { "keyId", dataKeyId4}, + { "path", new BsonString("patientRecord.billing")}, + { "bsonType", new BsonString("object")}, }, } } @@ -154,8 +156,8 @@ BsonBinaryData CreateKeyGetID(DataKeyOptions options) var extraOptions = new Dictionary() { - {"cryptSharedLibPath", settings.LibPath}, - }; + { "cryptSharedLibPath", settings.LibPath }, + }; var autoEncryptionOptions = new AutoEncryptionOptions( keyVaultNamespace, @@ -163,7 +165,6 @@ BsonBinaryData CreateKeyGetID(DataKeyOptions options) encryptedFieldsMap: encryptedFieldsMap, extraOptions: extraOptions); - return autoEncryptionOptions; // This is the last client diff --git a/src/Genocs.Persistence.MongoDb/Extensions/MongoDbExtensions.cs b/src/Genocs.Persistence.MongoDb/Extensions/MongoDbExtensions.cs index 38d8fd3f..aa7eea7a 100644 --- a/src/Genocs.Persistence.MongoDb/Extensions/MongoDbExtensions.cs +++ b/src/Genocs.Persistence.MongoDb/Extensions/MongoDbExtensions.cs @@ -1,9 +1,10 @@ -using Genocs.Common.Types; using Genocs.Core.Builders; +using Genocs.Core.Domain.Entities; using Genocs.Persistence.MongoDb.Builders; +using Genocs.Persistence.MongoDb.Configurations; +using Genocs.Persistence.MongoDb.Domain.Repositories; using Genocs.Persistence.MongoDb.Factories; using Genocs.Persistence.MongoDb.Initializers; -using Genocs.Persistence.MongoDb.Options; using Genocs.Persistence.MongoDb.Repositories; using Genocs.Persistence.MongoDb.Seeders; using Microsoft.Extensions.Configuration; @@ -15,78 +16,85 @@ namespace Genocs.Persistence.MongoDb.Extensions; /// -/// The MongoDb Extensions +/// The MongoDb Extensions. /// public static class MongoDbExtensions { // Helpful when dealing with integration testing private static bool _conventionsRegistered; - private const string RegistryName = "persistence.mongoDb"; /// - /// + /// It allows to add support for MongoDb. /// - /// The Genocs builder - /// - /// - /// - /// The Genocs builder - public static IGenocsBuilder AddMongo(this IGenocsBuilder builder, - string sectionName = MongoDbSettings.Position, + /// The Genocs builder. + /// The section name. + /// The seeder name. + /// Defines if setup the MongoDB Conventions. + /// The Genocs builder. + public static IGenocsBuilder AddMongo( + this IGenocsBuilder builder, + string sectionName = MongoDbOptions.Position, Type? seederType = null, bool registerConventions = true) { if (string.IsNullOrWhiteSpace(sectionName)) { - sectionName = MongoDbSettings.Position; + sectionName = MongoDbOptions.Position; } - var mongoOptions = builder.GetOptions(sectionName); + var mongoOptions = builder.GetOptions(sectionName); return builder.AddMongo(mongoOptions, seederType, registerConventions); } /// - /// Setup MongoDb support + /// It allows to add support for MongoDb. /// - /// The Genocs builder - /// - /// - /// - /// The Genocs builder - public static IGenocsBuilder AddMongo(this IGenocsBuilder builder, Func buildOptions, Type? seederType = null, bool registerConventions = true) + /// The Genocs builder. + /// The Genocs builder. + /// The seeder name. + /// Defines if setup the MongoDB Conventions. + /// The Genocs builder. + public static IGenocsBuilder AddMongo( + this IGenocsBuilder builder, + Func buildOptions, + Type? seederType = null, + bool registerConventions = true) { var mongoOptions = buildOptions(new MongoDbOptionsBuilder()).Build(); return builder.AddMongo(mongoOptions, seederType, registerConventions); } /// - /// Setup MongoDb support + /// Setup MongoDb support. /// - /// The Genocs builder - /// The settings + /// The Genocs builder. + /// The settings. /// /// - /// The Genocs builder - public static IGenocsBuilder AddMongo(this IGenocsBuilder builder, MongoDbSettings mongoOptions, - Type? seederType = null, bool registerConventions = true) + /// The Genocs builder. + public static IGenocsBuilder AddMongo( + this IGenocsBuilder builder, + MongoDbOptions options, + Type? seederType = null, + bool registerConventions = true) { - if (!builder.TryRegister(RegistryName)) + if (!builder.TryRegister(MongoDbOptions.Position)) { return builder; } - if (mongoOptions.SetRandomDatabaseSuffix) + if (options.SetRandomDatabaseSuffix) { - var suffix = $"{Guid.NewGuid():N}"; + string suffix = $"{Guid.NewGuid():N}"; Console.WriteLine($"Setting a random MongoDB database suffix: '{suffix}'."); - mongoOptions.Database = $"{mongoOptions.Database}_{suffix}"; + options.Database = $"{options.Database}_{suffix}"; } - builder.Services.AddSingleton(mongoOptions); + builder.Services.AddSingleton(options); builder.Services.AddSingleton(sp => { - var options = sp.GetRequiredService(); + var options = sp.GetRequiredService(); MongoClientSettings clientSettings = MongoClientSettings.FromConnectionString(options.ConnectionString); @@ -100,7 +108,7 @@ public static IGenocsBuilder AddMongo(this IGenocsBuilder builder, MongoDbSettin builder.Services.AddTransient(sp => { - var options = sp.GetRequiredService(); + var options = sp.GetRequiredService(); var client = sp.GetRequiredService(); return client.GetDatabase(options.Database); }); @@ -129,42 +137,43 @@ public static IGenocsBuilder AddMongo(this IGenocsBuilder builder, MongoDbSettin } /// - /// Adds a MongoDb repository to the DI container. Using Genocs builder support + /// Adds a MongoDb repository to the DI container. Using Genocs builder support. /// - /// The name of the entity - /// The kind of identifier - /// The Genocs builder - /// The collection name where to store data - /// The Genocs builder - public static IGenocsBuilder AddMongoRepository(this IGenocsBuilder builder, - string collectionName) - where TEntity : IIdentifiable + /// The name of the entity. + /// The kind of identifier. + /// The Genocs builder. + /// The collection name where to store data. + /// The Genocs builder. + public static IGenocsBuilder AddMongoRepository( + this IGenocsBuilder builder, + string collectionName) + where TEntity : IEntity { - builder.Services.AddTransient>(sp => + builder.Services.AddTransient>(sp => { var database = sp.GetRequiredService(); - return new MongoRepository(database, collectionName); + return new MongoDbBaseRepository(database, collectionName); }); return builder; } - /// - /// Add MongoDb support + /// Add MongoDb support. /// - /// The Genocs builder - /// The Genocs builder - /// The Genocs builder - /// The Genocs builder - public static IGenocsBuilder AddMongoFast(this IGenocsBuilder builder, - string sectionName = MongoDbSettings.Position, - bool registerConventions = true) + /// The Genocs builder. + /// The Genocs builder. + /// The Genocs builder. + /// The Genocs builder. + public static IGenocsBuilder AddMongoFast( + this IGenocsBuilder builder, + string sectionName = MongoDbOptions.Position, + bool registerConventions = true) { if (string.IsNullOrWhiteSpace(sectionName)) { - sectionName = MongoDbSettings.Position; + sectionName = MongoDbOptions.Position; } var section = builder.Configuration.GetSection(sectionName); @@ -174,7 +183,7 @@ public static IGenocsBuilder AddMongoFast(this IGenocsBuilder builder, return builder; } - builder.Services.Configure(section); + builder.Services.Configure(section); builder.Services.AddSingleton(); builder.Services.AddScoped(typeof(IMongoDbRepository<>), typeof(MongoDbRepository<>)); @@ -188,14 +197,16 @@ public static IGenocsBuilder AddMongoFast(this IGenocsBuilder builder, } /// - /// Register all the default MongoDb repository + /// Register all the default MongoDb repository. /// - /// The Genocs builder - /// Assembly to scan - /// Kind of ServiceLifetime - /// The Genocs builder - public static IGenocsBuilder RegisterMongoRepositories(this IGenocsBuilder builder, Assembly assembly, - ServiceLifetime lifetime = ServiceLifetime.Transient) + /// The Genocs builder. + /// Assembly to scan. + /// Kind of ServiceLifetime. + /// The Genocs builder. + public static IGenocsBuilder RegisterMongoRepositories( + this IGenocsBuilder builder, + Assembly assembly, + ServiceLifetime lifetime = ServiceLifetime.Transient) { builder.Services .Scan(s => s.FromAssemblyDependencies(assembly) diff --git a/src/Genocs.Persistence.MongoDb/Extensions/ServiceCollectionExtensions.cs b/src/Genocs.Persistence.MongoDb/Extensions/ServiceCollectionExtensions.cs index ea2aab66..ed983fb1 100644 --- a/src/Genocs.Persistence.MongoDb/Extensions/ServiceCollectionExtensions.cs +++ b/src/Genocs.Persistence.MongoDb/Extensions/ServiceCollectionExtensions.cs @@ -6,7 +6,7 @@ namespace Genocs.Persistence.MongoDb.Extensions; /// -/// Service Collection Extension for MongoDb Repository setup +/// Service Collection Extension for MongoDb Repository setup. /// public static class ServiceCollectionExtensions { @@ -14,8 +14,14 @@ public static class ServiceCollectionExtensions internal static void RegisterConventions() { BsonSerializer.RegisterSerializer(typeof(decimal), new DecimalSerializer(BsonType.Decimal128)); - BsonSerializer.RegisterSerializer(typeof(decimal?), - new NullableSerializer(new DecimalSerializer(BsonType.Decimal128))); + + // Move to standard GuidRepresentation + BsonSerializer.RegisterSerializer(new GuidSerializer(GuidRepresentation.CSharpLegacy)); + + BsonSerializer.RegisterSerializer( + typeof(decimal?), + new NullableSerializer(new DecimalSerializer(BsonType.Decimal128))); + ConventionRegistry.Register("genocs", new ConventionPack { new CamelCaseElementNameConvention(), diff --git a/src/Genocs.Persistence.MongoDb/Genocs.Persistence.MongoDb.csproj b/src/Genocs.Persistence.MongoDb/Genocs.Persistence.MongoDb.csproj index dd46c1b5..7f9f2b16 100644 --- a/src/Genocs.Persistence.MongoDb/Genocs.Persistence.MongoDb.csproj +++ b/src/Genocs.Persistence.MongoDb/Genocs.Persistence.MongoDb.csproj @@ -1,58 +1,33 @@  - - net6.0;net7.0 - enable - enable - Genocs.Persistence.MongoDb - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The Persistence MongoDB Library. - The library containing base repository pattern to build .NET Core projects. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Moved to NET7.0 - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.Persistence.MongoDb + Genocs.Persistence.MongoDb + Genocs.Persistence.MongoDb + The Persistence MongoDB Library. + The Genocs Library .NET Core to be used with MongoDB as persistence datalayer.. + true + 5.0.0 + Nocco Giovanni Emanuele + mongodb aggregate architecture boilerplate repository-patterns domain-driven-design dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Moved to NET8.0 + True + latest + - - - True - \ - - - True - \ - - - True - \ - - + + + - - - + + + - - - - - - - - + + + + diff --git a/src/Genocs.Persistence.MongoDb/IMongoDatabaseProvider.cs b/src/Genocs.Persistence.MongoDb/IMongoDatabaseProvider.cs index d70620a1..5781822e 100644 --- a/src/Genocs.Persistence.MongoDb/IMongoDatabaseProvider.cs +++ b/src/Genocs.Persistence.MongoDb/IMongoDatabaseProvider.cs @@ -8,11 +8,10 @@ namespace Genocs.Persistence.MongoDb; public interface IMongoDatabaseProvider { /// - /// Gets the MongoClient + /// Gets the MongoClient. /// IMongoClient MongoClient { get; } - /// /// Gets the . /// diff --git a/src/Genocs.Persistence.MongoDb/Initializers/MongoDbInitializer.cs b/src/Genocs.Persistence.MongoDb/Initializers/MongoDbInitializer.cs index 11b4c6ad..dd60928e 100644 --- a/src/Genocs.Persistence.MongoDb/Initializers/MongoDbInitializer.cs +++ b/src/Genocs.Persistence.MongoDb/Initializers/MongoDbInitializer.cs @@ -1,4 +1,4 @@ -using Genocs.Persistence.MongoDb.Options; +using Genocs.Persistence.MongoDb.Configurations; using Genocs.Persistence.MongoDb.Repositories; using MongoDB.Driver; @@ -11,7 +11,7 @@ internal sealed class MongoDbInitializer : IMongoDbInitializer private readonly IMongoDatabase _database; private readonly IMongoDbSeeder _seeder; - public MongoDbInitializer(IMongoDatabase database, IMongoDbSeeder seeder, MongoDbSettings options) + public MongoDbInitializer(IMongoDatabase database, IMongoDbSeeder seeder, MongoDbOptions options) { _database = database; _seeder = seeder; diff --git a/src/Genocs.Persistence.MongoDb/MongoDatabaseProvider.cs b/src/Genocs.Persistence.MongoDb/MongoDatabaseProvider.cs index 412c8475..565c4854 100644 --- a/src/Genocs.Persistence.MongoDb/MongoDatabaseProvider.cs +++ b/src/Genocs.Persistence.MongoDb/MongoDatabaseProvider.cs @@ -1,42 +1,41 @@ -using Genocs.Persistence.MongoDb.Encryptions; -using Genocs.Persistence.MongoDb.Options; +using Genocs.Persistence.MongoDb.Configurations; +using Genocs.Persistence.MongoDb.Encryptions; using Microsoft.Extensions.Options; using MongoDB.Driver; using MongoDB.Driver.Core.Extensions.DiagnosticSources; namespace Genocs.Persistence.MongoDb; - /// -/// The MongoDatabaseProvider +/// The MongoDatabaseProvider. /// public class MongoDatabaseProvider : IMongoDatabaseProvider { /// - /// Reference to MongoClient + /// Reference to MongoClient. /// public IMongoClient MongoClient { get; private set; } /// - /// Reference to Database + /// Reference to Database. /// public IMongoDatabase Database { get; private set; } /// - /// Default Constructor + /// Default Constructor. /// /// /// - /// - public MongoDatabaseProvider(IOptions options, IOptions encrypOptions) + /// This exception happend in case mandatory data is missing. + public MongoDatabaseProvider(IOptions options, IOptions encrypOptions) { if (options == null) throw new NullReferenceException(nameof(options)); - MongoDbSettings dBSettings = options.Value; + MongoDbOptions dBSettings = options.Value; if (dBSettings == null) throw new NullReferenceException(nameof(dBSettings)); - if (!MongoDbSettings.IsValid(dBSettings)) throw new InvalidOperationException($"{nameof(dBSettings)} is invalid"); + if (!MongoDbOptions.IsValid(dBSettings)) throw new InvalidOperationException($"{nameof(dBSettings)} is invalid"); MongoClientSettings clientSettings = MongoClientSettings.FromConnectionString(dBSettings.ConnectionString); @@ -45,14 +44,17 @@ public MongoDatabaseProvider(IOptions options, IOptions cb.Subscribe(new DiagnosticsActivityEventSubscriber()); } - //if (encrypOptions != null) - //{ - // AzureInitializer initializer = new AzureInitializer(); - // var autoEncrypOptions = initializer.EncryptionOptions(encrypOptions); - // clientSettings.AutoEncryptionOptions = autoEncrypOptions; - //} + /* + if (encrypOptions != null) + { + AzureInitializer initializer = new AzureInitializer(); + var autoEncrypOptions = initializer.EncryptionOptions(encrypOptions); + clientSettings.AutoEncryptionOptions = autoEncrypOptions; + } + */ this.MongoClient = new MongoClient(clientSettings); this.Database = this.MongoClient.GetDatabase(dBSettings.Database); + } } diff --git a/src/Genocs.Persistence.MongoDb/Options/MongoDbEncryptionSettings.cs b/src/Genocs.Persistence.MongoDb/Options/MongoDbEncryptionSettings.cs deleted file mode 100644 index 35e03b2c..00000000 --- a/src/Genocs.Persistence.MongoDb/Options/MongoDbEncryptionSettings.cs +++ /dev/null @@ -1,70 +0,0 @@ -namespace Genocs.Persistence.MongoDb.Options -{ - /// - /// MongoDb encryption database Settings - /// - public class MongoDbEncryptionSettings - { - - /// - /// Default Section name - /// - public const string Position = "MongoDbEncryption"; - - /// - /// The Database connection string - /// - public string ConnectionString { get; set; } = default!; - - /// - /// The shared library used to encrypt - /// - public string LibPath { get; set; } = default!; - - /// - /// Azure Tenant Id - /// - public string TenantId { get; set; } = default!; - - /// - /// Azure Client Id - /// - public string ClientId { get; set; } = default!; - - /// - /// Azure Client Secret - /// - public string ClientSecret { get; set; } = default!; - - /// - /// Azure Client Secret - /// - public string KeyName { get; set; } = default!; - - /// - /// Azure Client Secret - /// - public string KeyVersion { get; set; } = default!; - - /// - /// Azure Client Secret - /// - public string KeyVaultEndpoint { get; set; } = default!; - - /// - /// Check if the MongoDbSettings object contains valid data - /// - /// MongoDbSettings object - /// return true if valid otherwise false - public static bool IsValid(MongoDbEncryptionSettings settings) - { - if (settings is null) return false; - - if (string.IsNullOrWhiteSpace(settings.ConnectionString)) return false; - if (string.IsNullOrWhiteSpace(settings.LibPath)) return false; - - return true; - - } - } -} diff --git a/src/Genocs.Persistence.MongoDb/Options/MongoDbSettings.cs b/src/Genocs.Persistence.MongoDb/Options/MongoDbSettings.cs deleted file mode 100644 index 20f34923..00000000 --- a/src/Genocs.Persistence.MongoDb/Options/MongoDbSettings.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.ComponentModel; - -namespace Genocs.Persistence.MongoDb.Options -{ - /// - /// MongoDb database Settings - /// - public class MongoDbSettings - { - /// - /// Default Section name - /// - public const string Position = "mongodb"; - - /// - /// The Database connection string - /// - public string ConnectionString { get; set; } = default!; - - /// - /// The database name where the data will be stored - /// - public string Database { get; set; } = default!; - - /// - /// Toggle database tracing - /// - public bool EnableTracing { get; set; } - - - /// - /// It defines if the database seed is applied - /// - public bool Seed { get; set; } - - - /// - /// It defines if random database name suffix is added - /// - [Description("Might be helpful for the integration testing.")] - public bool SetRandomDatabaseSuffix { get; set; } - - - /// - /// Check if the MongoDbSettings object contains valid data - /// - /// MongoDbSettings object - /// return true if valid otherwise false - public static bool IsValid(MongoDbSettings settings) - { - if (settings is null) return false; - - if (string.IsNullOrWhiteSpace(settings.ConnectionString)) return false; - if (string.IsNullOrWhiteSpace(settings.Database)) return false; - - return true; - - } - } -} diff --git a/src/Genocs.Persistence.MongoDb/README.md b/src/Genocs.Persistence.MongoDb/README_NUGET.md similarity index 82% rename from src/Genocs.Persistence.MongoDb/README.md rename to src/Genocs.Persistence.MongoDb/README_NUGET.md index 0e0463a5..a1a01d8b 100644 --- a/src/Genocs.Persistence.MongoDb/README.md +++ b/src/Genocs.Persistence.MongoDb/README_NUGET.md @@ -23,7 +23,7 @@ Please check the GitHub repository getting more info. Following is about how to setup **MongoDb** ``` json - "mongodb": { + "mongoDb": { "ConnectionString": "mongodb://localhost", "Database": "demo_database", "EnableTracing": false @@ -32,6 +32,28 @@ Following is about how to setup **MongoDb** ## Release notes +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + ### [2023-03-11] 3.4.0 - Updated to Genocs.Core 3.0.0 diff --git a/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbEntity.cs b/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbEntity.cs deleted file mode 100644 index cfdc0a94..00000000 --- a/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbEntity.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Genocs.Common.Types; - -namespace Genocs.Persistence.MongoDb.Repositories; - -/// -/// General purpose Entity used by default in MongoDB -/// -public interface IMongoDbEntity : IIdentifiable -{ - -} diff --git a/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbInitializer.cs b/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbInitializer.cs index 186da79a..a2858825 100644 --- a/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbInitializer.cs +++ b/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbInitializer.cs @@ -3,7 +3,7 @@ namespace Genocs.Persistence.MongoDb.Repositories; /// -/// The MongoDbInitializer interface placeholder +/// The MongoDbInitializer interface placeholder. /// public interface IMongoDbInitializer : IInitializer { diff --git a/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbRepository.cs b/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbRepository.cs deleted file mode 100644 index b5e603cb..00000000 --- a/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbRepository.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Genocs.Common.Types; - -namespace Genocs.Persistence.MongoDb.Repositories; - - -/// -/// The MongoDb repository interface -/// -/// -public interface IMongoDbRepository : IMongoRepository where TEntity : IIdentifiable -{ - -} diff --git a/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbSeeder.cs b/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbSeeder.cs index 3f5ca7ae..a4988961 100644 --- a/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbSeeder.cs +++ b/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbSeeder.cs @@ -3,14 +3,14 @@ namespace Genocs.Persistence.MongoDb.Repositories; /// -/// The MongoDb seeder +/// The MongoDb seeder. /// public interface IMongoDbSeeder { /// - /// Database Seed + /// Database Seed. /// - /// - /// + /// The database. + /// The async Task. Task SeedAsync(IMongoDatabase database); } \ No newline at end of file diff --git a/src/Genocs.Persistence.MongoDb/Repositories/IMongoSessionFactory.cs b/src/Genocs.Persistence.MongoDb/Repositories/IMongoSessionFactory.cs index aeb08733..2e9d7599 100644 --- a/src/Genocs.Persistence.MongoDb/Repositories/IMongoSessionFactory.cs +++ b/src/Genocs.Persistence.MongoDb/Repositories/IMongoSessionFactory.cs @@ -3,13 +3,13 @@ namespace Genocs.Persistence.MongoDb.Repositories; /// -/// The MongoDb Session Factory +/// The MongoDb Session Factory. /// public interface IMongoSessionFactory { /// - /// Create a new session + /// Create a new session. /// - /// + /// The async ClientSessionHandle. Task CreateAsync(); } \ No newline at end of file diff --git a/src/Genocs.Persistence.MongoDb/Repositories/MongoDbRepository.cs b/src/Genocs.Persistence.MongoDb/Repositories/MongoDbRepository.cs deleted file mode 100644 index 1f384738..00000000 --- a/src/Genocs.Persistence.MongoDb/Repositories/MongoDbRepository.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Genocs.Common.Types; - -namespace Genocs.Persistence.MongoDb.Repositories; - - -/// -/// Implements IRepository for MongoDB. -/// -/// Type of the Entity for this repository -public class MongoDbRepository : MongoDbRepositoryBase, IMongoDbRepository where TEntity : class, IIdentifiable -{ - /// - /// - /// - /// - public MongoDbRepository(IMongoDatabaseProvider databaseProvider) - : base(databaseProvider) - { - } -} \ No newline at end of file diff --git a/src/Genocs.Persistence.MongoDb/Repositories/MongoDbRepositoryBaseOfEntity.cs b/src/Genocs.Persistence.MongoDb/Repositories/MongoDbRepositoryBaseOfEntity.cs deleted file mode 100644 index 707e59b6..00000000 --- a/src/Genocs.Persistence.MongoDb/Repositories/MongoDbRepositoryBaseOfEntity.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Genocs.Core.CQRS.Queries; -using MongoDB.Driver; -using MongoDB.Driver.Linq; -using System.Linq.Expressions; - -namespace Genocs.Persistence.MongoDb.Repositories; - - -/// -/// Implements base class for IRepository for MongoDB. -/// -/// Type of the Entity for this repository -public class MongoDbRepositoryBase : MongoDbRepositoryBase - where TEntity : class, IMongoDbEntity -{ - /// - /// - /// - /// - public MongoDbRepositoryBase(IMongoDatabaseProvider databaseProvider) - : base(databaseProvider) - { - } - - /// - /// It returns the Mongo Collection as Queryable - /// - /// - public IMongoQueryable GetMongoQueryable() - { - return Collection.AsQueryable(); - } - - /// - /// - /// - /// - /// - /// - /// - public async Task> BrowseAsync(Expression> predicate, - TQuery query) where TQuery : IPagedQuery - => await Collection.AsQueryable().Where(predicate).PaginateAsync(query); -} \ No newline at end of file diff --git a/src/Genocs.Persistence.MongoDb/Repositories/MongoDbRepositoryBaseOfEntityAndKey.cs b/src/Genocs.Persistence.MongoDb/Repositories/MongoDbRepositoryBaseOfEntityAndKey.cs deleted file mode 100644 index 7d91044c..00000000 --- a/src/Genocs.Persistence.MongoDb/Repositories/MongoDbRepositoryBaseOfEntityAndKey.cs +++ /dev/null @@ -1,198 +0,0 @@ -using Genocs.Common.Types; -using Genocs.Core.CQRS.Queries; -using Genocs.Core.Domain.Entities; -using Genocs.Core.Domain.Repositories; -using MongoDB.Driver; -using MongoDB.Driver.Linq; -using System.Linq.Expressions; - -namespace Genocs.Persistence.MongoDb.Repositories; - - -/// -/// Implements IRepository for MongoDB. -/// -/// Type of the Entity for this repository -/// Primary key of the entity -public class MongoDbRepositoryBase : RepositoryBase, IMongoRepository - where TEntity : class, IIdentifiable -{ - /// - /// Todo - /// - public virtual IMongoDatabase Database - { - get { return _databaseProvider.Database; } - } - - /// - /// Todo - /// - public virtual IMongoCollection Collection - { - get - { - Attribute[] attrs = Attribute.GetCustomAttributes(typeof(TEntity)); // Reflection. - - // Displaying output. - foreach (Attribute attr in attrs) - { - if (attr is TableMappingAttribute) - { - return _databaseProvider.Database.GetCollection((attr as TableMappingAttribute).Name); - } - } - return _databaseProvider.Database.GetCollection(typeof(TEntity).Name); - } - } - - private readonly IMongoDatabaseProvider _databaseProvider; - - - /// - /// Standard constructor - /// - /// - public MongoDbRepositoryBase(IMongoDatabaseProvider databaseProvider) - { - _databaseProvider = databaseProvider; - } - - /// - /// Get all entities as IQueryable - /// - /// - public override IQueryable GetAll() - { - return Collection.AsQueryable(); - } - - - /// - /// Get single entity - /// - /// - /// - /// - public override TEntity Get(TPrimaryKey id) - { - FilterDefinition filter = Builders.Filter.Eq(m => m.Id, id); - var entity = Collection.Find(filter).FirstOrDefault(); - if (entity == null) - { - throw new EntityNotFoundException("There is no such an entity with given primary key. Entity type: " + typeof(TEntity).FullName + ", primary key: " + id); - } - - return entity; - } - - /// - /// First Or Default entity - /// - /// - /// - public override TEntity FirstOrDefault(TPrimaryKey id) - { - FilterDefinition filter = Builders.Filter.Eq(m => m.Id, id); - return Collection.Find(filter).FirstOrDefault(); - } - - /// - /// Insert an entity - /// - /// - /// - public override TEntity Insert(TEntity entity) - { - Collection.InsertOne(entity); - return entity; - } - - /// - /// Update an existing entity - /// - /// - /// - public override TEntity Update(TEntity entity) - { - Collection.ReplaceOneAsync( - filter: g => g.Id.Equals(entity.Id), - replacement: entity); - return entity; - } - - /// - /// Delete entity, passing the entire object - /// - /// - public override void Delete(TEntity entity) - { - Delete(entity.Id); - } - - - /// - /// Delete entity by primary key - /// - /// - public override void Delete(TPrimaryKey id) - { - FilterDefinition query = Builders.Filter.Eq(m => m.Id, id); - DeleteResult deleteResult = Collection.DeleteOneAsync(query).Result; - } - - public IMongoQueryable GetMongoQueryable() - { - throw new NotImplementedException(); - } - - public Task GetAsync(Expression> predicate) - { - throw new NotImplementedException(); - } - - public Task> FindAsync(Expression> predicate) - { - throw new NotImplementedException(); - } - - public Task> BrowseAsync(Expression> predicate, TQuery query) where TQuery : IPagedQuery - { - throw new NotImplementedException(); - } - - public Task AddAsync(TEntity entity) - { - throw new NotImplementedException(); - } - - public Task UpdateAsync(TEntity entity, Expression> predicate) - { - throw new NotImplementedException(); - } - - public Task ExistsAsync(Expression> predicate) - { - throw new NotImplementedException(); - } - - public TIdentifiable InsertAndGetId(TEntity entity) - { - throw new NotImplementedException(); - } - - public Task InsertAndGetIdAsync(TEntity entity) - { - throw new NotImplementedException(); - } - - public TIdentifiable InsertOrUpdateAndGetId(TEntity entity) - { - throw new NotImplementedException(); - } - - public Task InsertOrUpdateAndGetIdAsync(TEntity entity) - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/src/Genocs.Persistence.MongoDb/Repositories/Pagination.cs b/src/Genocs.Persistence.MongoDb/Repositories/Pagination.cs index fed767f6..2096828c 100644 --- a/src/Genocs.Persistence.MongoDb/Repositories/Pagination.cs +++ b/src/Genocs.Persistence.MongoDb/Repositories/Pagination.cs @@ -7,11 +7,21 @@ namespace Genocs.Persistence.MongoDb.Repositories; public static class Pagination { - public static async Task> PaginateAsync(this IMongoQueryable collection, IPagedQuery query) - => await collection.PaginateAsync(query.OrderBy, query.SortOrder, query.Page, query.Results); - - public static async Task> PaginateAsync(this IMongoQueryable collection, string? orderBy, - string? sortOrder, int page = 1, int resultsPerPage = 10) + public static async Task> PaginateAsync( + this IMongoQueryable collection, + IPagedQuery query) + => await collection.PaginateAsync( + query.OrderBy, + query.SortOrder, + query.Page, + query.Results); + + public static async Task> PaginateAsync( + this IMongoQueryable collection, + string? orderBy, + string? sortOrder, + int page = 1, + int resultsPerPage = 10) { if (page <= 0) { @@ -23,14 +33,14 @@ public static async Task> PaginateAsync(this IMongoQueryable.Empty; } - var totalResults = await collection.CountAsync(); - var totalPages = (int)Math.Ceiling((decimal)totalResults / resultsPerPage); + int totalResults = await collection.CountAsync(); + int totalPages = (int)Math.Ceiling((decimal)totalResults / resultsPerPage); List data; if (string.IsNullOrWhiteSpace(orderBy)) @@ -54,8 +64,10 @@ public static async Task> PaginateAsync(this IMongoQueryable Limit(this IMongoQueryable collection, IPagedQuery query) => collection.Limit(query.Page, query.Results); - public static IMongoQueryable Limit(this IMongoQueryable collection, - int page = 1, int resultsPerPage = 10) + public static IMongoQueryable Limit( + this IMongoQueryable collection, + int page = 1, + int resultsPerPage = 10) { if (page <= 0) { @@ -67,7 +79,7 @@ public static IMongoQueryable Limit(this IMongoQueryable collection, resultsPerPage = 10; } - var skip = (page - 1) * resultsPerPage; + int skip = (page - 1) * resultsPerPage; var data = collection .Skip(skip) diff --git a/src/Genocs.Persistence.MongoDb/_docs/README_NUGET.md b/src/Genocs.Persistence.MongoDb/_docs/README_NUGET.md index 51e3f8ef..a1a01d8b 100644 --- a/src/Genocs.Persistence.MongoDb/_docs/README_NUGET.md +++ b/src/Genocs.Persistence.MongoDb/_docs/README_NUGET.md @@ -23,7 +23,7 @@ Please check the GitHub repository getting more info. Following is about how to setup **MongoDb** ``` json - "MongoDb": { + "mongoDb": { "ConnectionString": "mongodb://localhost", "Database": "demo_database", "EnableTracing": false @@ -32,6 +32,28 @@ Following is about how to setup **MongoDb** ## Release notes +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + ### [2023-03-11] 3.4.0 - Updated to Genocs.Core 3.0.0 diff --git a/src/Genocs.Persistence.Redis/Builders/RedisOptionsBuilder.cs b/src/Genocs.Persistence.Redis/Builders/RedisSettingsBuilder.cs similarity index 65% rename from src/Genocs.Persistence.Redis/Builders/RedisOptionsBuilder.cs rename to src/Genocs.Persistence.Redis/Builders/RedisSettingsBuilder.cs index bd0b4846..38bbb955 100644 --- a/src/Genocs.Persistence.Redis/Builders/RedisOptionsBuilder.cs +++ b/src/Genocs.Persistence.Redis/Builders/RedisSettingsBuilder.cs @@ -1,10 +1,10 @@ -using Genocs.Persistence.Redis.Options; +using Genocs.Persistence.Redis.Configurations; namespace Genocs.Persistence.Redis.Builders; -internal sealed class RedisOptionsBuilder : IRedisOptionsBuilder +internal sealed class RedisSettingsBuilder : IRedisOptionsBuilder { - private readonly RedisSettings _options = new(); + private readonly RedisOptions _options = new(); public IRedisOptionsBuilder WithConnectionString(string connectionString) { @@ -18,6 +18,6 @@ public IRedisOptionsBuilder WithInstance(string instance) return this; } - public RedisSettings Build() + public RedisOptions Build() => _options; } \ No newline at end of file diff --git a/src/Genocs.Persistence.Redis/IRedisOptionsBuilder.cs b/src/Genocs.Persistence.Redis/Configurations/IRedisOptionsBuilder.cs similarity index 62% rename from src/Genocs.Persistence.Redis/IRedisOptionsBuilder.cs rename to src/Genocs.Persistence.Redis/Configurations/IRedisOptionsBuilder.cs index dd692c01..8d2000ea 100644 --- a/src/Genocs.Persistence.Redis/IRedisOptionsBuilder.cs +++ b/src/Genocs.Persistence.Redis/Configurations/IRedisOptionsBuilder.cs @@ -1,10 +1,8 @@ -using Genocs.Persistence.Redis.Options; - -namespace Genocs.Persistence.Redis; +namespace Genocs.Persistence.Redis.Configurations; public interface IRedisOptionsBuilder { IRedisOptionsBuilder WithConnectionString(string connectionString); IRedisOptionsBuilder WithInstance(string instance); - RedisSettings Build(); + RedisOptions Build(); } \ No newline at end of file diff --git a/src/Genocs.Persistence.Redis/Options/RedisSettings.cs b/src/Genocs.Persistence.Redis/Configurations/RedisOptions.cs similarity index 51% rename from src/Genocs.Persistence.Redis/Options/RedisSettings.cs rename to src/Genocs.Persistence.Redis/Configurations/RedisOptions.cs index 87d527ef..5f256db9 100644 --- a/src/Genocs.Persistence.Redis/Options/RedisSettings.cs +++ b/src/Genocs.Persistence.Redis/Configurations/RedisOptions.cs @@ -1,27 +1,32 @@ -namespace Genocs.Persistence.Redis.Options; +namespace Genocs.Persistence.Redis.Configurations; /// -/// The Redis Options +/// The Redis Options. /// -public class RedisSettings +public class RedisOptions { /// - /// The section name + /// Default section name. /// public const string Position = "redis"; /// - /// The connection string + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + /// + /// The connection string. /// public string ConnectionString { get; set; } = "localhost"; /// - /// Redis instance + /// Redis instance. /// public string? Instance { get; set; } /// - /// The database Id + /// The database Id. /// public int Database { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Persistence.Redis/Extensions.cs b/src/Genocs.Persistence.Redis/Extensions.cs index e75eb044..248df998 100644 --- a/src/Genocs.Persistence.Redis/Extensions.cs +++ b/src/Genocs.Persistence.Redis/Extensions.cs @@ -1,14 +1,13 @@ using Genocs.Core.Builders; using Genocs.Persistence.Redis.Builders; -using Genocs.Persistence.Redis.Options; +using Genocs.Persistence.Redis.Configurations; using Microsoft.Extensions.DependencyInjection; using StackExchange.Redis; namespace Genocs.Persistence.Redis; - /// -/// The redis extensions +/// The redis extensions. /// public static class Extensions { @@ -20,14 +19,14 @@ public static class Extensions /// The Genocs builder /// /// - public static IGenocsBuilder AddRedis(this IGenocsBuilder builder, string sectionName = RedisSettings.Position) + public static IGenocsBuilder AddRedis(this IGenocsBuilder builder, string sectionName = RedisOptions.Position) { if (string.IsNullOrWhiteSpace(sectionName)) { - sectionName = RedisSettings.Position; + sectionName = RedisOptions.Position; } - var options = builder.GetOptions(sectionName); + var options = builder.GetOptions(sectionName); return builder.AddRedis(options); } @@ -37,10 +36,11 @@ public static IGenocsBuilder AddRedis(this IGenocsBuilder builder, string sectio /// The Genocs builder /// /// - public static IGenocsBuilder AddRedis(this IGenocsBuilder builder, - Func buildOptions) + public static IGenocsBuilder AddRedis( + this IGenocsBuilder builder, + Func buildOptions) { - var options = buildOptions(new RedisOptionsBuilder()).Build(); + var options = buildOptions(new RedisSettingsBuilder()).Build(); return builder.AddRedis(options); } @@ -50,7 +50,7 @@ public static IGenocsBuilder AddRedis(this IGenocsBuilder builder, /// The Genocs builder /// /// - public static IGenocsBuilder AddRedis(this IGenocsBuilder builder, RedisSettings options) + public static IGenocsBuilder AddRedis(this IGenocsBuilder builder, RedisOptions options) { if (!builder.TryRegister(RegistryName)) { diff --git a/src/Genocs.Persistence.Redis/Genocs.Persistence.Redis.csproj b/src/Genocs.Persistence.Redis/Genocs.Persistence.Redis.csproj index a9615719..0b44127c 100644 --- a/src/Genocs.Persistence.Redis/Genocs.Persistence.Redis.csproj +++ b/src/Genocs.Persistence.Redis/Genocs.Persistence.Redis.csproj @@ -1,57 +1,32 @@  - - net6.0;net7.0 - enable - enable - Genocs.Persistence.Redis - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The Persistence Redis Library. - The library containing base repository pattern to build .NET Core projects. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.Persistence.Redis + Genocs.Persistence.Redis + Genocs.Persistence.Redis + The Persistence Redis Library. + The library containing base repository pattern to build .NET Core projects. + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + - - - True - \ - - - True - \ - - - True - \ - - + + + - - - + + + - - - - - - - + + + diff --git a/src/Genocs.MessageBrokers.RabbitMQ/README.md b/src/Genocs.Persistence.Redis/README_NUGET.md similarity index 57% rename from src/Genocs.MessageBrokers.RabbitMQ/README.md rename to src/Genocs.Persistence.Redis/README_NUGET.md index c96381ac..cf06bc9b 100644 --- a/src/Genocs.MessageBrokers.RabbitMQ/README.md +++ b/src/Genocs.Persistence.Redis/README_NUGET.md @@ -16,7 +16,35 @@ Please check the GitHub repository getting more info. ## Release notes -### [2023-03-12] 5.0.0-preview.5.0 +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 - Implemented MongoDB repository interfaces ### [2023-03-12] 5.0.0 diff --git a/src/Genocs.QueryBuilder.UnitTests/DynamicQuery/InMemoryBasicDynamicQueriesUnitTests.cs b/src/Genocs.QueryBuilder.UnitTests/DynamicQuery/InMemoryBasicDynamicQueriesUnitTests.cs index f907b62d..d43c5e49 100644 --- a/src/Genocs.QueryBuilder.UnitTests/DynamicQuery/InMemoryBasicDynamicQueriesUnitTests.cs +++ b/src/Genocs.QueryBuilder.UnitTests/DynamicQuery/InMemoryBasicDynamicQueriesUnitTests.cs @@ -11,48 +11,53 @@ public void InMemoryLinqTest() { var liCust = new List(); - Customer oCust = new Customer(001, 123000, "Devesh", "Ghaziabad", + Customer oCust = new Customer(001, "Devesh", "Ghaziabad", "250301", "9891586890", "devesh.akgec@gmail.com", "Genpact", "3123000"); liCust.Add(oCust); - oCust = new Customer(002, 123001, "NIKHIL", "NOIDA", "250201", + oCust = new Customer(002, "NIKHIL", "NOIDA", "250201", "xxx9892224", "devesh.akgec@gmail.com", "X-vainat", "4123001"); liCust.Add(oCust); - oCust = new Customer(003, 123002, "Shruti", "NOIDA", "25001", + oCust = new Customer(003, "Shruti", "NOIDA", "25001", "xxx0002345", "devesh.akgec@gmail.com", "Genpact", "5123002"); liCust.Add(oCust); - oCust = new Customer(004, 123003, "RAJ", "DELHI", "2500133", + oCust = new Customer(004, "RAJ", "DELHI", "2500133", "xxx9898907", "devesh.akgec@gmail.com", "HCL", "6123003"); liCust.Add(oCust); - oCust = new Customer(005, 123004, "Shubham", "Patna", "250013", + oCust = new Customer(005, "Shubham", "Patna", "250013", "x222333xx3", "devesh.akgec@gmail.com", "Genpact", "6123004"); liCust.Add(oCust); - - //order data + // Order data var liOrder = new List(); - Order oOrder = new Order(123000, "Noika Lumia", "7000", "2"); - liOrder.Add(oOrder); - oOrder = new Order(123001, "Moto G", "17000", "1"); - liOrder.Add(oOrder); - oOrder = new Order(123002, "Intext Mobile", "7000", "1"); - liOrder.Add(oOrder); - oOrder = new Order(123001, "Celkom GX898", "2500", "1"); - liOrder.Add(oOrder); - oOrder = new Order(123001, "Micromax", "1000", "10"); - liOrder.Add(oOrder); - oOrder = new Order(222, "NOIKA Asha", "2500", "1"); - liOrder.Add(oOrder); - oOrder = new Order(22212, "Iphone", "1000", "10"); - liOrder.Add(oOrder); + Order order = new Order(123000, 003); + order.Products.Add(new Product("Noika Lumia", "Noika Lumia", 150)); + order.Products.Add(new Product("Moto G", "Moto G", 2500)); + order.Products.Add(new Product("Intext Mobile", "Intext Mobile", 7000)); + liOrder.Add(order); + + //Order oOrder = new Order(123000, "Noika Lumia", "7000", "2"); + //liOrder.Add(oOrder); + //oOrder = new Order(123001, "Moto G", "17000", "1"); + //liOrder.Add(oOrder); + //oOrder = new Order(123002, "Intext Mobile", "7000", "1"); + //liOrder.Add(oOrder); + //oOrder = new Order(123001, "Celkom GX898", "2500", "1"); + //liOrder.Add(oOrder); + //oOrder = new Order(123001, "Micromax", "1000", "10"); + //liOrder.Add(oOrder); + //oOrder = new Order(222, "NOIKA Asha", "2500", "1"); + //liOrder.Add(oOrder); + //oOrder = new Order(22212, "Iphone", "1000", "10"); + //liOrder.Add(oOrder); // Query Sintax - var result = (from T1 in liCust - join T2 in liOrder - on T1.OrderId equals T2.OrderId + var result = (from c in liCust + join o in liOrder + on c.CustomerId equals o.CustomerId select new { - T1, - T2 + c, + o }).AsQueryable(); string selectStatement = "Where(x => x.OrderId = 123001)"; diff --git a/src/Genocs.QueryBuilder.UnitTests/DynamicQuery/InMemoryDynamicQueriesUnitTests.cs b/src/Genocs.QueryBuilder.UnitTests/DynamicQuery/InMemoryDynamicQueriesUnitTests.cs index c38e417f..d8e17690 100644 --- a/src/Genocs.QueryBuilder.UnitTests/DynamicQuery/InMemoryDynamicQueriesUnitTests.cs +++ b/src/Genocs.QueryBuilder.UnitTests/DynamicQuery/InMemoryDynamicQueriesUnitTests.cs @@ -20,19 +20,36 @@ public class InMemoryDynamicQueriesUnitTests { private async Task> GetUsers() { - List users = new List(); + List users = new List + { + new User { Id = 1, FirstName = "Giovanni", LastName = "Nocco", Age = 53, IsActive = true }, + new User { Id = 2, FirstName = "Giulio", LastName = "Nocco", Age = 19, IsActive = true }, + new User { Id = 3, FirstName = "Vittoria", LastName = "Nocco", Age = 17, IsActive = false }, + new User { Id = 4, FirstName = "Emanuele", LastName = "Nocco", Age = 54, IsActive = true, DateOfBirth = DateTime.Parse("1968-10-24"), Childs = 4 } + }; - users.Add(new User { Id = 1, FirstName = "Giovanni", LastName = "Nocco", Age = 53, IsActive = true }); - users.Add(new User { Id = 2, FirstName = "Giulio", LastName = "Nocco", Age = 19, IsActive = true }); - users.Add(new User { Id = 3, FirstName = "Vittoria", LastName = "Nocco", Age = 17, IsActive = false }); - users.Add(new User { Id = 4, FirstName = "Emanuele", LastName = "Nocco", Age = 54, IsActive = true, DateOfBirth = DateTime.Parse("1968-10-24"), Childrens = 4 }); + Address address = new Address { City = "Milano" }; + + users.Add(new User { Id = 5, FirstName = "Joshua", LastName = "Nocco", Age = 61, IsActive = true, DateOfBirth = DateTime.Parse("1965-10-24"), Childs = 2, Address = address }); + + return await Task.Run(() => users); + } + private async Task> GetOrders() + { + List orders = new List + { + new Order(1, 1), + new Order(2, 2), + new Order(3, 1), + new Order(4, 2) + }; Address address = new Address { City = "Milano" }; - users.Add(new User { Id = 5, FirstName = "Joshua", LastName = "Nocco", Age = 61, IsActive = true, DateOfBirth = DateTime.Parse("1965-10-24"), Childrens = 2, Address = address }); + // users.Add(new User { Id = 5, FirstName = "Joshua", LastName = "Nocco", Age = 61, IsActive = true, DateOfBirth = DateTime.Parse("1965-10-24"), Childs = 2, Address = address }); - return await Task.Run(() => users); + return await Task.Run(() => orders); } [Fact] @@ -41,7 +58,6 @@ public async Task MultipleUnwindTestAsync() var result = await GetUsers(); } - [Fact] public async Task ApplyAndOrOperatorToAStringTest() { @@ -221,7 +237,7 @@ public async Task ApplyGreaterThanOperatorToNullableIntGetItemTest() { var users = await GetUsers(); - QueryItem queryItem = new QueryItem(propertyName: "Childrens", propertyValue: "2", propertyType: "numeric", operatorType: QueryOperator.GreaterThan); + QueryItem queryItem = new QueryItem(propertyName: "Childs", propertyValue: "2", propertyType: "numeric", operatorType: QueryOperator.GreaterThan); IQueryable usersQuery = users.AsQueryable(); usersQuery = usersQuery.Where(DynamicQueryBuilder.BuildAdvancedSearchExpressionTree(queryItem, "User")); @@ -230,8 +246,6 @@ public async Task ApplyGreaterThanOperatorToNullableIntGetItemTest() Assert.Single(result); } - - [Fact] public async Task ApplyOperatorWithQueryItemListTest() { @@ -266,7 +280,6 @@ public async Task ApplyCombinedFirstNameAndAgeReturnEmpty() Assert.Empty(result); } - [Fact] public async Task ApplyOperatorOnNestedObjectThatCanBeNull() { diff --git a/src/Genocs.QueryBuilder.UnitTests/DynamicQuery/SqlServerDynamicQueriesUnitTests.cs b/src/Genocs.QueryBuilder.UnitTests/DynamicQuery/SqlServerDynamicQueriesUnitTests.cs index aee4364a..d10f703a 100644 --- a/src/Genocs.QueryBuilder.UnitTests/DynamicQuery/SqlServerDynamicQueriesUnitTests.cs +++ b/src/Genocs.QueryBuilder.UnitTests/DynamicQuery/SqlServerDynamicQueriesUnitTests.cs @@ -1,69 +1,72 @@ -namespace Genocs.QueryBuilder.UnitTests.DynamicQuery; - -public class SqlServerDynamicQueriesUnitTests -{ - //private async Task> GetUnwindItems() - //{ - // IMongoCollection promotionWallets = TestHelper.GetPromotionWalletCollection(); - - // var aggregates = await promotionWallets - // .Aggregate() - // //.Unwind(x => x.CreditCards, new AggregateUnwindOptions { IncludeArrayIndex = "CreditCardsCount", PreserveNullAndEmptyArrays = true }) - // //.Unwind(x => x.FidelityCards, new AggregateUnwindOptions { IncludeArrayIndex = "FidelityCardsCount", PreserveNullAndEmptyArrays = true }) - // //.Unwind(x => x.PartnerAccounts, new AggregateUnwindOptions { IncludeArrayIndex = "PartnerAccountsCount", PreserveNullAndEmptyArrays = true }) - // //.Unwind(x => x.Transactions, new AggregateUnwindOptions { IncludeArrayIndex = "TransactionsCount", PreserveNullAndEmptyArrays = true }) - // //.Unwind(x => x["Bs.Cs"]) - // //.Group(new BsonDocument { { "_id", "$Bs.Cs.Name" }, { "total", new BsonDocument("$sum", "$Bs.Cs.Amount") } }) - // //.Sort(new BsonDocument("total", -1)) - // .ToListAsync(); - - // List result = new List(); - - // foreach (var aggregate in aggregates) - // { - // var tmp = new UnwindPromotionWallet(aggregate.MemberId, - // aggregate.MobileNumber, - // aggregate.MobilePrefix, - // aggregate.Email, - // aggregate.MobileLanguage, - // aggregate.CountryOfResidence, - // aggregate.Currency, - // aggregate.SignIn, - // //aggregate.Membership, - // aggregate.CreatedAt, - // aggregate.UpdatedAt); - - // tmp.CreditCards = aggregate.CreditCards.FirstOrDefault(); - - // result.Add(tmp); - // } - - // return result; - //} - - //[Fact] - //public async Task MultipleUnwindTestAsync() - //{ - // await Task.CompletedTask; - // //var result = await GetUnwindItems(); - //} - - - //[Fact] - //public async Task ApplyAndOrOperatorToAStringTest() - //{ - // await Task.CompletedTask; - - // //var unwindedItems = await GetUnwindItems(); - - // //QueryItem queryItem = new QueryItem(propertyName: "MemberId", propertyValue: "UK9I1LONH or (HRNX5XOFA or MRNXFXOFA)"); +using MongoDB.Driver; +using Xunit; - // //IQueryable unwindedItemsQuery = unwindedItems.AsQueryable(); - // //unwindedItemsQuery = unwindedItemsQuery.Where(DynamicQueryBuilder.BuildAdvancedSearchExpressionTree(queryItem, "UnwindPromotionWallet")); +namespace Genocs.QueryBuilder.UnitTests.DynamicQuery; - // //var result = unwindedItemsQuery.ToList(); - // //Assert.NotEmpty(result); - //} +//public class SqlServerDynamicQueriesUnitTests +//{ +// private async Task> GetUnwindItems() +// { +// IMongoCollection promotionWallets = TestHelper.GetPromotionWalletCollection(); + +// var aggregates = await promotionWallets +// .Aggregate() +// .Unwind(x => x.CreditCards, new AggregateUnwindOptions { IncludeArrayIndex = "CreditCardsCount", PreserveNullAndEmptyArrays = true }) +// .Unwind(x => x.FidelityCards, new AggregateUnwindOptions { IncludeArrayIndex = "FidelityCardsCount", PreserveNullAndEmptyArrays = true }) +// .Unwind(x => x.PartnerAccounts, new AggregateUnwindOptions { IncludeArrayIndex = "PartnerAccountsCount", PreserveNullAndEmptyArrays = true }) +// .Unwind(x => x.Transactions, new AggregateUnwindOptions { IncludeArrayIndex = "TransactionsCount", PreserveNullAndEmptyArrays = true }) +// .Unwind(x => x["Bs.Cs"]) +// .Group(new BsonDocument { { "_id", "$Bs.Cs.Name" }, { "total", new BsonDocument("$sum", "$Bs.Cs.Amount") } }) +// .Sort(new BsonDocument("total", -1)) +// .ToListAsync(); + +// List result = new List(); + +// foreach (var aggregate in aggregates) +// { +// var tmp = new UnwindPromotionWallet(aggregate.MemberId, +// aggregate.MobileNumber, +// aggregate.MobilePrefix, +// aggregate.Email, +// aggregate.MobileLanguage, +// aggregate.CountryOfResidence, +// aggregate.Currency, +// aggregate.SignIn, +// //aggregate.Membership, +// aggregate.CreatedAt, +// aggregate.UpdatedAt); + +// tmp.CreditCards = aggregate.CreditCards.FirstOrDefault(); + +// result.Add(tmp); +// } + +// return result; +// } + +// [Fact] +// public async Task MultipleUnwindTestAsync() +// { +// await Task.CompletedTask; +// //var result = await GetUnwindItems(); +// } + + +// [Fact] +// public async Task ApplyAndOrOperatorToAStringTest() +// { +// await Task.CompletedTask; + +// var unwindedItems = await GetUnwindItems(); + +// QueryItem queryItem = new QueryItem(propertyName: "MemberId", propertyValue: "UK9I1LONH or (HRNX5XOFA or MRNXFXOFA)"); + +// IQueryable unwindedItemsQuery = unwindedItems.AsQueryable(); +// unwindedItemsQuery = unwindedItemsQuery.Where(DynamicQueryBuilder.BuildAdvancedSearchExpressionTree(queryItem, "UnwindPromotionWallet")); + +// //var result = unwindedItemsQuery.ToList(); +// //Assert.NotEmpty(result); +// } //[Fact] //public async Task ApplyEqualOperatorToNullableIntReturnItemsTest() @@ -153,4 +156,4 @@ public class SqlServerDynamicQueriesUnitTests // //var result = unwindedItemsQuery.ToList(); // //Assert.NotEmpty(result); //} -} \ No newline at end of file +//} \ No newline at end of file diff --git a/src/Genocs.QueryBuilder.UnitTests/DynamicQuery/TreeTraverseUnitTests.cs b/src/Genocs.QueryBuilder.UnitTests/DynamicQuery/TreeTraverseUnitTests.cs index b43c6ed5..d03016b0 100644 --- a/src/Genocs.QueryBuilder.UnitTests/DynamicQuery/TreeTraverseUnitTests.cs +++ b/src/Genocs.QueryBuilder.UnitTests/DynamicQuery/TreeTraverseUnitTests.cs @@ -53,10 +53,8 @@ private async Task BuildValidTreeLevel1() c01.ChildNodes = childrenLevel1; c02.ChildNodes = childrenLevel2; - List childrenLevel0 = new List { c01, c02 }; - TreeNode root = new TreeNode() { Name = "Root", Valid = true, ChildNodes = childrenLevel0 }; return await Task.Run(() => root); @@ -86,10 +84,8 @@ private async Task BuildInvalidTreeLevel1() c01.ChildNodes = childrenLevel1; c02.ChildNodes = childrenLevel2; - List childrenLevel0 = new List { c01, c02 }; - TreeNode root = new TreeNode() { Name = "Root", Valid = true, ChildNodes = childrenLevel0 }; return await Task.Run(() => root); @@ -156,7 +152,6 @@ private async Task BuildInvalidTreeLevel3() List childrenLevel4 = new List { c17, c18 }; List childrenLevel5 = new List { c19, c20 }; - c01.ChildNodes = childrenLevel1; c02.ChildNodes = childrenLevel2; @@ -167,7 +162,6 @@ private async Task BuildInvalidTreeLevel3() List childrenLevel0 = new List { c01, c02 }; - TreeNode root = new TreeNode() { Name = "Root", Valid = true, ChildNodes = childrenLevel0 }; return await Task.Run(() => root); diff --git a/src/Genocs.QueryBuilder.UnitTests/Genocs.QueryBuilder.UnitTests.csproj b/src/Genocs.QueryBuilder.UnitTests/Genocs.QueryBuilder.UnitTests.csproj index 0ced936b..e51e8049 100644 --- a/src/Genocs.QueryBuilder.UnitTests/Genocs.QueryBuilder.UnitTests.csproj +++ b/src/Genocs.QueryBuilder.UnitTests/Genocs.QueryBuilder.UnitTests.csproj @@ -1,34 +1,38 @@  - - net7.0 - enable - enable - false - false - + + net8.0 + false + false + - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + - - - diff --git a/src/Genocs.QueryBuilder.UnitTests/Models/Customer.cs b/src/Genocs.QueryBuilder.UnitTests/Models/Customer.cs index be80ca5b..e394db39 100644 --- a/src/Genocs.QueryBuilder.UnitTests/Models/Customer.cs +++ b/src/Genocs.QueryBuilder.UnitTests/Models/Customer.cs @@ -2,8 +2,7 @@ namespace Genocs.QueryBuilder.UnitTests.Models; public class Customer { - public int CustomerID { get; set; } - public int OrderId { get; set; } + public int CustomerId { get; set; } public string? CustomerName { get; set; } public string? CustomerAddress { get; set; } public string? CustomerPinCode { get; set; } @@ -13,7 +12,6 @@ public class Customer public string? LocationCode { get; set; } public Customer(int custid, - int orderid, string custname, string cusAddress, string custtPin, @@ -22,8 +20,7 @@ public Customer(int custid, string custOffice, string locCode) { - CustomerID = custid; - OrderId = orderid; + CustomerId = custid; CustomerName = custname; CustomerAddress = cusAddress; CustomerPinCode = custtPin; @@ -31,7 +28,5 @@ public Customer(int custid, CustomerEmail = custEmail; CustomerOffice = custOffice; LocationCode = locCode; - } - } diff --git a/src/Genocs.QueryBuilder.UnitTests/Models/Order.cs b/src/Genocs.QueryBuilder.UnitTests/Models/Order.cs index 4eb180ac..022b9fa5 100644 --- a/src/Genocs.QueryBuilder.UnitTests/Models/Order.cs +++ b/src/Genocs.QueryBuilder.UnitTests/Models/Order.cs @@ -3,15 +3,41 @@ namespace Genocs.QueryBuilder.UnitTests.Models; public class Order { public int OrderId { get; set; } - public string ProductName { get; set; } - public string ProductCost { get; set; } - public string ProductQunatity { get; set; } + public int CustomerId { get; } + public List Products { get; set; } - public Order(int orderid, string pName, string pCost, string Pquant) + public Order(int orderId, int customerId) { - OrderId = orderid; - ProductCost = pCost; - ProductQunatity = Pquant; - ProductName = pName; + + OrderId = orderId; + CustomerId = customerId; + Products = new List(); + } + + public Order(int orderId, int customerId, string pName, string pCost, int cost) + : this(orderId, customerId) + { + Products.Add(new Product(pName, pCost, cost)); + } + + public void AddProduct(string pName, string pCost, int cost) + { + if (Products == null) Products = new List(); + Products.Add(new Product(pName, pCost, cost)); } } + +public class Product +{ + public Product(string sKU, string? description, int cost) + { + SKU = sKU; + Description = description; + Cost = cost; + } + + public string SKU { get; set; } = default!; + public string? Description { get; set; } + public int Cost { get; set; } + +} diff --git a/src/Genocs.QueryBuilder.UnitTests/Models/TreeNode.cs b/src/Genocs.QueryBuilder.UnitTests/Models/TreeNode.cs index f6f2fb68..117fee41 100644 --- a/src/Genocs.QueryBuilder.UnitTests/Models/TreeNode.cs +++ b/src/Genocs.QueryBuilder.UnitTests/Models/TreeNode.cs @@ -1,13 +1,12 @@ namespace Genocs.QueryBuilder.UnitTests.Models; -class TreeNode +internal class TreeNode { public string? Name { get; set; } public List? ChildNodes { get; set; } - public int OwnderId { get; set; } + public int OrderId { get; set; } public bool Valid { get; set; } - public bool FindDescendant() { if (ChildNodes != null && ChildNodes.Any()) diff --git a/src/Genocs.QueryBuilder.UnitTests/Models/User.cs b/src/Genocs.QueryBuilder.UnitTests/Models/User.cs index 74af36e6..01968b43 100644 --- a/src/Genocs.QueryBuilder.UnitTests/Models/User.cs +++ b/src/Genocs.QueryBuilder.UnitTests/Models/User.cs @@ -9,9 +9,9 @@ public class User public int Age { get; set; } /// - /// This is used as nullable to check the builder + /// This is used as nullable to check the builder. /// - public int? Childrens { get; set; } + public int? Childs { get; set; } public DateTime DateOfBirth { get; set; } public bool IsActive { get; set; } public string? MobileNumber { get; set; } diff --git a/src/Genocs.QueryBuilder/Constants.cs b/src/Genocs.QueryBuilder/Constants.cs index 5671e3e0..c1570a0e 100644 --- a/src/Genocs.QueryBuilder/Constants.cs +++ b/src/Genocs.QueryBuilder/Constants.cs @@ -1,59 +1,58 @@ -namespace Genocs.QueryBuilder +namespace Genocs.QueryBuilder; + +/// +/// Query constants. +/// +public static class Constants +{ + /// + /// Default date format. + /// + public const string DateFormat = "yyyy-MM-dd"; + + /// + /// Default filtering date. + /// + public const string DateFiltering = "UpdatedAt"; + + /// + /// Default property name. + /// + public const string DefaultPropertyName = "IsDefault"; +} + +/// +/// Query operator types. +/// +public enum QueryOperator { /// - /// Query constants - /// - public static class Constants - { - /// - /// Default date format - /// - public const string DATE_FORMAT = "yyyy-MM-dd"; - - /// - /// Default filtering date - /// - public const string DATE_FILTERING = "UpdatedAt"; - - /// - /// Default property name - /// - public const string DEFAULT_PROPERY_NAME = "IsDefault"; - } - - /// - /// Query operator types - /// - public enum QueryOperator - { - /// - /// GreaterThan - /// - GreaterThan, - - /// - /// GreaterThanOrEqual - /// - GreaterThanOrEqual, - - /// - /// Equal - /// - Equal, - - /// - /// NotEqual - /// - NotEqual, - - /// - /// LessThan - /// - LessThan, - - /// - /// LessThanOrEqual - /// - LessThanOrEqual - } + /// GreaterThan. + /// + GreaterThan, + + /// + /// GreaterThanOrEqual. + /// + GreaterThanOrEqual, + + /// + /// Equal. + /// + Equal, + + /// + /// NotEqual. + /// + NotEqual, + + /// + /// LessThan. + /// + LessThan, + + /// + /// LessThanOrEqual. + /// + LessThanOrEqual } \ No newline at end of file diff --git a/src/Genocs.QueryBuilder/DynamicQueryBuilder.cs b/src/Genocs.QueryBuilder/DynamicQueryBuilder.cs index 32820599..f916356b 100644 --- a/src/Genocs.QueryBuilder/DynamicQueryBuilder.cs +++ b/src/Genocs.QueryBuilder/DynamicQueryBuilder.cs @@ -1,170 +1,164 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; -namespace Genocs.QueryBuilder +namespace Genocs.QueryBuilder; + +/// +/// DynamicQueryBuilder. +/// +public class DynamicQueryBuilder { + #region Private static members + private static readonly string AndOperator = " and "; + private static readonly string OrOperator = " or "; + private static readonly string GtOperator = " gt "; + private static readonly string LtOperator = " lt "; + private static readonly string GtEOperator = " gte "; + private static readonly string LtEOperator = " lte "; + + // private static readonly string NotOperator = " not "; + + private static readonly string[] StringSeparators = new string[] { AndOperator, OrOperator, LtOperator, GtOperator, GtEOperator, LtEOperator }; + #endregion + /// - /// DynamicQueryBuilder + /// Builds the advanced search expression tree. + /// This enables dynamic query on Following attributes + /// MemberId, FirstName, LastName, Employer, Email, Address.City. + /// It can be enabled on other fields as well, but i have just restricted as a sample ot these set of fields. /// - public class DynamicQueryBuilder + /// The type of the source. + /// The search term. + /// The search. + /// + public static Expression> BuildAdvancedSearchExpressionTree(List searchItems, string sourceName) { - #region Private static members - private static readonly string AndOperator = " and "; - private static readonly string OrOperator = " or "; - private static readonly string GtOperator = " gt "; - private static readonly string LtOperator = " lt "; - private static readonly string GtEOperator = " gte "; - private static readonly string LtEOperator = " lte "; - - private static readonly string NotOperator = " not "; - - private static readonly string[] stringSeparators = new string[] { AndOperator, OrOperator, LtOperator, GtOperator, GtEOperator, LtEOperator }; - #endregion - - - /// - /// Builds the advanced search expression tree. - /// This enables dynamic query on Following attributes - /// MemberId, FirstName, LastName, Employer, Email, Address.City. - /// It can be enabled on other fields as well, but i have just restricted as a sample ot these set of fields. - /// - /// The type of the source. - /// The search term. - /// The search. - /// - public static Expression> BuildAdvancedSearchExpressionTree(List searchItems, string sourceName) + ParameterExpression pe = Expression.Parameter(typeof(TSource), sourceName); + Expression? searchExpression = null; + + foreach (var searchItem in searchItems) { - ParameterExpression pe = Expression.Parameter(typeof(TSource), sourceName); - Expression searchExpression = null; + Expression expression = ExpressionBuilder(searchItem, pe); - foreach (var searchItem in searchItems) - { - Expression expression = ExpressionBuilder(searchItem, pe); + // If you want to apply OR + // searchExpression = searchExpression == null ? expression : Expression.OrElse(searchExpression, expression); - // If you want to apply OR - //searchExpression = searchExpression == null ? expression : Expression.OrElse(searchExpression, expression); + // If you want to apply AND + searchExpression = searchExpression == null ? expression : Expression.AndAlso(searchExpression, expression); + } - // If you want to apply AND - searchExpression = searchExpression == null ? expression : Expression.AndAlso(searchExpression, expression); - } + return Expression.Lambda>(searchExpression, pe); + } - return Expression.Lambda>(searchExpression, pe); - } + /// + /// Builds the advanced search expression tree. + /// This enables dynamic query on a single item. + /// + /// The type of the source. + /// The search term. + /// The search. + /// + public static Expression> BuildAdvancedSearchExpressionTree(QueryItem searchItem, string sourceName) + { + ParameterExpression pe = Expression.Parameter(typeof(TSource), sourceName); + Expression searchExpression = ExpressionBuilder(searchItem, pe); + return Expression.Lambda>(searchExpression, pe); + } + private static Expression ExpressionBuilder(QueryItem searchItem, ParameterExpression pe) + { + string[] results = searchItem.PropertyValue.Split(StringSeparators, StringSplitOptions.RemoveEmptyEntries); + var operatorIndexes = GetListOfSortedOperatorIndexes(searchItem.PropertyValue); - /// - /// Builds the advanced search expression tree. - /// This enables dynamic query on a single item - /// - /// The type of the source. - /// The search term. - /// The search. - /// - public static Expression> BuildAdvancedSearchExpressionTree(QueryItem searchItem, string sourceName) + Expression expression; + switch (searchItem.PropertyType.ToLower().Trim()) { - ParameterExpression pe = Expression.Parameter(typeof(TSource), sourceName); - Expression searchExpression = ExpressionBuilder(searchItem, pe); - return Expression.Lambda>(searchExpression, pe); + case "string": + expression = QueryBuilder.ExpressionBuilder.GetExpressionString(results, searchItem.PropertyValue, searchItem.PropertyName, operatorIndexes, pe, searchItem.ParentCanBeNull); + break; + case "int": + expression = QueryBuilder.ExpressionBuilder.GetExpressionInt(results, searchItem.PropertyName, searchItem.OperatorType, pe); + break; + case "numeric": + expression = QueryBuilder.ExpressionBuilder.GetExpressionNumeric(results, searchItem.PropertyName, searchItem.OperatorType, pe); + break; + case "date": + expression = QueryBuilder.ExpressionBuilder.GetExpressionDate(results, searchItem.PropertyName, searchItem.OperatorType, pe); + break; + case "bool": + expression = QueryBuilder.ExpressionBuilder.GetExpressionBool(results, searchItem.PropertyName, pe); + break; + default: + expression = QueryBuilder.ExpressionBuilder.GetExpressionString(results, searchItem.PropertyValue, searchItem.PropertyName, operatorIndexes, pe, searchItem.ParentCanBeNull); + break; } - private static Expression ExpressionBuilder(QueryItem searchItem, ParameterExpression pe) - { - string[] results = searchItem.PropertyValue.Split(stringSeparators, StringSplitOptions.RemoveEmptyEntries); - var operatorIndexes = GetListOfSortedOperatorIndexes(searchItem.PropertyValue); + return expression; - Expression expression; - switch (searchItem.PropertyType.ToLower().Trim()) - { - case "string": - expression = QueryBuilder.ExpressionBuilder.GetExpressionString(results, searchItem.PropertyValue, searchItem.PropertyName, operatorIndexes, pe, searchItem.ParentCanBeNull); - break; - case "int": - expression = QueryBuilder.ExpressionBuilder.GetExpressionInt(results, searchItem.PropertyName, searchItem.OperatorType, pe); - break; - case "numeric": - expression = QueryBuilder.ExpressionBuilder.GetExpressionNumeric(results, searchItem.PropertyName, searchItem.OperatorType, pe); - break; - case "date": - expression = QueryBuilder.ExpressionBuilder.GetExpressionDate(results, searchItem.PropertyName, searchItem.OperatorType, pe); - break; - case "bool": - expression = QueryBuilder.ExpressionBuilder.GetExpressionBool(results, searchItem.PropertyName, pe); - break; - default: - expression = QueryBuilder.ExpressionBuilder.GetExpressionString(results, searchItem.PropertyValue, searchItem.PropertyName, operatorIndexes, pe, searchItem.ParentCanBeNull); - break; - } - - return expression; + } - } + /// + /// Gets the list of sorted operator indexes. + /// + /// The input. + /// + private static List GetListOfSortedOperatorIndexes(string input) + { + input = input.ToLower(); + string[] result = input.Split(StringSeparators, StringSplitOptions.RemoveEmptyEntries); + List operatorIndexes = new List(); + var andOperatorIndexes = AllIndexesOf(input, StringSeparators[0]); + var orOperatorIndexes = AllIndexesOf(input, StringSeparators[1]); - /// - /// Gets the list of sorted operator indexes. - /// - /// The input. - /// - private static List GetListOfSortedOperatorIndexes(string input) + if (andOperatorIndexes.Count > 0) { - input = input.ToLower(); - string[] result = input.Split(stringSeparators, StringSplitOptions.RemoveEmptyEntries); + operatorIndexes.AddRange(andOperatorIndexes); + } - List operatorIndexes = new List(); - var andOperatorIndexes = AllIndexesOf(input, stringSeparators[0]); - var orOperatorIndexes = AllIndexesOf(input, stringSeparators[1]); + if (orOperatorIndexes.Count > 0) + { + operatorIndexes.AddRange(orOperatorIndexes); + } - if (andOperatorIndexes.Count > 0) - { - operatorIndexes.AddRange(andOperatorIndexes); - } + if (operatorIndexes.Count > 0) + { + var sorterOperatorIndexes = operatorIndexes.ToList().OrderBy(v => v.Index).ToList(); + } - if (orOperatorIndexes.Count > 0) - { - operatorIndexes.AddRange(orOperatorIndexes); - } + return operatorIndexes; + } - if (operatorIndexes.Count > 0) - { - var sorterOperatorIndexes = operatorIndexes.ToList().OrderBy(v => v.Index).ToList(); - } - return operatorIndexes; + /// + /// All the indexes of. + /// + /// The string. + /// The value. + /// + /// the string to find may not be empty value. + private static List AllIndexesOf(string str, string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentException("the string to find may not be empty", "value"); } - /// - /// All the indexes of. - /// - /// The string. - /// The value. - /// - /// the string to find may not be empty;value - private static List AllIndexesOf(string str, string value) + List indexes = new List(); + for (int index = 0; ; index += value.Length) { - if (string.IsNullOrWhiteSpace(value)) + index = str.IndexOf(value, index); + if (index == -1) + return indexes; + indexes.Add(new OperatorIndexes { - throw new ArgumentException("the string to find may not be empty", "value"); - } - - List indexes = new List(); - for (int index = 0; ; index += value.Length) - { - index = str.IndexOf(value, index); - if (index == -1) - return indexes; - indexes.Add(new OperatorIndexes - { - Index = index, - Operator = value - }); - } + Index = index, + Operator = value + }); } } +} - internal class OperatorIndexes - { - public int Index { get; set; } - public string Operator { get; set; } - } +internal class OperatorIndexes +{ + public int Index { get; set; } + public string Operator { get; set; } = default!; } \ No newline at end of file diff --git a/src/Genocs.QueryBuilder/Expression.Bool.cs b/src/Genocs.QueryBuilder/Expression.Bool.cs index 2ec9baf9..43145946 100644 --- a/src/Genocs.QueryBuilder/Expression.Bool.cs +++ b/src/Genocs.QueryBuilder/Expression.Bool.cs @@ -1,43 +1,41 @@ -using System; using System.Linq.Expressions; -namespace Genocs.QueryBuilder +namespace Genocs.QueryBuilder; + +/// +/// The ExpressionBuilder for bool type. +/// +public static partial class ExpressionBuilder { + /// - /// The ExpressionBuilder for bool type + /// Gets the expression. /// - public static partial class ExpressionBuilder + /// The type of the source. + /// The search terms. + /// Name of the property. + /// The pe. + /// + internal static Expression GetExpressionBool(string[] searchTerms, string propertyName, ParameterExpression pe) { - - /// - /// Gets the expression. - /// - /// The type of the source. - /// The search terms. - /// Name of the property. - /// The pe. - /// - internal static Expression GetExpressionBool(string[] searchTerms, string propertyName, ParameterExpression pe) + // Compose the expression tree that represents the parameter to the predicate. + Expression propertyExp = pe; + foreach (string member in propertyName.Split('.')) { - // Compose the expression tree that represents the parameter to the predicate. - Expression propertyExp = pe; - foreach (var member in propertyName.Split('.')) - { - propertyExp = Expression.PropertyOrField(propertyExp, member); - } + propertyExp = Expression.PropertyOrField(propertyExp, member); + } - Expression searchExpression = null; + Expression? searchExpression = null; - MethodCallExpression left = Expression.Call(propertyExp, typeof(bool).GetMethod("ToString", Type.EmptyTypes)); - left = Expression.Call(left, typeof(string).GetMethod("ToLower", Type.EmptyTypes)); + MethodCallExpression left = Expression.Call(propertyExp, typeof(bool).GetMethod("ToString", Type.EmptyTypes)); + left = Expression.Call(left, typeof(string).GetMethod("ToLower", Type.EmptyTypes)); - var method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); + var method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); - var searchTerm = searchTerms[0].ToLower(); - Expression rightExpression = Expression.Constant(searchTerm); - searchExpression = Expression.Call(left, method, rightExpression); + string searchTerm = searchTerms[0].ToLower(); + Expression rightExpression = Expression.Constant(searchTerm); + searchExpression = Expression.Call(left, method, rightExpression); - return searchExpression; - } + return searchExpression; } } diff --git a/src/Genocs.QueryBuilder/Expression.Date.cs b/src/Genocs.QueryBuilder/Expression.Date.cs index 596a4754..c1ed1d24 100644 --- a/src/Genocs.QueryBuilder/Expression.Date.cs +++ b/src/Genocs.QueryBuilder/Expression.Date.cs @@ -18,20 +18,19 @@ internal static Expression GetExpressionDate(string[] searchTerms, stri { // Compose the expression tree that represents the parameter to the predicate. Expression propertyExp = pe; - foreach (var member in propertyName.Split('.')) + foreach (string? member in propertyName.Split('.')) { propertyExp = Expression.PropertyOrField(propertyExp, member); } // To do for date management - MethodCallExpression left = Expression.Call(propertyExp, typeof(DateTime).GetMethod("ToString", new Type[] { typeof(String) }), Expression.Constant(Constants.DATE_FORMAT)); + MethodCallExpression left = Expression.Call(propertyExp, typeof(DateTime).GetMethod("ToString", new Type[] { typeof(string) }), Expression.Constant(Constants.DateFormat)); var method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); ConstantExpression constantExpression = Expression.Constant(DateTime.Parse(searchTerms[0].ToLower())); var nullCheck = NormalizeNullable(propertyExp, constantExpression); - Expression searchExpression; switch (operatorType) { diff --git a/src/Genocs.QueryBuilder/Expression.Int.cs b/src/Genocs.QueryBuilder/Expression.Int.cs index bbcf560a..8a581dc3 100644 --- a/src/Genocs.QueryBuilder/Expression.Int.cs +++ b/src/Genocs.QueryBuilder/Expression.Int.cs @@ -1,57 +1,43 @@ using System.Linq.Expressions; -namespace Genocs.QueryBuilder +namespace Genocs.QueryBuilder; + +public static partial class ExpressionBuilder { - public static partial class ExpressionBuilder + /// + /// Gets the expression. + /// + /// The type of the source. + /// The search terms. + /// Name of the property. + /// The operatorType. + /// The parameter expression. + /// + internal static Expression GetExpressionInt( + string[] searchTerms, + string propertyName, + QueryOperator operatorType, + ParameterExpression pe) { - /// - /// Gets the expression. - /// - /// The type of the source. - /// The search terms. - /// Name of the property. - /// The operatorType. - /// The parameter expression. - /// - internal static Expression GetExpressionInt(string[] searchTerms, string propertyName, QueryOperator operatorType, - ParameterExpression pe) + // Compose the expression tree that represents the parameter to the predicate. + Expression propertyExp = pe; + foreach (string? member in propertyName.Split('.')) { - // Compose the expression tree that represents the parameter to the predicate. - Expression propertyExp = pe; - foreach (var member in propertyName.Split('.')) - { - propertyExp = Expression.PropertyOrField(propertyExp, member); - } - - ConstantExpression constantExpression = Expression.Constant(int.Parse(searchTerms[0].ToLower())); - - var nullCheck = NormalizeNullable(propertyExp, constantExpression); + propertyExp = Expression.PropertyOrField(propertyExp, member); + } - Expression searchExpression; - switch (operatorType) - { - case QueryOperator.GreaterThan: - searchExpression = Expression.GreaterThan(nullCheck.Item1, nullCheck.Item2); - break; - case QueryOperator.GreaterThanOrEqual: - searchExpression = Expression.GreaterThanOrEqual(nullCheck.Item1, nullCheck.Item2); - break; - case QueryOperator.LessThan: - searchExpression = Expression.LessThan(nullCheck.Item1, nullCheck.Item2); - break; - case QueryOperator.LessThanOrEqual: - searchExpression = Expression.LessThanOrEqual(nullCheck.Item1, nullCheck.Item2); - break; - case QueryOperator.NotEqual: - searchExpression = Expression.NotEqual(nullCheck.Item1, nullCheck.Item2); - break; - case QueryOperator.Equal: - default: - searchExpression = Expression.Equal(nullCheck.Item1, nullCheck.Item2); - break; - } + ConstantExpression constantExpression = Expression.Constant(int.Parse(searchTerms[0].ToLower())); - return searchExpression; - } + var nullCheck = NormalizeNullable(propertyExp, constantExpression); + Expression searchExpression = operatorType switch + { + QueryOperator.GreaterThan => Expression.GreaterThan(nullCheck.Item1, nullCheck.Item2), + QueryOperator.GreaterThanOrEqual => Expression.GreaterThanOrEqual(nullCheck.Item1, nullCheck.Item2), + QueryOperator.LessThan => Expression.LessThan(nullCheck.Item1, nullCheck.Item2), + QueryOperator.LessThanOrEqual => Expression.LessThanOrEqual(nullCheck.Item1, nullCheck.Item2), + QueryOperator.NotEqual => Expression.NotEqual(nullCheck.Item1, nullCheck.Item2), + _ => Expression.Equal(nullCheck.Item1, nullCheck.Item2), + }; + return searchExpression; } } \ No newline at end of file diff --git a/src/Genocs.QueryBuilder/Expression.Numeric.cs b/src/Genocs.QueryBuilder/Expression.Numeric.cs index 6dcc8931..75fbc9f3 100644 --- a/src/Genocs.QueryBuilder/Expression.Numeric.cs +++ b/src/Genocs.QueryBuilder/Expression.Numeric.cs @@ -1,60 +1,46 @@ using System.Linq.Expressions; -namespace Genocs.QueryBuilder +namespace Genocs.QueryBuilder; + +/// +/// ExpressionBuilder class for Numeric type. +/// +public static partial class ExpressionBuilder { /// - /// ExpressionBuilder class for Numeric type + /// Gets the expression. /// - public static partial class ExpressionBuilder + /// The type of the source. + /// The search terms. + /// Name of the property. + /// The operator type property. + /// The pe. + /// + internal static Expression GetExpressionNumeric( + string[] searchTerms, + string propertyName, + QueryOperator operatorType, + ParameterExpression pe) { - /// - /// Gets the expression. - /// - /// The type of the source. - /// The search terms. - /// Name of the property. - /// The operator type property. - /// The pe. - /// - internal static Expression GetExpressionNumeric(string[] searchTerms, string propertyName, QueryOperator operatorType, - ParameterExpression pe) + // Compose the expression tree that represents the parameter to the predicate. + Expression propertyExp = pe; + foreach (string? member in propertyName.Split('.')) { - // Compose the expression tree that represents the parameter to the predicate. - Expression propertyExp = pe; - foreach (var member in propertyName.Split('.')) - { - propertyExp = Expression.PropertyOrField(propertyExp, member); - } - - ConstantExpression constantExpression = Expression.Constant(decimal.Parse(searchTerms[0].ToLower())); - - var nullCheck = NormalizeNullable(propertyExp, constantExpression); + propertyExp = Expression.PropertyOrField(propertyExp, member); + } - Expression searchExpression; - switch (operatorType) - { - case QueryOperator.GreaterThan: - searchExpression = Expression.GreaterThan(nullCheck.Item1, nullCheck.Item2); - break; - case QueryOperator.GreaterThanOrEqual: - searchExpression = Expression.GreaterThanOrEqual(nullCheck.Item1, nullCheck.Item2); - break; - case QueryOperator.LessThan: - searchExpression = Expression.LessThan(nullCheck.Item1, nullCheck.Item2); - break; - case QueryOperator.LessThanOrEqual: - searchExpression = Expression.LessThanOrEqual(nullCheck.Item1, nullCheck.Item2); - break; - case QueryOperator.NotEqual: - searchExpression = Expression.NotEqual(nullCheck.Item1, nullCheck.Item2); - break; - case QueryOperator.Equal: - default: - searchExpression = Expression.Equal(nullCheck.Item1, nullCheck.Item2); - break; - } + ConstantExpression constantExpression = Expression.Constant(decimal.Parse(searchTerms[0].ToLower())); - return searchExpression; - } + var nullCheck = NormalizeNullable(propertyExp, constantExpression); + Expression searchExpression = operatorType switch + { + QueryOperator.GreaterThan => Expression.GreaterThan(nullCheck.Item1, nullCheck.Item2), + QueryOperator.GreaterThanOrEqual => Expression.GreaterThanOrEqual(nullCheck.Item1, nullCheck.Item2), + QueryOperator.LessThan => Expression.LessThan(nullCheck.Item1, nullCheck.Item2), + QueryOperator.LessThanOrEqual => Expression.LessThanOrEqual(nullCheck.Item1, nullCheck.Item2), + QueryOperator.NotEqual => Expression.NotEqual(nullCheck.Item1, nullCheck.Item2), + _ => Expression.Equal(nullCheck.Item1, nullCheck.Item2), + }; + return searchExpression; } } \ No newline at end of file diff --git a/src/Genocs.QueryBuilder/Expression.String.cs b/src/Genocs.QueryBuilder/Expression.String.cs index 221332bc..8e040f8f 100644 --- a/src/Genocs.QueryBuilder/Expression.String.cs +++ b/src/Genocs.QueryBuilder/Expression.String.cs @@ -1,104 +1,102 @@ -using System; -using System.Collections.Generic; using System.Linq.Expressions; -namespace Genocs.QueryBuilder +namespace Genocs.QueryBuilder; + +/// +/// ExpressionBuilder class. +/// +public static partial class ExpressionBuilder { /// - /// ExpressionBuilder class + /// Gets the expression. /// - public static partial class ExpressionBuilder + /// The type of the source. + /// The search terms. + /// The keyword. + /// Name of the property. + /// The operator indexes. + /// The pe. + /// if set to true [add parent object null check]. + /// + internal static Expression GetExpressionString( + string[] searchTerms, + string keyword, + string propertyName, + List operatorIndexes, + ParameterExpression pe, + bool addParentObjectNullCheck) { - /// - /// Gets the expression. - /// - /// The type of the source. - /// The search terms. - /// The keyword. - /// Name of the property. - /// The operator indexes. - /// The pe. - /// if set to true [add parent object null check]. - /// - internal static Expression GetExpressionString(string[] searchTerms, - string keyword, - string propertyName, - List operatorIndexes, - ParameterExpression pe, - bool addParentObjectNullCheck) + // Compose the expression tree that represents the parameter to the predicate. + Expression propertyExp = pe; + string[] members = propertyName.Split('.'); + foreach (string member in members) { - // Compose the expression tree that represents the parameter to the predicate. - Expression propertyExp = pe; - string[] members = propertyName.Split('.'); - foreach (var member in members) - { - propertyExp = Expression.PropertyOrField(propertyExp, member); - } + propertyExp = Expression.PropertyOrField(propertyExp, member); + } - Expression searchExpression = null; - Expression finalExpression = null; - Expression nullorEmptyCheck = null; + Expression? searchExpression = null; + Expression? finalExpression = null; + Expression? nullOrEmptyCheck = null; - MethodCallExpression left = Expression.Call(propertyExp, typeof(string).GetMethod("ToLower", Type.EmptyTypes)); + MethodCallExpression left = Expression.Call(propertyExp, typeof(string).GetMethod("ToLower", Type.EmptyTypes)); - var method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); + var method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); - for (int count = 0; count < searchTerms.Length; count++) + for (int count = 0; count < searchTerms.Length; count++) + { + string searchTerm = searchTerms[count].ToLower(); + searchTerm = searchTerm.Replace("*", string.Empty); + searchTerm = searchTerm.Replace("\"", string.Empty); + Expression rightExpression; + Expression methodCallExpression; + if (searchTerm.Contains(NotOperator.TrimStart())) { - var searchTerm = searchTerms[count].ToLower(); - searchTerm = searchTerm.Replace("*", string.Empty); - searchTerm = searchTerm.Replace("\"", string.Empty); - Expression rightExpression; - Expression methodCallExpresssion; - if (searchTerm.Contains(NotOperator.TrimStart())) - { - searchTerm = searchTerm.Replace(NotOperator.TrimStart(), string.Empty).Trim(); - rightExpression = Expression.Constant(searchTerm); - methodCallExpresssion = Expression.Call(left, method, rightExpression); - methodCallExpresssion = Expression.Not(methodCallExpresssion); - } - else - { - rightExpression = Expression.Constant(searchTerm); - methodCallExpresssion = Expression.Call(left, method, rightExpression); - } - - if (count == 0) - { - searchExpression = methodCallExpresssion; - } - else - { - var conditionOperator = operatorIndexes[count - 1].Operator.Trim(); - switch (conditionOperator) - { - case "and": - searchExpression = Expression.AndAlso(searchExpression, methodCallExpresssion); - break; - case "or": - searchExpression = Expression.OrElse(searchExpression, methodCallExpresssion); - break; - default: - break; - } - } + searchTerm = searchTerm.Replace(NotOperator.TrimStart(), string.Empty).Trim(); + rightExpression = Expression.Constant(searchTerm); + methodCallExpression = Expression.Call(left, method, rightExpression); + methodCallExpression = Expression.Not(methodCallExpression); + } + else + { + rightExpression = Expression.Constant(searchTerm); + methodCallExpression = Expression.Call(left, method, rightExpression); } - if (addParentObjectNullCheck) + if (count == 0) { - //Add Null check for nested Object before checking the value of the property. - var nullCheck = Expression.NotEqual(Expression.PropertyOrField(pe, members[0]), Expression.Constant(null, typeof(object))); - nullorEmptyCheck = Expression.Not(Expression.Call(typeof(string), typeof(string).GetMethod("IsNullOrEmpty").Name, null, propertyExp)); - finalExpression = Expression.AndAlso(nullCheck, nullorEmptyCheck); - finalExpression = Expression.AndAlso(finalExpression, searchExpression); + searchExpression = methodCallExpression; } else { - nullorEmptyCheck = Expression.Not(Expression.Call(typeof(string), typeof(string).GetMethod("IsNullOrEmpty").Name, null, propertyExp)); - finalExpression = Expression.AndAlso(nullorEmptyCheck, searchExpression); + string conditionOperator = operatorIndexes[count - 1].Operator.Trim(); + switch (conditionOperator) + { + case "and": + searchExpression = Expression.AndAlso(searchExpression, methodCallExpression); + break; + case "or": + searchExpression = Expression.OrElse(searchExpression, methodCallExpression); + break; + default: + break; + } } + } - return finalExpression; + if (addParentObjectNullCheck) + { + // Add Null check for nested Object before checking the value of the property. + var nullCheck = Expression.NotEqual(Expression.PropertyOrField(pe, members[0]), Expression.Constant(null, typeof(object))); + nullOrEmptyCheck = Expression.Not(Expression.Call(typeof(string), typeof(string).GetMethod("IsNullOrEmpty").Name, null, propertyExp)); + finalExpression = Expression.AndAlso(nullCheck, nullOrEmptyCheck); + finalExpression = Expression.AndAlso(finalExpression, searchExpression); } + else + { + nullOrEmptyCheck = Expression.Not(Expression.Call(typeof(string), typeof(string).GetMethod("IsNullOrEmpty").Name, null, propertyExp)); + finalExpression = Expression.AndAlso(nullOrEmptyCheck, searchExpression); + } + + return finalExpression; } } \ No newline at end of file diff --git a/src/Genocs.QueryBuilder/Genocs.QueryBuilder.csproj b/src/Genocs.QueryBuilder/Genocs.QueryBuilder.csproj index 19628780..615f851b 100644 --- a/src/Genocs.QueryBuilder/Genocs.QueryBuilder.csproj +++ b/src/Genocs.QueryBuilder/Genocs.QueryBuilder.csproj @@ -1,45 +1,20 @@  - - netstandard2.1 - enable - enable - Genocs.QueryBuilder - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The Persistence Agnostic Query Builder Library. - The persistence agnostic query builder library. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - - - - - True - \ - - - True - \ - - - True - \ - - + + netstandard2.1 + Genocs.QueryBuilder + Genocs.QueryBuilder + Genocs.QueryBuilder + The Persistence Agnostic Query Builder Library. + The persistence agnostic query builder library. + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + diff --git a/src/Genocs.QueryBuilder/QueryItem.cs b/src/Genocs.QueryBuilder/QueryItem.cs index e650685a..1ea52faf 100644 --- a/src/Genocs.QueryBuilder/QueryItem.cs +++ b/src/Genocs.QueryBuilder/QueryItem.cs @@ -1,11 +1,12 @@ -namespace Genocs.QueryBuilder +namespace Genocs.QueryBuilder; + +/// +/// The QueryItem class. +/// +public class QueryItem { - /// - /// - /// - public class QueryItem - { - public QueryItem(string propertyName, + public QueryItem( + string propertyName, string propertyValue, string propertyType = "string", QueryOperator operatorType = QueryOperator.Equal) @@ -16,7 +17,8 @@ public QueryItem(string propertyName, OperatorType = operatorType; } - public QueryItem(string propertyName, + public QueryItem( + string propertyName, string propertyValue, bool parentCanBeNull, string propertyType = "string", @@ -34,5 +36,4 @@ public QueryItem(string propertyName, public string PropertyValue { get; set; } public QueryOperator OperatorType { get; set; } public bool ParentCanBeNull { get; set; } -} } \ No newline at end of file diff --git a/src/Genocs.LoadBalancing.Fabio/README.md b/src/Genocs.QueryBuilder/README_NUGET.md similarity index 60% rename from src/Genocs.LoadBalancing.Fabio/README.md rename to src/Genocs.QueryBuilder/README_NUGET.md index 945bb03b..6ffbc96c 100644 --- a/src/Genocs.LoadBalancing.Fabio/README.md +++ b/src/Genocs.QueryBuilder/README_NUGET.md @@ -15,7 +15,35 @@ Please check the GitHub repository getting more info. ## Release notes -### [2023-03-12] 5.0.0-preview.5.0 +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 - Implemented MongoDB repository interfaces ### [2023-03-12] 5.0.0 diff --git a/src/Genocs.Secrets.AzureKeyVault/Configurations/AzureKeyVaultOptions.cs b/src/Genocs.Secrets.AzureKeyVault/Configurations/AzureKeyVaultOptions.cs new file mode 100644 index 00000000..6e1ca058 --- /dev/null +++ b/src/Genocs.Secrets.AzureKeyVault/Configurations/AzureKeyVaultOptions.cs @@ -0,0 +1,42 @@ +namespace Genocs.Secrets.AzureKeyVault.Configurations; + +/// +/// The Azure Kay Vault setting definition. +/// +public class AzureKeyVaultOptions +{ + /// + /// Default section name. + /// + public const string Position = "azureKeyVault"; + + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + /// + /// The name of the Azure Key Vault. + /// + public string? Name { get; set; } + + /// + /// The managed identity id. + /// + public string? ManagedIdentityId { get; set; } + + /// + /// The certificate thumbprint. To be used with Certificate authentication. + /// + public object? AzureADCertThumbprint { get; set; } + + /// + /// The Active Directory Application id. To be used with Certificate authentication. + /// + public string? AzureADApplicationId { get; set; } + + /// + /// The Azure EntraID tenant Id. To be used with Certificate authentication. + /// + public string? AzureADDirectoryId { get; set; } +} \ No newline at end of file diff --git a/src/Genocs.Secrets.AzureKeyVault/Extensions.cs b/src/Genocs.Secrets.AzureKeyVault/Extensions.cs new file mode 100644 index 00000000..c90efaf0 --- /dev/null +++ b/src/Genocs.Secrets.AzureKeyVault/Extensions.cs @@ -0,0 +1,95 @@ +using Azure.Identity; +using Genocs.Core.Builders; +using Genocs.Secrets.AzureKeyVault.Configurations; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; + +namespace Genocs.Secrets.AzureKeyVault; + +/// +/// The Extensions helper class. +/// +public static class Extensions +{ + /// + /// This method is used to add the Azure Key Vault to the Host builder. + /// You can use the Azure Key Vault to store and manage application secrets. + /// + /// The builder. + /// The section name. + /// The Host builder. + public static IHostBuilder UseAzureKeyVault( + this IHostBuilder builder, + string sectionName = AzureKeyVaultOptions.Position) + => builder.ConfigureAppConfiguration((ctx, cfg) => + { + // TODO Test + if (string.IsNullOrWhiteSpace(sectionName)) + { + sectionName = AzureKeyVaultOptions.Position; + } + + var settings = ctx.Configuration.GetOptions(sectionName); + if (!settings.Enabled) + { + return; + } + + cfg.AddAzureKeyVault( + new Uri($"https://{settings.Name}.vault.azure.net/"), + new DefaultAzureCredential(new DefaultAzureCredentialOptions + { + ManagedIdentityClientId = settings.ManagedIdentityId + })); + }); + + /// + /// UseVault. + /// + /// The builder. + /// The section name. + /// The Web Host builder. + public static IWebHostBuilder UseAzureKeyVault( + this IWebHostBuilder builder, + string sectionName = AzureKeyVaultOptions.Position) + => builder.ConfigureAppConfiguration((ctx, cfg) => + { + if (string.IsNullOrWhiteSpace(sectionName)) + { + sectionName = AzureKeyVaultOptions.Position; + } + + AzureKeyVaultOptions settings = ctx.Configuration.GetOptions(sectionName); + if (!settings.Enabled) + { + return; + } + + cfg.AddAzureKeyVault( + new Uri($"https://{settings.Name}.vault.azure.net/"), + new DefaultAzureCredential(new DefaultAzureCredentialOptions + { + ManagedIdentityClientId = settings.ManagedIdentityId + })); + }); + + public static WebApplicationBuilder UseAzureKeyVault(this WebApplicationBuilder builder) + { + AzureKeyVaultOptions settings = builder.Configuration.GetOptions(AzureKeyVaultOptions.Position); + if (!settings.Enabled) + { + return builder; + } + + builder.Configuration.AddAzureKeyVault( + new Uri($"https://{settings.Name}.vault.azure.net/"), + new DefaultAzureCredential(new DefaultAzureCredentialOptions + { + ManagedIdentityClientId = settings.ManagedIdentityId + })); + + return builder; + } +} \ No newline at end of file diff --git a/src/Genocs.Secrets.AzureKeyVault/ExtensionsCertificate.cs b/src/Genocs.Secrets.AzureKeyVault/ExtensionsCertificate.cs new file mode 100644 index 00000000..a7e14519 --- /dev/null +++ b/src/Genocs.Secrets.AzureKeyVault/ExtensionsCertificate.cs @@ -0,0 +1,145 @@ +using Azure.Identity; +using Genocs.Core.Builders; +using Genocs.Secrets.AzureKeyVault.Configurations; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using System.Security.Cryptography.X509Certificates; + +namespace Genocs.Secrets.AzureKeyVault; + +/// +/// The ExtensionsCertificate helper class. +/// +public static class ExtensionsCertificate +{ + /// + /// This method is used to add the Azure Key Vault to the Host builder. + /// You can use the Azure Key Vault to store and manage application secrets. + /// + /// The builder. + /// The section name. + /// The Host builder. + public static IHostBuilder UseAzureKeyVaultWithCertificate( + this IHostBuilder builder, + string sectionName = AzureKeyVaultOptions.Position) + => builder.ConfigureAppConfiguration((ctx, cfg) => + { + // TODO: Test + if (string.IsNullOrWhiteSpace(sectionName)) + { + sectionName = AzureKeyVaultOptions.Position; + } + + var settings = ctx.Configuration.GetOptions(sectionName); + if (!settings.Enabled) + { + return; + } + + // TODO: Test + // To use the Azure Key Vault with Certificate authentication, you need to have the certificate installed in the Current User store. + using (var x509Store = new X509Store(StoreLocation.CurrentUser)) + { + x509Store.Open(OpenFlags.ReadOnly); + + var x509Certificate = x509Store.Certificates + .Find( + X509FindType.FindByThumbprint, + settings.AzureADCertThumbprint!, + validOnly: false) + .OfType() + .Single(); + + cfg.AddAzureKeyVault( + new Uri($"https://{settings.Name}.vault.azure.net/"), + new ClientCertificateCredential( + settings.AzureADDirectoryId, + settings.AzureADApplicationId, + x509Certificate)); + + x509Store?.Close(); + } + }); + + /// + /// UseVault. + /// + /// The builder. + /// The section name. + /// The Web Host builder. + public static IWebHostBuilder UseAzureKeyVaultWithCertificates( + this IWebHostBuilder builder, + string sectionName = AzureKeyVaultOptions.Position) + => builder.ConfigureAppConfiguration((ctx, cfg) => + { + if (string.IsNullOrWhiteSpace(sectionName)) + { + sectionName = AzureKeyVaultOptions.Position; + } + + + AzureKeyVaultOptions settings = ctx.Configuration.GetOptions(sectionName); + if (!settings.Enabled) + { + return; + } + + using (var x509Store = new X509Store(StoreLocation.CurrentUser)) + { + x509Store.Open(OpenFlags.ReadOnly); + + var x509Certificate = x509Store.Certificates + .Find( + X509FindType.FindByThumbprint, + settings.AzureADCertThumbprint!, + validOnly: false) + .OfType() + .Single(); + + cfg.AddAzureKeyVault( + new Uri($"https://{settings.Name}.vault.azure.net/"), + new ClientCertificateCredential( + settings.AzureADDirectoryId, + settings.AzureADApplicationId, + x509Certificate)); + + x509Store?.Close(); + } + }); + + public static WebApplicationBuilder UseAzureKeyVaultWithCertificates(this WebApplicationBuilder builder) + { + + AzureKeyVaultOptions settings = builder.Configuration.GetOptions(AzureKeyVaultOptions.Position); + if (!settings.Enabled) + { + return builder; + } + + using (var x509Store = new X509Store(StoreLocation.CurrentUser)) + { + x509Store.Open(OpenFlags.ReadOnly); + + var x509Certificate = x509Store.Certificates + .Find( + X509FindType.FindByThumbprint, + settings.AzureADCertThumbprint!, + validOnly: false) + .OfType() + .Single(); + + builder.Configuration.AddAzureKeyVault( + new Uri($"https://{settings.Name}.vault.azure.net/"), + new ClientCertificateCredential( + settings.AzureADDirectoryId, + settings.AzureADApplicationId, + x509Certificate)); + + x509Store?.Close(); + } + + return builder; + } +} diff --git a/src/Genocs.Secrets.AzureKeyVault/Genocs.Secrets.AzureKeyVault.csproj b/src/Genocs.Secrets.AzureKeyVault/Genocs.Secrets.AzureKeyVault.csproj new file mode 100644 index 00000000..eda6a48d --- /dev/null +++ b/src/Genocs.Secrets.AzureKeyVault/Genocs.Secrets.AzureKeyVault.csproj @@ -0,0 +1,33 @@ + + + + net8.0;net7.0;net6.0 + Genocs.Secrets.AzureKeyVault + Genocs.Secrets.AzureKeyVault + Genocs.Secrets.AzureKeyVault + The Genocs Secrets AzureKeyVault library + The Genocs Secrets AzureKeyVault library + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + + + + + + + + + + + + + + + + diff --git a/src/Genocs.Secrets.AzureKeyVault/README_NUGET.md b/src/Genocs.Secrets.AzureKeyVault/README_NUGET.md new file mode 100644 index 00000000..e45cec0b --- /dev/null +++ b/src/Genocs.Secrets.AzureKeyVault/README_NUGET.md @@ -0,0 +1,18 @@ +# Genocs Library Azure Key Vault integration + +This package contains a support function to read secret variable from Azure Key Vault. + + +## Description + +This package contains a support function to read secret variable from Azure Key Vault. + + +## Support + +Please check the GitHub repository getting more info. + + +## Release notes + +### [2024-06-12] 5.0.6 diff --git a/src/Genocs.Secrets.Vault/Configurations/VaultSettings.cs b/src/Genocs.Secrets.Vault/Configurations/VaultSettings.cs new file mode 100644 index 00000000..87889279 --- /dev/null +++ b/src/Genocs.Secrets.Vault/Configurations/VaultSettings.cs @@ -0,0 +1,56 @@ +namespace Genocs.Secrets.Vault.Configurations; + +/// +/// The vault Setting definition. +/// +public class VaultOptions +{ + public bool Enabled { get; set; } + public string? Url { get; set; } + public string? Key { get; set; } + public string? AuthType { get; set; } + public string? Token { get; set; } + public string? Username { get; set; } + public string? Password { get; set; } + public bool RevokeLeaseOnShutdown { get; set; } + public int RenewalsInterval { get; set; } + public KeyValueOptions Kv { get; set; } + public PkiOptions Pki { get; set; } + public IDictionary Lease { get; set; } + + public class KeyValueOptions + { + public bool Enabled { get; set; } + public int EngineVersion { get; set; } = 2; + public string? MountPoint { get; set; } = "kv"; + public string? Path { get; set; } + public int? Version { get; set; } + } + + public class LeaseOptions + { + public bool Enabled { get; set; } + public string? Type { get; set; } + public string? RoleName { get; set; } + public string? MountPoint { get; set; } + public bool AutoRenewal { get; set; } + public IDictionary? Templates { get; set; } + } + + public class PkiOptions + { + public bool Enabled { get; set; } + public string? RoleName { get; set; } + public string? MountPoint { get; set; } + public string? CertificateFormat { get; set; } + public string? PrivateKeyFormat { get; set; } + public string? CommonName { get; set; } + public string? TTL { get; set; } + public string? SubjectAlternativeNames { get; set; } + public string? OtherSubjectAlternativeNames { get; set; } + public bool ExcludeCommonNameFromSubjectAlternativeNames { get; set; } + public string? IPSubjectAlternativeNames { get; set; } + public string? URISubjectAlternativeNames { get; set; } + public bool ImportPrivateKey { get; set; } + } +} \ No newline at end of file diff --git a/src/Genocs.Secrets.Vault/Extensions.cs b/src/Genocs.Secrets.Vault/Extensions.cs index c0ce47b4..6fa05898 100644 --- a/src/Genocs.Secrets.Vault/Extensions.cs +++ b/src/Genocs.Secrets.Vault/Extensions.cs @@ -1,6 +1,6 @@ using Genocs.Core.Builders; +using Genocs.Secrets.Vault.Configurations; using Genocs.Secrets.Vault.Internals; -using Genocs.Secrets.Vault.Options; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.Memory; @@ -16,7 +16,7 @@ namespace Genocs.Secrets.Vault; /// -/// The Extensions helper class +/// The Extensions helper class. /// public static class Extensions { @@ -25,19 +25,21 @@ public static class Extensions private static readonly ICertificatesService CertificatesService = new CertificatesService(); /// - /// UseVault + /// UseVault. /// - /// The builder + /// The builder. /// /// /// - public static IHostBuilder UseVault(this IHostBuilder builder, string? keyValuePath = null, - string sectionName = SectionName) + public static IHostBuilder UseVault( + this IHostBuilder builder, + string? keyValuePath = null, + string sectionName = SectionName) => builder.ConfigureServices(services => services.AddVault(sectionName)) .ConfigureAppConfiguration((ctx, cfg) => { // TODO Test - VaultSettings options = ctx.Configuration.GetOptions(sectionName); + VaultOptions options = ctx.Configuration.GetOptions(sectionName); if (!options.Enabled) { return; @@ -47,20 +49,21 @@ public static IHostBuilder UseVault(this IHostBuilder builder, string? keyValueP }); /// - /// UseVault + /// UseVault. /// /// /// /// /// - public static IWebHostBuilder UseVault(this IWebHostBuilder builder, string? keyValuePath = null, - string sectionName = SectionName) + public static IWebHostBuilder UseVault( + this IWebHostBuilder builder, + string? keyValuePath = null, + string sectionName = SectionName) => builder.ConfigureServices(services => services.AddVault(sectionName)) .ConfigureAppConfiguration((ctx, cfg) => { // TODO Test - var options = new VaultSettings(); - ctx.Configuration.GetSection(sectionName).Bind(options); + VaultOptions options = ctx.Configuration.GetOptions(sectionName); if (!options.Enabled) { return; @@ -81,8 +84,8 @@ private static IServiceCollection AddVault(this IServiceCollection services, str { configuration = serviceProvider.GetRequiredService(); } - var options = new VaultSettings(); - configuration.GetSection(sectionName).Bind(options); + + VaultOptions options = configuration.GetOptions(sectionName); if (!options.Enabled) { return services; @@ -109,13 +112,13 @@ private static IServiceCollection AddVault(this IServiceCollection services, str return services; } - private static void VerifyOptions(VaultSettings options) + private static void VerifyOptions(VaultOptions options) { if (options.Kv is null) { if (!string.IsNullOrWhiteSpace(options.Key)) { - options.Kv = new VaultSettings.KeyValueSettings + options.Kv = new VaultOptions.KeyValueOptions { Enabled = options.Enabled, Path = options.Key @@ -136,11 +139,13 @@ private static void VerifyOptions(VaultSettings options) } } - private static async Task AddVaultAsync(this IConfigurationBuilder builder, VaultSettings options, - string? keyValuePath) + private static async Task AddVaultAsync( + this IConfigurationBuilder builder, + VaultOptions options, + string? keyValuePath) { VerifyOptions(options); - var kvPath = string.IsNullOrWhiteSpace(keyValuePath) ? options.Kv?.Path : keyValuePath; + string? kvPath = string.IsNullOrWhiteSpace(keyValuePath) ? options.Kv?.Path : keyValuePath; var (client, _) = GetClientAndSettings(options); if (!string.IsNullOrWhiteSpace(kvPath) && options.Kv.Enabled) { @@ -148,7 +153,7 @@ private static async Task AddVaultAsync(this IConfigurationBuilder builder, Vaul var keyValueSecrets = new KeyValueSecrets(client, options); var secret = await keyValueSecrets.GetAsync(kvPath); var parser = new JsonParser(); - var json = JsonConvert.SerializeObject(secret); + string json = JsonConvert.SerializeObject(secret); var data = parser.Parse(json); var source = new MemoryConfigurationSource { InitialData = data }; builder.Add(source); @@ -184,8 +189,11 @@ private static async Task AddVaultAsync(this IConfigurationBuilder builder, Vaul } } - private static Task InitLeaseAsync(string key, IVaultClient client, VaultSettings.LeaseSettings options, - IDictionary configuration) + private static Task InitLeaseAsync( + string key, + IVaultClient client, + VaultOptions.LeaseOptions options, + IDictionary configuration) => options.Type.ToLowerInvariant() switch { "activedirectory" => SetActiveDirectorySecretsAsync(key, client, options, configuration), @@ -196,11 +204,14 @@ private static Task InitLeaseAsync(string key, IVaultClient client, VaultSetting _ => Task.CompletedTask }; - private static async Task SetActiveDirectorySecretsAsync(string key, IVaultClient client, - VaultSettings.LeaseSettings options, IDictionary configuration) + private static async Task SetActiveDirectorySecretsAsync( + string key, + IVaultClient client, + VaultOptions.LeaseOptions options, + IDictionary configuration) { const string name = SecretsEngineMountPoints.Defaults.ActiveDirectory; - var mountPoint = string.IsNullOrWhiteSpace(options.MountPoint) ? name : options.MountPoint; + string mountPoint = string.IsNullOrWhiteSpace(options.MountPoint) ? name : options.MountPoint; var credentials = await client.V1.Secrets.ActiveDirectory.GetCredentialsAsync(options.RoleName, mountPoint); SetSecrets(key, options, configuration, name, () => (credentials, new Dictionary @@ -211,14 +222,17 @@ private static async Task SetActiveDirectorySecretsAsync(string key, IVaultClien }, credentials.LeaseId, credentials.LeaseDurationSeconds, credentials.Renewable)); } - private static async Task SetAzureSecretsAsync(string key, IVaultClient client, - VaultSettings.LeaseSettings options, - IDictionary configuration) + private static async Task SetAzureSecretsAsync( + string key, + IVaultClient client, + VaultOptions.LeaseOptions options, + IDictionary configuration) { const string name = SecretsEngineMountPoints.Defaults.Azure; - var mountPoint = string.IsNullOrWhiteSpace(options.MountPoint) ? name : options.MountPoint; - var credentials = - await client.V1.Secrets.Azure.GetCredentialsAsync(options.RoleName, mountPoint); + string? mountPoint = string.IsNullOrWhiteSpace(options.MountPoint) ? name : options.MountPoint; + + var credentials = await client.V1.Secrets.Azure.GetCredentialsAsync(options.RoleName, mountPoint); + SetSecrets(key, options, configuration, name, () => (credentials, new Dictionary { ["clientId"] = credentials.Data.ClientId, @@ -226,28 +240,32 @@ private static async Task SetAzureSecretsAsync(string key, IVaultClient client, }, credentials.LeaseId, credentials.LeaseDurationSeconds, credentials.Renewable)); } - private static async Task SetConsulSecretsAsync(string key, IVaultClient client, - VaultSettings.LeaseSettings options, - IDictionary configuration) + private static async Task SetConsulSecretsAsync( + string key, + IVaultClient client, + VaultOptions.LeaseOptions options, + IDictionary configuration) { const string name = SecretsEngineMountPoints.Defaults.Consul; - var mountPoint = string.IsNullOrWhiteSpace(options.MountPoint) ? name : options.MountPoint; - var credentials = - await client.V1.Secrets.Consul.GetCredentialsAsync(options.RoleName, mountPoint); + string? mountPoint = string.IsNullOrWhiteSpace(options.MountPoint) ? name : options.MountPoint; + var credentials = await client.V1.Secrets.Consul.GetCredentialsAsync(options.RoleName, mountPoint); + SetSecrets(key, options, configuration, name, () => (credentials, new Dictionary { ["token"] = credentials.Data.Token }, credentials.LeaseId, credentials.LeaseDurationSeconds, credentials.Renewable)); } - private static async Task SetDatabaseSecretsAsync(string key, IVaultClient client, - VaultSettings.LeaseSettings options, - IDictionary configuration) + private static async Task SetDatabaseSecretsAsync( + string key, + IVaultClient client, + VaultOptions.LeaseOptions options, + IDictionary configuration) { const string name = SecretsEngineMountPoints.Defaults.Database; - var mountPoint = string.IsNullOrWhiteSpace(options.MountPoint) ? name : options.MountPoint; - var credentials = - await client.V1.Secrets.Database.GetCredentialsAsync(options.RoleName, mountPoint); + string? mountPoint = string.IsNullOrWhiteSpace(options.MountPoint) ? name : options.MountPoint; + var credentials = await client.V1.Secrets.Database.GetCredentialsAsync(options.RoleName, mountPoint); + SetSecrets(key, options, configuration, name, () => (credentials, new Dictionary { ["username"] = credentials.Data.Username, @@ -255,21 +273,23 @@ private static async Task SetDatabaseSecretsAsync(string key, IVaultClient clien }, credentials.LeaseId, credentials.LeaseDurationSeconds, credentials.Renewable)); } - private static async Task SetPkiSecretsAsync(IVaultClient client, VaultSettings options) + private static async Task SetPkiSecretsAsync(IVaultClient client, VaultOptions options) { var issuer = new CertificatesIssuer(client, options); var certificate = await issuer.IssueAsync(); CertificatesService.Set(options.Pki.RoleName, certificate); } - private static async Task SetRabbitMqSecretsAsync(string key, IVaultClient client, - VaultSettings.LeaseSettings options, - IDictionary configuration) + private static async Task SetRabbitMqSecretsAsync( + string key, + IVaultClient client, + VaultOptions.LeaseOptions options, + IDictionary configuration) { const string name = SecretsEngineMountPoints.Defaults.RabbitMQ; - var mountPoint = string.IsNullOrWhiteSpace(options.MountPoint) ? name : options.MountPoint; - var credentials = - await client.V1.Secrets.RabbitMQ.GetCredentialsAsync(options.RoleName, mountPoint); + string? mountPoint = string.IsNullOrWhiteSpace(options.MountPoint) ? name : options.MountPoint; + var credentials = await client.V1.Secrets.RabbitMQ.GetCredentialsAsync(options.RoleName, mountPoint); + SetSecrets(key, options, configuration, name, () => (credentials, new Dictionary { ["username"] = credentials.Data.Username, @@ -277,9 +297,12 @@ private static async Task SetRabbitMqSecretsAsync(string key, IVaultClient clien }, credentials.LeaseId, credentials.LeaseDurationSeconds, credentials.Renewable)); } - private static void SetSecrets(string key, VaultSettings.LeaseSettings options, - IDictionary configuration, string name, - Func<(object, Dictionary, string, int, bool)> lease) + private static void SetSecrets( + string key, + VaultOptions.LeaseOptions options, + IDictionary configuration, + string name, + Func<(object Credentials, Dictionary Values, string LeaseId, int Duration, bool Renewable)> lease) { var createdAt = DateTime.UtcNow; var (credentials, values, leaseId, duration, renewable) = lease(); @@ -288,7 +311,7 @@ private static void SetSecrets(string key, VaultSettings.LeaseSettings options, LeaseService.Set(key, leaseData); } - private static (IVaultClient client, VaultClientSettings settings) GetClientAndSettings(VaultSettings options) + private static (IVaultClient Client, VaultClientSettings Settings) GetClientAndSettings(VaultOptions options) { var settings = new VaultClientSettings(options.Url, GetAuthMethod(options)); var client = new VaultClient(settings); @@ -296,7 +319,7 @@ private static (IVaultClient client, VaultClientSettings settings) GetClientAndS return (client, settings); } - private static void SetTemplates(string key, VaultSettings.LeaseSettings lease, + private static void SetTemplates(string key, VaultOptions.LeaseOptions lease, IDictionary configuration, IDictionary values) { if (lease.Templates is null || !lease.Templates.Any()) @@ -318,7 +341,7 @@ private static void SetTemplates(string key, VaultSettings.LeaseSettings lease, } } - private static IAuthMethodInfo GetAuthMethod(VaultSettings options) + private static IAuthMethodInfo GetAuthMethod(VaultOptions options) => options.AuthType?.ToLowerInvariant() switch { "token" => new TokenAuthMethodInfo(options.Token), diff --git a/src/Genocs.Secrets.Vault/Genocs.Secrets.Vault.csproj b/src/Genocs.Secrets.Vault/Genocs.Secrets.Vault.csproj index af1b194e..61c373ee 100644 --- a/src/Genocs.Secrets.Vault/Genocs.Secrets.Vault.csproj +++ b/src/Genocs.Secrets.Vault/Genocs.Secrets.Vault.csproj @@ -1,58 +1,32 @@  - - net6.0;net7.0 - enable - enable - Genocs.Secrets.Vault - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The Genocs Secrets Vault library - The Genocs Secrets Vault library - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.Secrets.Vault + Genocs.Secrets.Vault + Genocs.Secrets.Vault + The Genocs Secrets Vault library + The Genocs Secrets Vault library + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + - - - True - \ - - - True - \ - - - True - \ - - + + + - - - - - - - - - - - - + + + + + + + diff --git a/src/Genocs.Secrets.Vault/ICertificatesService.cs b/src/Genocs.Secrets.Vault/ICertificatesService.cs index 29cf0077..52d4db47 100644 --- a/src/Genocs.Secrets.Vault/ICertificatesService.cs +++ b/src/Genocs.Secrets.Vault/ICertificatesService.cs @@ -3,11 +3,11 @@ namespace Genocs.Secrets.Vault; /// -/// The Certification Service implementation +/// The Certification Service interface definition. /// public interface ICertificatesService { IReadOnlyDictionary All { get; } - X509Certificate2 Get(string name); + X509Certificate2? Get(string name); void Set(string name, X509Certificate2 certificate); } \ No newline at end of file diff --git a/src/Genocs.Secrets.Vault/ILeaseService.cs b/src/Genocs.Secrets.Vault/ILeaseService.cs index 339b0057..c8013f01 100644 --- a/src/Genocs.Secrets.Vault/ILeaseService.cs +++ b/src/Genocs.Secrets.Vault/ILeaseService.cs @@ -3,6 +3,6 @@ namespace Genocs.Secrets.Vault; public interface ILeaseService { IReadOnlyDictionary All { get; } - LeaseData Get(string key); + LeaseData? Get(string key); void Set(string key, LeaseData data); } \ No newline at end of file diff --git a/src/Genocs.Secrets.Vault/Internals/CertificatesIssuer.cs b/src/Genocs.Secrets.Vault/Internals/CertificatesIssuer.cs index 5bcf4e09..c05a434a 100644 --- a/src/Genocs.Secrets.Vault/Internals/CertificatesIssuer.cs +++ b/src/Genocs.Secrets.Vault/Internals/CertificatesIssuer.cs @@ -1,7 +1,7 @@ +using Genocs.Secrets.Vault.Configurations; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; -using Genocs.Secrets.Vault.Options; using VaultSharp; using VaultSharp.V1.SecretsEngines; using VaultSharp.V1.SecretsEngines.PKI; @@ -11,12 +11,12 @@ namespace Genocs.Secrets.Vault.Internals; internal sealed class CertificatesIssuer : ICertificatesIssuer { private readonly IVaultClient _client; - private readonly VaultSettings.PkiSettings _options; + private readonly VaultOptions.PkiOptions _options; private readonly CertificateFormat _certificateFormat; private readonly PrivateKeyFormat _privateKeyFormat; private readonly string _mountPoint; - public CertificatesIssuer(IVaultClient client, VaultSettings options) + public CertificatesIssuer(IVaultClient client, VaultOptions options) { _client = client; _options = options.Pki; @@ -55,7 +55,7 @@ await _client.V1.Secrets.PKI.GetCredentialsAsync(_options.RoleName, return certificate; } - var privateKey = Convert.FromBase64String(credentials.Data.PrivateKeyContent + byte[] privateKey = Convert.FromBase64String(credentials.Data.PrivateKeyContent .Replace("-----BEGIN RSA PRIVATE KEY-----", string.Empty) .Replace("-----END RSA PRIVATE KEY-----", string.Empty)); diff --git a/src/Genocs.Secrets.Vault/Internals/CertificatesService.cs b/src/Genocs.Secrets.Vault/Internals/CertificatesService.cs index e25d5baf..11bde0b0 100644 --- a/src/Genocs.Secrets.Vault/Internals/CertificatesService.cs +++ b/src/Genocs.Secrets.Vault/Internals/CertificatesService.cs @@ -10,7 +10,7 @@ internal sealed class CertificatesService : ICertificatesService public IReadOnlyDictionary All => Certificates; - public X509Certificate2 Get(string name) => Certificates.TryGetValue(name, out var cert) ? cert : null; + public X509Certificate2? Get(string name) => Certificates.TryGetValue(name, out var cert) ? cert : null; public void Set(string name, X509Certificate2 certificate) { diff --git a/src/Genocs.Secrets.Vault/Internals/KeyValueSecrets.cs b/src/Genocs.Secrets.Vault/Internals/KeyValueSecrets.cs index a6d268f1..365cf667 100644 --- a/src/Genocs.Secrets.Vault/Internals/KeyValueSecrets.cs +++ b/src/Genocs.Secrets.Vault/Internals/KeyValueSecrets.cs @@ -1,5 +1,5 @@ +using Genocs.Secrets.Vault.Configurations; using System.Text.Json; -using Genocs.Secrets.Vault.Options; using VaultSharp; namespace Genocs.Secrets.Vault.Internals; @@ -7,9 +7,9 @@ namespace Genocs.Secrets.Vault.Internals; internal sealed class KeyValueSecrets : IKeyValueSecrets { private readonly IVaultClient _client; - private readonly VaultSettings _options; + private readonly VaultOptions _options; - public KeyValueSecrets(IVaultClient client, VaultSettings options) + public KeyValueSecrets(IVaultClient client, VaultOptions options) { _client = client; _options = options; diff --git a/src/Genocs.Secrets.Vault/Internals/LeaseService.cs b/src/Genocs.Secrets.Vault/Internals/LeaseService.cs index 9f4b1802..51cbeac8 100644 --- a/src/Genocs.Secrets.Vault/Internals/LeaseService.cs +++ b/src/Genocs.Secrets.Vault/Internals/LeaseService.cs @@ -9,7 +9,7 @@ internal sealed class LeaseService : ILeaseService public IReadOnlyDictionary All => Secrets; - public LeaseData Get(string key) => Secrets.TryGetValue(key, out var data) ? data : null; + public LeaseData? Get(string key) => Secrets.TryGetValue(key, out var data) ? data : null; public void Set(string key, LeaseData data) { diff --git a/src/Genocs.Secrets.Vault/Internals/VaultHostedService.cs b/src/Genocs.Secrets.Vault/Internals/VaultHostedService.cs index f7f708f4..3ccf06e6 100644 --- a/src/Genocs.Secrets.Vault/Internals/VaultHostedService.cs +++ b/src/Genocs.Secrets.Vault/Internals/VaultHostedService.cs @@ -1,4 +1,4 @@ -using Genocs.Secrets.Vault.Options; +using Genocs.Secrets.Vault.Configurations; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using VaultSharp; @@ -11,32 +11,37 @@ internal sealed class VaultHostedService : BackgroundService private readonly ILeaseService _leaseService; private readonly ICertificatesIssuer _certificatesIssuer; private readonly ICertificatesService _certificatesService; - private readonly VaultSettings _options; + private readonly VaultOptions _settings; private readonly ILogger _logger; private readonly int _interval; - public VaultHostedService(IVaultClient client, ILeaseService leaseService, ICertificatesIssuer certificatesIssuer, - ICertificatesService certificatesService, VaultSettings options, ILogger logger) + public VaultHostedService( + IVaultClient client, + ILeaseService leaseService, + ICertificatesIssuer certificatesIssuer, + ICertificatesService certificatesService, + VaultOptions settings, + ILogger logger) { _client = client; _leaseService = leaseService; _certificatesIssuer = certificatesIssuer; _certificatesService = certificatesService; - _options = options; + _settings = settings; _logger = logger; - _interval = _options.RenewalsInterval <= 0 ? 10 : _options.RenewalsInterval; + _interval = _settings.RenewalsInterval <= 0 ? 10 : _settings.RenewalsInterval; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - if (!_options.Enabled) + if (!_settings.Enabled) { return; } - if ((_options.Pki is null || !_options.Pki.Enabled) && - (_options.Lease is null || _options.Lease.All(l => !l.Value.Enabled) || - !_options.Lease.Any(l => l.Value.AutoRenewal))) + if ((_settings.Pki is null || !_settings.Pki.Enabled) && + (_settings.Lease is null || _settings.Lease.All(l => !l.Value.Enabled) || + !_settings.Lease.Any(l => l.Value.AutoRenewal))) { return; } @@ -48,7 +53,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) var now = DateTime.UtcNow; var nextIterationAt = now.AddSeconds(2 * _interval); - if (_options.Pki is not null && _options.Pki.Enabled) + if (_settings.Pki is not null && _settings.Pki.Enabled) { foreach (var (role, cert) in _certificatesService.All) { @@ -81,7 +86,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) await Task.Delay(interval.Subtract(DateTime.UtcNow - now), stoppingToken); } - if (!_options.RevokeLeaseOnShutdown) + if (!_settings.RevokeLeaseOnShutdown) { return; } diff --git a/src/Genocs.Secrets.Vault/Options/VaultSettings.cs b/src/Genocs.Secrets.Vault/Options/VaultSettings.cs deleted file mode 100644 index ccff724f..00000000 --- a/src/Genocs.Secrets.Vault/Options/VaultSettings.cs +++ /dev/null @@ -1,57 +0,0 @@ -namespace Genocs.Secrets.Vault.Options; - - -/// -/// The vault Setting definition -/// -public class VaultSettings -{ - public bool Enabled { get; set; } - public string Url { get; set; } - public string Key { get; set; } - public string AuthType { get; set; } - public string Token { get; set; } - public string Username { get; set; } - public string Password { get; set; } - public bool RevokeLeaseOnShutdown { get; set; } - public int RenewalsInterval { get; set; } - public KeyValueSettings Kv { get; set; } - public PkiSettings Pki { get; set; } - public IDictionary Lease { get; set; } - - public class KeyValueSettings - { - public bool Enabled { get; set; } - public int EngineVersion { get; set; } = 2; - public string MountPoint { get; set; } = "kv"; - public string Path { get; set; } - public int? Version { get; set; } - } - - public class LeaseSettings - { - public bool Enabled { get; set; } - public string Type { get; set; } - public string RoleName { get; set; } - public string MountPoint { get; set; } - public bool AutoRenewal { get; set; } - public IDictionary Templates { get; set; } - } - - public class PkiSettings - { - public bool Enabled { get; set; } - public string RoleName { get; set; } - public string MountPoint { get; set; } - public string CertificateFormat { get; set; } - public string PrivateKeyFormat { get; set; } - public string CommonName { get; set; } - public string TTL { get; set; } - public string SubjectAlternativeNames { get; set; } - public string OtherSubjectAlternativeNames { get; set; } - public bool ExcludeCommonNameFromSubjectAlternativeNames { get; set; } - public string IPSubjectAlternativeNames { get; set; } - public string URISubjectAlternativeNames { get; set; } - public bool ImportPrivateKey { get; set; } - } -} \ No newline at end of file diff --git a/src/Genocs.Secrets.Vault/README.md b/src/Genocs.Secrets.Vault/README.md deleted file mode 100644 index c96381ac..00000000 --- a/src/Genocs.Secrets.Vault/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# .NET Core Base library - -This package contains a set of base functionalities designed by Genocs. -The libraries are built using .NET standard 2.1. - - -## Description - -Core NuGet package contains general purpose functionalities to be used on DDD services. - - -## Support - -Please check the GitHub repository getting more info. - - -## Release notes - -### [2023-03-12] 5.0.0-preview.5.0 -- Implemented MongoDB repository interfaces - -### [2023-03-12] 5.0.0 -- New Architecture - -### [2023-03-12] 3.1.0 -- Added Builders - -### [2023-03-12] 3.0.0 -- Refactory to implement CQRS pattern - -### [2023-03-04] 2.4.1 -- Updated System.Text.Json diff --git a/src/Genocs.Metrics/README.md b/src/Genocs.Secrets.Vault/README_NUGET.md similarity index 57% rename from src/Genocs.Metrics/README.md rename to src/Genocs.Secrets.Vault/README_NUGET.md index c96381ac..cf06bc9b 100644 --- a/src/Genocs.Metrics/README.md +++ b/src/Genocs.Secrets.Vault/README_NUGET.md @@ -16,7 +16,35 @@ Please check the GitHub repository getting more info. ## Release notes -### [2023-03-12] 5.0.0-preview.5.0 +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 - Implemented MongoDB repository interfaces ### [2023-03-12] 5.0.0 diff --git a/src/Genocs.Secrets.Vault/VaultAuthTypeNotSupportedException.cs b/src/Genocs.Secrets.Vault/VaultAuthTypeNotSupportedException.cs index a2505571..f6c5e488 100644 --- a/src/Genocs.Secrets.Vault/VaultAuthTypeNotSupportedException.cs +++ b/src/Genocs.Secrets.Vault/VaultAuthTypeNotSupportedException.cs @@ -4,11 +4,13 @@ internal sealed class VaultAuthTypeNotSupportedException : Exception { public string AuthType { get; set; } - public VaultAuthTypeNotSupportedException(string authType) : this(string.Empty, authType) + public VaultAuthTypeNotSupportedException(string authType) + : this(string.Empty, authType) { } - public VaultAuthTypeNotSupportedException(string message, string authType) : base(message) + public VaultAuthTypeNotSupportedException(string message, string authType) + : base(message) { AuthType = authType; } diff --git a/src/Genocs.Secrets.Vault/VaultException.cs b/src/Genocs.Secrets.Vault/VaultException.cs index 1330034b..3f272f85 100644 --- a/src/Genocs.Secrets.Vault/VaultException.cs +++ b/src/Genocs.Secrets.Vault/VaultException.cs @@ -4,15 +4,18 @@ internal sealed class VaultException : Exception { public string Key { get; } - public VaultException(string key) : this(null, key) + public VaultException(string key) + : this(null, key) { } - public VaultException(Exception? innerException, string key) : this(string.Empty, innerException, key) + public VaultException(Exception? innerException, string key) + : this(string.Empty, innerException, key) { } - public VaultException(string message, Exception? innerException, string key) : base(message, innerException) + public VaultException(string message, Exception? innerException, string key) + : base(message, innerException) { Key = key; } diff --git a/src/Genocs.Security/Crc8.cs b/src/Genocs.Security/Crc8.cs new file mode 100644 index 00000000..6740783f --- /dev/null +++ b/src/Genocs.Security/Crc8.cs @@ -0,0 +1,71 @@ +using System.Text; + +namespace Genocs.Security; + +/// +/// CRC 8-bit checksum calculator. +/// +public static class Crc8 +{ + // x8 + x7 + x6 + x4 + x2 + 1 + private const byte Poly = 0xd5; + + private static readonly byte[] Table = new byte[256]; + + /// + /// Compute the checksum of the byte array. + /// + /// + /// + public static byte ComputeChecksum(params byte[] bytes) + { + byte crc = 0; + if (bytes != null && bytes.Length > 0) + { + foreach (byte b in bytes) + { + crc = Table[crc ^ b]; + } + } + + return crc; + } + + /// + /// Compute the checksum of the string. + /// + /// The string where to calculate the CRC. + /// The Checksum. + /// If the payload is a null, empty or whitespaces string. + public static byte ComputeChecksum(string payload) + { + if (string.IsNullOrWhiteSpace(payload)) + { + throw new ArgumentNullException(nameof(payload)); + } + + byte[] buffer = Encoding.UTF8.GetBytes(payload); + return ComputeChecksum(buffer); + } + + static Crc8() + { + for (int i = 0; i < 256; ++i) + { + int temp = i; + for (int j = 0; j < 8; ++j) + { + if ((temp & 0x80) != 0) + { + temp = (temp << 1) ^ Poly; + } + else + { + temp <<= 1; + } + } + + Table[i] = (byte)temp; + } + } +} diff --git a/src/Genocs.Security/Extensions.cs b/src/Genocs.Security/Extensions.cs index 3e31cd48..eec8bc37 100644 --- a/src/Genocs.Security/Extensions.cs +++ b/src/Genocs.Security/Extensions.cs @@ -6,6 +6,15 @@ namespace Genocs.Security; public static class Extensions { + /// + /// Extension method to add security services to the DI container. + /// The AddSecurity method adds the following services to the DI container: + /// The Encryptor service . + /// The Hasher service . + /// The Signer service . + /// + /// The Genocs builder. + /// The builder to be used for chain. public static IGenocsBuilder AddSecurity(this IGenocsBuilder builder) { builder.Services diff --git a/src/Genocs.Security/Genocs.Security.csproj b/src/Genocs.Security/Genocs.Security.csproj index e42b67e6..911dec65 100644 --- a/src/Genocs.Security/Genocs.Security.csproj +++ b/src/Genocs.Security/Genocs.Security.csproj @@ -1,54 +1,40 @@  - - net6.0;net7.0 - enable - enable - Genocs.Security - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The Genocs Security library - The Genocs Security library - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Moved tAligned to the ecosystem - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.Security + Genocs.Security + Genocs.Security + The Genocs Security library + The Genocs Security library + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Moved tAligned to the ecosystem + True + latest + - - - True - \ - - - True - \ - - - True - \ - - + + + + + + - - - + + + - - - + + + + + + + diff --git a/src/Genocs.Security/IHasher.cs b/src/Genocs.Security/IHasher.cs index d75e5a09..79dded6a 100644 --- a/src/Genocs.Security/IHasher.cs +++ b/src/Genocs.Security/IHasher.cs @@ -1,8 +1,37 @@ namespace Genocs.Security; -// SHA-256 +/// +/// This provides interfaces to the class. +/// public interface IHasher { + /// + /// Generates the hash value of the given data. + /// + /// The data used to create the hash. + /// The hash result as string. string Hash(string data); + + /// + /// Generates the hash value of the given data. + /// + /// The data used to create the hash. + /// The private key used to used to create the hash. + /// The hash result as string. + string Hash(string data, string key); + + /// + /// Generates the hash value of the given data. + /// + /// The data used to create the hash as byte array. + /// The hash result as byte array. byte[] Hash(byte[] data); + + /// + /// Generates the hash value of the given data. + /// + /// The data used to create the hash. + /// The private key used to used to create the hash. + /// The hash result as byte array. + byte[] Hash(byte[] data, byte[] key); } \ No newline at end of file diff --git a/src/Genocs.Security/README.md b/src/Genocs.Security/README.md deleted file mode 100644 index c96381ac..00000000 --- a/src/Genocs.Security/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# .NET Core Base library - -This package contains a set of base functionalities designed by Genocs. -The libraries are built using .NET standard 2.1. - - -## Description - -Core NuGet package contains general purpose functionalities to be used on DDD services. - - -## Support - -Please check the GitHub repository getting more info. - - -## Release notes - -### [2023-03-12] 5.0.0-preview.5.0 -- Implemented MongoDB repository interfaces - -### [2023-03-12] 5.0.0 -- New Architecture - -### [2023-03-12] 3.1.0 -- Added Builders - -### [2023-03-12] 3.0.0 -- Refactory to implement CQRS pattern - -### [2023-03-04] 2.4.1 -- Updated System.Text.Json diff --git a/src/Genocs.Security/README_NUGET.md b/src/Genocs.Security/README_NUGET.md new file mode 100644 index 00000000..7c093329 --- /dev/null +++ b/src/Genocs.Security/README_NUGET.md @@ -0,0 +1,36 @@ +# Genocs Security library + +This package contains the functionalities to be used to handle standard security concerns. +The libraries can be used on .NET6, .NET7, .NET8. + +## Description + +Following an example of how the key should be. + +**WARNING: DO NOT USE IT IN PROD!!!** + +``` xml + + svbEQ96xMdgUpnkDiSaULDbM/HVFLHLc46BdyqwEzIhK+Ml2dqWq/RZIh8kLWmYwpB5gqfOya8Wid3GKIpq7Ke8ciV53qW/1ImOZZPxOtwX1mNzvIEagq80QJoMLphtU1ytPWRXvOjBdGUeTzmdV2kpHNax41n4Uv0QpOPIhzME= + AQAB +

+ + + + + +
" +``` + + +## Support + +Please check the GitHub repository getting more info. + + +## Release notes + +### [2024-06-15] 5.0.6 +- Refactory Settings + + diff --git a/src/Genocs.Security/Services/Encryptor.cs b/src/Genocs.Security/Services/Encryptor.cs index 10f13b43..320feaa7 100644 --- a/src/Genocs.Security/Services/Encryptor.cs +++ b/src/Genocs.Security/Services/Encryptor.cs @@ -19,7 +19,7 @@ public string Encrypt(string data, string key) using var aes = Aes.Create(); aes.Key = Encoding.UTF8.GetBytes(key); - var iv = Convert.ToBase64String(aes.IV); + string iv = Convert.ToBase64String(aes.IV); var transform = aes.CreateEncryptor(aes.Key, aes.IV); using var memoryStream = new MemoryStream(); using var cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write); diff --git a/src/Genocs.Security/Services/Hasher.cs b/src/Genocs.Security/Services/Hasher.cs index 4cd31a14..4f4b6d34 100644 --- a/src/Genocs.Security/Services/Hasher.cs +++ b/src/Genocs.Security/Services/Hasher.cs @@ -7,9 +7,9 @@ internal sealed class Hasher : IHasher { public string Hash(string data) { - var hash = Hash(Encoding.UTF8.GetBytes(data)); + byte[] hash = Hash(Encoding.UTF8.GetBytes(data)); var builder = new StringBuilder(); - foreach (var @byte in hash) + foreach (byte @byte in hash) { builder.Append(@byte.ToString("x2")); } @@ -28,4 +28,38 @@ public byte[] Hash(byte[] data) return sha256Hash.ComputeHash(data); } + + public byte[] Hash(byte[] data, byte[] key) + { + if (data is null || !data.Any()) + { + throw new ArgumentException("Data to be hashed cannot be empty.", nameof(data)); + } + + if (key is null || !key.Any()) + { + throw new ArgumentException("Key to be hashed cannot be empty.", nameof(key)); + } + + HMACSHA256 sha256Hash = new HMACSHA256(key); + return sha256Hash.ComputeHash(data); + } + + public string Hash(string data, string key) + { + byte[] bytesData = Encoding.Default.GetBytes(data); + byte[] bytesKey = Encoding.Default.GetBytes(key); + + byte[] hash = Hash(bytesData, bytesKey); + + // convert the byte array to a hexadecimal string + var builder = new StringBuilder(); + foreach (byte @byte in hash) + { + builder.Append(@byte.ToString("x2")); + } + + return builder.ToString(); + + } } \ No newline at end of file diff --git a/src/Genocs.Security/Services/SecurityKeyBuilder.cs b/src/Genocs.Security/Services/SecurityKeyBuilder.cs new file mode 100644 index 00000000..70036a21 --- /dev/null +++ b/src/Genocs.Security/Services/SecurityKeyBuilder.cs @@ -0,0 +1,72 @@ +using Microsoft.IdentityModel.Tokens; +using System.Security.Cryptography; +using System.Xml; + +namespace Genocs.Security.Services; + +public static class SecurityKeyBuilder +{ + /// + /// Create a new instance of using the provided secret. + /// + /// The secret key as xml string. + /// The created RSA. + public static SecurityKey CreateRsaSecurityKey(string secret) + { + RSA rsa = RSA.Create(); + rsa = FromCustomXmlString(rsa, secret); + return new RsaSecurityKey(rsa); + } + + /// + /// Helper method to create a RSA instance from a custom XML string. + /// + /// The RSA object instance. + /// The secret key as xml string. + /// The created RSA. + /// In case the secret key is invalid xml. + private static RSA FromCustomXmlString(RSA rsa, string xmlKey) + { + RSAParameters parameters = default(RSAParameters); + XmlDocument xmlDocument = new XmlDocument(); + xmlDocument.LoadXml(xmlKey); + if (xmlDocument.DocumentElement != null && xmlDocument.DocumentElement!.Name.Equals("RSAKeyValue")) + { + foreach (XmlNode childNode in xmlDocument.DocumentElement!.ChildNodes) + { + switch (childNode.Name) + { + case "Modulus": + parameters.Modulus = string.IsNullOrEmpty(childNode.InnerText) ? null : Convert.FromBase64String(childNode.InnerText); + break; + case "Exponent": + parameters.Exponent = string.IsNullOrEmpty(childNode.InnerText) ? null : Convert.FromBase64String(childNode.InnerText); + break; + case "P": + parameters.P = string.IsNullOrEmpty(childNode.InnerText) ? null : Convert.FromBase64String(childNode.InnerText); + break; + case "Q": + parameters.Q = string.IsNullOrEmpty(childNode.InnerText) ? null : Convert.FromBase64String(childNode.InnerText); + break; + case "DP": + parameters.DP = string.IsNullOrEmpty(childNode.InnerText) ? null : Convert.FromBase64String(childNode.InnerText); + break; + case "DQ": + parameters.DQ = string.IsNullOrEmpty(childNode.InnerText) ? null : Convert.FromBase64String(childNode.InnerText); + break; + case "InverseQ": + parameters.InverseQ = string.IsNullOrEmpty(childNode.InnerText) ? null : Convert.FromBase64String(childNode.InnerText); + break; + case "D": + parameters.D = string.IsNullOrEmpty(childNode.InnerText) ? null : Convert.FromBase64String(childNode.InnerText); + break; + } + } + + rsa.ImportParameters(parameters); + return rsa; + } + + throw new Exception("Invalid XML RSA key."); + } +} \ No newline at end of file diff --git a/src/Genocs.Security/Services/Signer.cs b/src/Genocs.Security/Services/Signer.cs index f4de9da4..7288bb88 100644 --- a/src/Genocs.Security/Services/Signer.cs +++ b/src/Genocs.Security/Services/Signer.cs @@ -18,8 +18,8 @@ public string Sign(object data, X509Certificate2 certificate) throw new ArgumentNullException(nameof(certificate), "Certificate cannot be null."); } - var bytes = JsonSerializer.SerializeToUtf8Bytes(data); - var signature = Sign(bytes, certificate); + byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(data); + byte[] signature = Sign(bytes, certificate); return BitConverter.ToString(signature).Replace("-", string.Empty); } @@ -41,7 +41,7 @@ public bool Verify(object data, X509Certificate2 certificate, string signature, throw new ArgumentException("Signature cannot be empty.", nameof(signature)); } - var bytes = JsonSerializer.SerializeToUtf8Bytes(data); + byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(data); return Verify(bytes, certificate, ToByteArray(signature), throwException); } @@ -107,8 +107,8 @@ public bool Verify(byte[] data, X509Certificate2 certificate, byte[] signature, private static byte[] ToByteArray(string hex) { - var bytes = new byte[hex.Length / 2]; - for (var i = 0; i < hex.Length; i += 2) + byte[] bytes = new byte[hex.Length / 2]; + for (int i = 0; i < hex.Length; i += 2) { bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); } diff --git a/src/Genocs.ServiceBusAzure.UnitTests/Genocs.ServiceBusAzure.UnitTests.csproj b/src/Genocs.ServiceBusAzure.UnitTests/Genocs.ServiceBusAzure.UnitTests.csproj index da901de6..bc90559c 100644 --- a/src/Genocs.ServiceBusAzure.UnitTests/Genocs.ServiceBusAzure.UnitTests.csproj +++ b/src/Genocs.ServiceBusAzure.UnitTests/Genocs.ServiceBusAzure.UnitTests.csproj @@ -1,23 +1,22 @@ - + - - net7.0 - enable - enable - false - false - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - + + net8.0 + false + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + \ No newline at end of file diff --git a/src/Genocs.ServiceBusAzure.UnitTests/SimpleUnitTests.cs b/src/Genocs.ServiceBusAzure.UnitTests/SimpleUnitTests.cs index 95b53906..5c7d02bb 100644 --- a/src/Genocs.ServiceBusAzure.UnitTests/SimpleUnitTests.cs +++ b/src/Genocs.ServiceBusAzure.UnitTests/SimpleUnitTests.cs @@ -1,6 +1,6 @@ -namespace Genocs.ServiceBusAzure.UnitTests; +using Xunit; -using Xunit; +namespace Genocs.ServiceBusAzure.UnitTests; public class SimpleUnitTests { diff --git a/src/Genocs.ServiceBusAzure/Configurations/AzureServiceBusQueueOptions.cs b/src/Genocs.ServiceBusAzure/Configurations/AzureServiceBusQueueOptions.cs new file mode 100644 index 00000000..1ff80237 --- /dev/null +++ b/src/Genocs.ServiceBusAzure/Configurations/AzureServiceBusQueueOptions.cs @@ -0,0 +1,24 @@ +using Microsoft.Azure.ServiceBus; + +namespace Genocs.ServiceBusAzure.Configurations; + +public class AzureServiceBusQueueOptions +{ + /// + /// Default section name. + /// + public const string Position = "azureServiceBusQueue"; + + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + public string? ConnectionString { get; set; } + public string? QueueName { get; set; } + public int MaxConcurrentCalls { get; set; } = 20; + public int PrefetchCount { get; set; } = 100; + public ReceiveMode ReceiveMode { get; set; } = ReceiveMode.PeekLock; + public RetryPolicy RetryPolicy { get; set; } = RetryPolicy.Default; + public bool AutoComplete { get; set; } = true; +} diff --git a/src/Genocs.ServiceBusAzure/Configurations/AzureServiceBusTopicOptions.cs b/src/Genocs.ServiceBusAzure/Configurations/AzureServiceBusTopicOptions.cs new file mode 100644 index 00000000..f830f97f --- /dev/null +++ b/src/Genocs.ServiceBusAzure/Configurations/AzureServiceBusTopicOptions.cs @@ -0,0 +1,25 @@ +using Microsoft.Azure.ServiceBus; + +namespace Genocs.ServiceBusAzure.Configurations; + +public class AzureServiceBusTopicOptions +{ + /// + /// Default section name. + /// + public const string Position = "azureServiceBusTopic"; + + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + public string? ConnectionString { get; set; } + public string? TopicName { get; set; } + public string? SubscriptionName { get; set; } + public int MaxConcurrentCalls { get; set; } = 20; + public int PrefetchCount { get; set; } = 100; + public ReceiveMode ReceiveMode { get; set; } = ReceiveMode.PeekLock; + public RetryPolicy RetryPolicy { get; set; } = RetryPolicy.Default; + public bool AutoComplete { get; set; } = true; +} + diff --git a/src/Genocs.ServiceBusAzure/Genocs.ServiceBusAzure.csproj b/src/Genocs.ServiceBusAzure/Genocs.ServiceBusAzure.csproj index 28b70d72..c2e209e7 100644 --- a/src/Genocs.ServiceBusAzure/Genocs.ServiceBusAzure.csproj +++ b/src/Genocs.ServiceBusAzure/Genocs.ServiceBusAzure.csproj @@ -1,60 +1,34 @@  - - net6.0;net7.0 - enable - enable - Genocs.ServiceBusAzure - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The low level library to use Azure Service Bus. - The low level library to use Azure Service Bus. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.ServiceBusAzure + Genocs.ServiceBusAzure + Genocs.ServiceBusAzure + The low level library to use Azure Service Bus. + The low level library to use Azure Service Bus. + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + - - - True - \ - - - True - \ - - - True - \ - - + + + + + + - - - + + + + + - - - - - - - - - - diff --git a/src/Genocs.ServiceBusAzure/Options/AzureServiceBusQueueSettings.cs b/src/Genocs.ServiceBusAzure/Options/AzureServiceBusQueueSettings.cs deleted file mode 100644 index 815ad5ce..00000000 --- a/src/Genocs.ServiceBusAzure/Options/AzureServiceBusQueueSettings.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Microsoft.Azure.ServiceBus; - -namespace Genocs.ServiceBusAzure.Options -{ - public class AzureServiceBusQueueSettings - { - public const string Position = "AzureServiceBusQueue"; - public string ConnectionString { get; set; } - public string QueueName { get; set; } - public int MaxConcurrentCalls { get; set; } = 20; - public int PrefetchCount { get; set; } = 100; - public ReceiveMode ReceiveMode { get; set; } = ReceiveMode.PeekLock; - public RetryPolicy RetryPolicy { get; set; } = RetryPolicy.Default; - public bool AutoComplete { get; set; } = true; - } -} diff --git a/src/Genocs.ServiceBusAzure/Options/AzureServiceBusTopicSettings.cs b/src/Genocs.ServiceBusAzure/Options/AzureServiceBusTopicSettings.cs deleted file mode 100644 index f66d4283..00000000 --- a/src/Genocs.ServiceBusAzure/Options/AzureServiceBusTopicSettings.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.Azure.ServiceBus; - -namespace Genocs.ServiceBusAzure.Options -{ - public class AzureServiceBusTopicSettings - { - public const string Position = "AzureServiceBusTopic"; - public string ConnectionString { get; set; } - public string TopicName { get; set; } - public string SubscriptionName { get; set; } - public int MaxConcurrentCalls { get; set; } = 20; - public int PrefetchCount { get; set; } = 100; - public ReceiveMode ReceiveMode { get; set; } = ReceiveMode.PeekLock; - public RetryPolicy RetryPolicy { get; set; } = RetryPolicy.Default; - public bool AutoComplete { get; set; } = true; - } -} - diff --git a/src/Genocs.ServiceBusAzure/Queues/AzureServiceBusQueue.cs b/src/Genocs.ServiceBusAzure/Queues/AzureServiceBusQueue.cs index 6d17d772..b0522451 100644 --- a/src/Genocs.ServiceBusAzure/Queues/AzureServiceBusQueue.cs +++ b/src/Genocs.ServiceBusAzure/Queues/AzureServiceBusQueue.cs @@ -1,15 +1,12 @@ using Genocs.Core.CQRS.Commands; -using Genocs.ServiceBusAzure.Options; +using Genocs.ServiceBusAzure.Configurations; using Genocs.ServiceBusAzure.Queues.Interfaces; using Microsoft.Azure.ServiceBus; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; -using System; -using System.Collections.Generic; using System.Text; -using System.Threading.Tasks; namespace Genocs.ServiceBusAzure.Queues; @@ -19,7 +16,7 @@ namespace Genocs.ServiceBusAzure.Queues; public class AzureServiceBusQueue : IAzureServiceBusQueue { private readonly IQueueClient _queueClient; - private readonly AzureServiceBusQueueSettings _options; + private readonly AzureServiceBusQueueOptions _options; private readonly ILogger _logger; private Dictionary> _handlers = new Dictionary>(); private const string COMMAND_SUFFIX = "Command"; @@ -32,7 +29,7 @@ public class AzureServiceBusQueue : IAzureServiceBusQueue /// /// /// - public AzureServiceBusQueue(IOptions options, + public AzureServiceBusQueue(IOptions options, IServiceProvider serviceProvider, ILogger logger) { @@ -62,7 +59,7 @@ public AzureServiceBusQueue(IOptions options, /// /// /// - public AzureServiceBusQueue(AzureServiceBusQueueSettings options, + public AzureServiceBusQueue(AzureServiceBusQueueOptions options, IServiceProvider serviceProvider, ILogger logger) { @@ -70,6 +67,7 @@ public AzureServiceBusQueue(AzureServiceBusQueueSettings options, { throw new ArgumentNullException(nameof(options)); } + _options = options; _logger = logger ?? throw new ArgumentNullException(nameof(logger)); diff --git a/src/Genocs.ServiceBusAzure/Queues/Interfaces/IAzureServiceBusQueue.cs b/src/Genocs.ServiceBusAzure/Queues/Interfaces/IAzureServiceBusQueue.cs index f544d3fc..2748c189 100644 --- a/src/Genocs.ServiceBusAzure/Queues/Interfaces/IAzureServiceBusQueue.cs +++ b/src/Genocs.ServiceBusAzure/Queues/Interfaces/IAzureServiceBusQueue.cs @@ -2,13 +2,14 @@ namespace Genocs.ServiceBusAzure.Queues.Interfaces; - /// -/// Azure Service bus +/// Azure Service bus. /// public interface IAzureServiceBusQueue { Task SendAsync(ICommand command); Task ScheduleAsync(ICommand command, DateTimeOffset offset); - void Consume() where T : ICommand where TH : ICommandHandlerLegacy; + void Consume() + where T : ICommand + where TH : ICommandHandlerLegacy; } diff --git a/src/Genocs.ServiceBusAzure/README.md b/src/Genocs.ServiceBusAzure/README_NUGET.md similarity index 74% rename from src/Genocs.ServiceBusAzure/README.md rename to src/Genocs.ServiceBusAzure/README_NUGET.md index 9d913aa8..06093b27 100644 --- a/src/Genocs.ServiceBusAzure/README.md +++ b/src/Genocs.ServiceBusAzure/README_NUGET.md @@ -33,7 +33,35 @@ Following are the project settings needed to enable monitoring ## Release notes -### [2023-03-12] 5.0.0-preview.5.0 +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 - Implemented MongoDB repository interfaces ### [2023-03-12] 5.0.0 diff --git a/src/Genocs.ServiceBusAzure/Topics/AzureServiceBusTopic.cs b/src/Genocs.ServiceBusAzure/Topics/AzureServiceBusTopic.cs index 0fe896e5..9686274b 100644 --- a/src/Genocs.ServiceBusAzure/Topics/AzureServiceBusTopic.cs +++ b/src/Genocs.ServiceBusAzure/Topics/AzureServiceBusTopic.cs @@ -1,5 +1,5 @@ using Genocs.Core.CQRS.Events; -using Genocs.ServiceBusAzure.Options; +using Genocs.ServiceBusAzure.Configurations; using Genocs.ServiceBusAzure.Topics.Interfaces; using Microsoft.Azure.ServiceBus; using Microsoft.Extensions.DependencyInjection; @@ -8,239 +8,239 @@ using Newtonsoft.Json; using System.Text; -namespace Genocs.ServiceBusAzure.Topics +namespace Genocs.ServiceBusAzure.Topics; + +public class AzureServiceBusTopic : IAzureServiceBusTopic { - public class AzureServiceBusTopic : IAzureServiceBusTopic + private readonly TopicClient _topicClient; + private readonly AzureServiceBusTopicOptions _options; + private readonly ILogger _logger; + private readonly IServiceProvider _serviceProvider; + private const string EVENT_SUFFIX = "Event"; + private readonly Dictionary> _handlers; + private readonly SubscriptionClient _subscriptionClient; + private readonly List _eventTypes; + + public AzureServiceBusTopic( + IOptions options, + IServiceProvider serviceProvider, + ILogger logger) { - private readonly TopicClient _topicClient; - private readonly AzureServiceBusTopicSettings _options; - private readonly ILogger _logger; - private readonly IServiceProvider _serviceProvider; - private const string EVENT_SUFFIX = "Event"; - private readonly Dictionary> _handlers; - private readonly SubscriptionClient _subscriptionClient; - private readonly List _eventTypes; - - public AzureServiceBusTopic(IOptions options, - IServiceProvider serviceProvider, - ILogger logger) + if (options == null) { - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } - _options = options.Value; - - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - - ServiceBusConnectionStringBuilder serviceBusConnectionStringBuilder = new ServiceBusConnectionStringBuilder(_options.ConnectionString); - serviceBusConnectionStringBuilder.EntityPath = _options.TopicName; - _topicClient = new TopicClient(serviceBusConnectionStringBuilder, _options.RetryPolicy); - _handlers = new Dictionary>(); - _eventTypes = new List(); - if (!string.IsNullOrEmpty(_options.SubscriptionName)) - { - _subscriptionClient = new SubscriptionClient(serviceBusConnectionStringBuilder, _options.SubscriptionName); - RegisterSubscriptionClientMessageHandler(); - } + throw new ArgumentNullException(nameof(options)); } + _options = options.Value; + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - public AzureServiceBusTopic(AzureServiceBusTopicSettings options, - IServiceProvider serviceProvider, - ILogger logger) + ServiceBusConnectionStringBuilder serviceBusConnectionStringBuilder = new ServiceBusConnectionStringBuilder(_options.ConnectionString); + serviceBusConnectionStringBuilder.EntityPath = _options.TopicName; + _topicClient = new TopicClient(serviceBusConnectionStringBuilder, _options.RetryPolicy); + _handlers = new Dictionary>(); + _eventTypes = new List(); + if (!string.IsNullOrEmpty(_options.SubscriptionName)) { - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } - _options = options; - - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - - ServiceBusConnectionStringBuilder serviceBusConnectionStringBuilder = new ServiceBusConnectionStringBuilder(_options.ConnectionString); - serviceBusConnectionStringBuilder.EntityPath = _options.TopicName; - _topicClient = new TopicClient(serviceBusConnectionStringBuilder, _options.RetryPolicy); - _handlers = new Dictionary>(); - _eventTypes = new List(); - if (!string.IsNullOrEmpty(_options.SubscriptionName)) - { - _subscriptionClient = new SubscriptionClient(serviceBusConnectionStringBuilder, _options.SubscriptionName); - RegisterSubscriptionClientMessageHandler(); - } + _subscriptionClient = new SubscriptionClient(serviceBusConnectionStringBuilder, _options.SubscriptionName); + RegisterSubscriptionClientMessageHandler(); } - - public async Task PublishAsync(IEvent @event) + } + public AzureServiceBusTopic( + AzureServiceBusTopicOptions options, + IServiceProvider serviceProvider, + ILogger logger) + { + if (options == null) { - var eventName = @event.GetType().Name.Replace(EVENT_SUFFIX, ""); - var jsonMessage = JsonConvert.SerializeObject(@event); - var body = Encoding.UTF8.GetBytes(jsonMessage); - - var message = new Message - { - MessageId = Guid.NewGuid().ToString(), - Body = body, - Label = eventName, - }; + throw new ArgumentNullException(nameof(options)); + } + _options = options; - await _topicClient.SendAsync(message); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + ServiceBusConnectionStringBuilder serviceBusConnectionStringBuilder = new ServiceBusConnectionStringBuilder(_options.ConnectionString); + serviceBusConnectionStringBuilder.EntityPath = _options.TopicName; + _topicClient = new TopicClient(serviceBusConnectionStringBuilder, _options.RetryPolicy); + _handlers = new Dictionary>(); + _eventTypes = new List(); + if (!string.IsNullOrEmpty(_options.SubscriptionName)) + { + _subscriptionClient = new SubscriptionClient(serviceBusConnectionStringBuilder, _options.SubscriptionName); + RegisterSubscriptionClientMessageHandler(); } + } + + public async Task PublishAsync(IEvent @event) + { + string eventName = @event.GetType().Name.Replace(EVENT_SUFFIX, string.Empty); + string jsonMessage = JsonConvert.SerializeObject(@event); + byte[] body = Encoding.UTF8.GetBytes(jsonMessage); - public async Task PublishAsync(IEvent @event, Dictionary filters) + var message = new Message { - var eventName = @event.GetType().Name.Replace(EVENT_SUFFIX, ""); - var jsonMessage = JsonConvert.SerializeObject(@event); - var body = Encoding.UTF8.GetBytes(jsonMessage); + MessageId = Guid.NewGuid().ToString(), + Body = body, + Label = eventName, + }; - var message = new Message - { - MessageId = Guid.NewGuid().ToString(), - Body = body, - Label = eventName, - }; + await _topicClient.SendAsync(message); - foreach (KeyValuePair filter in filters) - { - message.UserProperties.Add(filter); - } + } - await _topicClient.SendAsync(message); - } + public async Task PublishAsync(IEvent @event, Dictionary filters) + { + string eventName = @event.GetType().Name.Replace(EVENT_SUFFIX, string.Empty); + string jsonMessage = JsonConvert.SerializeObject(@event); + byte[] body = Encoding.UTF8.GetBytes(jsonMessage); - public async Task ScheduleAsync(IEvent @event, DateTimeOffset offset) + var message = new Message { - var eventName = @event.GetType().Name.Replace(EVENT_SUFFIX, ""); - var jsonMessage = JsonConvert.SerializeObject(@event); - var body = Encoding.UTF8.GetBytes(jsonMessage); + MessageId = Guid.NewGuid().ToString(), + Body = body, + Label = eventName, + }; - var message = new Message - { - MessageId = Guid.NewGuid().ToString(), - Body = body, - Label = eventName, - }; - await _topicClient.ScheduleMessageAsync(message, offset); + foreach (KeyValuePair filter in filters) + { + message.UserProperties.Add(filter); } - public async Task ScheduleAsync(IEvent @event, DateTimeOffset offset, Dictionary filters) - { - var eventName = @event.GetType().Name.Replace(EVENT_SUFFIX, ""); - var jsonMessage = JsonConvert.SerializeObject(@event); - var body = Encoding.UTF8.GetBytes(jsonMessage); + await _topicClient.SendAsync(message); + } - var message = new Message - { - MessageId = Guid.NewGuid().ToString(), - Body = body, - Label = eventName, - }; + public async Task ScheduleAsync(IEvent @event, DateTimeOffset offset) + { + string eventName = @event.GetType().Name.Replace(EVENT_SUFFIX, string.Empty); + string jsonMessage = JsonConvert.SerializeObject(@event); + byte[] body = Encoding.UTF8.GetBytes(jsonMessage); - foreach (KeyValuePair filter in filters) - { - message.UserProperties.Add(filter); - } + var message = new Message + { + MessageId = Guid.NewGuid().ToString(), + Body = body, + Label = eventName, + }; + await _topicClient.ScheduleMessageAsync(message, offset); + } - await _topicClient.ScheduleMessageAsync(message, offset); - } + public async Task ScheduleAsync(IEvent @event, DateTimeOffset offset, Dictionary filters) + { + string eventName = @event.GetType().Name.Replace(EVENT_SUFFIX, string.Empty); + string jsonMessage = JsonConvert.SerializeObject(@event); + byte[] body = Encoding.UTF8.GetBytes(jsonMessage); - /// - /// Todo - /// - /// - /// - /// - public void Subscribe() - where T : IEvent - where TH : IEventHandlerLegacy + var message = new Message { - var eventName = typeof(T).Name.Replace(EVENT_SUFFIX, ""); - var key = typeof(T).Name; - if (!_handlers.ContainsKey(key)) - { - _handlers.Add(key, new List()); + MessageId = Guid.NewGuid().ToString(), + Body = body, + Label = eventName, + }; - //_subscriptionClient.AddRuleAsync(new RuleDescription - //{ - // Filter = new CorrelationFilter { Label = eventName.ToLower() }, - // Name = eventName - //}).GetAwaiter().GetResult(); - } - Type handlerType = typeof(TH); - - if (_handlers[key].Any(s => s.HandlerType == handlerType)) - { - throw new ArgumentException( - $"Handler Type '{typeof(TH).Name}' already registered for '{key}'", nameof(handlerType)); - } + foreach (KeyValuePair filter in filters) + { + message.UserProperties.Add(filter); + } - if (!_eventTypes.Contains(typeof(T))) - { - _eventTypes.Add(typeof(T)); - } + await _topicClient.ScheduleMessageAsync(message, offset); + } - _handlers[key].Add(SubscriptionInfo.Typed(handlerType)); + /// + /// Todo + /// + /// + /// + /// + public void Subscribe() + where T : IEvent + where TH : IEventHandlerLegacy + { + string eventName = typeof(T).Name.Replace(EVENT_SUFFIX, ""); + string key = typeof(T).Name; + if (!_handlers.ContainsKey(key)) + { + _handlers.Add(key, new List()); + //_subscriptionClient.AddRuleAsync(new RuleDescription + //{ + // Filter = new CorrelationFilter { Label = eventName.ToLower() }, + // Name = eventName + //}).GetAwaiter().GetResult(); } - private void RegisterSubscriptionClientMessageHandler() - { - _subscriptionClient.RegisterMessageHandler( - async (message, token) => - { - var eventName = $"{message.Label}{EVENT_SUFFIX}"; - var messageData = Encoding.UTF8.GetString(message.Body); + Type handlerType = typeof(TH); - // Complete the message so that it is not received again. - if (await ProcessEvent(eventName, messageData)) - { - await _subscriptionClient.CompleteAsync(message.SystemProperties.LockToken); - } - }, - new MessageHandlerOptions(ExceptionReceivedHandler) { MaxConcurrentCalls = _options.MaxConcurrentCalls, AutoComplete = false }); + if (_handlers[key].Any(s => s.HandlerType == handlerType)) + { + throw new ArgumentException( + $"Handler Type '{typeof(TH).Name}' already registered for '{key}'", nameof(handlerType)); } - private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs) + if (!_eventTypes.Contains(typeof(T))) { - var ex = exceptionReceivedEventArgs.Exception; - var context = exceptionReceivedEventArgs.ExceptionReceivedContext; + _eventTypes.Add(typeof(T)); + } + + _handlers[key].Add(SubscriptionInfo.Typed(handlerType)); + } - _logger.LogError(ex, "ERROR handling message: {ExceptionMessage} - Context: {@ExceptionContext}", ex.Message, context); + private void RegisterSubscriptionClientMessageHandler() + { + _subscriptionClient.RegisterMessageHandler( + async (message, token) => + { + string eventName = $"{message.Label}{EVENT_SUFFIX}"; + string messageData = Encoding.UTF8.GetString(message.Body); - return Task.CompletedTask; - } + // Complete the message so that it is not received again. + if (await ProcessEvent(eventName, messageData)) + { + await _subscriptionClient.CompleteAsync(message.SystemProperties.LockToken); + } + }, + new MessageHandlerOptions(ExceptionReceivedHandler) { MaxConcurrentCalls = _options.MaxConcurrentCalls, AutoComplete = false }); + } + + private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs) + { + var ex = exceptionReceivedEventArgs.Exception; + var context = exceptionReceivedEventArgs.ExceptionReceivedContext; + + _logger.LogError(ex, "ERROR handling message: {ExceptionMessage} - Context: {@ExceptionContext}", ex.Message, context); + return Task.CompletedTask; + } - private async Task ProcessEvent(string eventName, string message) + private async Task ProcessEvent(string eventName, string message) + { + bool processed = false; + if (_handlers.ContainsKey(eventName)) { - var processed = false; - if (_handlers.ContainsKey(eventName)) + using (var scope = _serviceProvider.CreateScope()) { - using (var scope = _serviceProvider.CreateScope()) - { - var subscriptions = _handlers[eventName]; + var subscriptions = _handlers[eventName]; - foreach (var subscription in subscriptions) + foreach (var subscription in subscriptions) + { + object handler = scope.ServiceProvider.GetRequiredService(subscription.HandlerType); + if (handler != null) { - var handler = scope.ServiceProvider.GetRequiredService(subscription.HandlerType); - if (handler != null) - { - var eventType = _eventTypes.SingleOrDefault(e => e.Name == eventName); - var command = JsonConvert.DeserializeObject(message, eventType); - var concreteType = typeof(IEventHandler<>).MakeGenericType(eventType); - await (Task)concreteType.GetMethod("HandleEvent").Invoke(handler, new object[] { command }); - } + var eventType = _eventTypes.SingleOrDefault(e => e.Name == eventName); + object command = JsonConvert.DeserializeObject(message, eventType); + var concreteType = typeof(IEventHandler<>).MakeGenericType(eventType); + await (Task)concreteType.GetMethod("HandleEvent").Invoke(handler, new object[] { command }); } } - processed = true; - } - else - { - _logger.LogError($"Event '{eventName}' do not contains handlers. Check whether Subscribe is set"); } - return processed; + + processed = true; } + else + { + _logger.LogError($"Event '{eventName}' do not contains handlers. Check whether Subscribe is set"); + } + + return processed; } } diff --git a/src/Genocs.Tracing.Jaeger.RabbitMQ/Genocs.Tracing.Jaeger.RabbitMQ.csproj b/src/Genocs.Tracing.Jaeger.RabbitMQ/Genocs.Tracing.Jaeger.RabbitMQ.csproj index 20ed6ed9..850a07f9 100644 --- a/src/Genocs.Tracing.Jaeger.RabbitMQ/Genocs.Tracing.Jaeger.RabbitMQ.csproj +++ b/src/Genocs.Tracing.Jaeger.RabbitMQ/Genocs.Tracing.Jaeger.RabbitMQ.csproj @@ -1,55 +1,30 @@  - net6.0;net7.0 - enable - enable + net8.0;net7.0;net6.0 Genocs.Tracing.Jaeger.RabbitMQ - 10.0 + Genocs.Tracing.Jaeger.RabbitMQ + Genocs.Tracing.Jaeger.RabbitMQ + The load balacer based on Fabio library useful to build .NET Core projects. + The load balacer based on Fabio library useful to build .NET Core projects. true - 5.0.0-preview1.0 5.0.0 Nocco Giovanni Emanuele - Genocs - The load balacer based on Fabio library useful to build .NET Core projects. - The load balacer based on Fabio library useful to build .NET Core projects. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md + README_NUGET.md Aligned to the ecosystem True latest - True - - - True - \ - - - True - \ - - - True - \ - - - - - + + diff --git a/src/Genocs.Tracing.Jaeger.RabbitMQ/README.md b/src/Genocs.Tracing.Jaeger.RabbitMQ/README.md deleted file mode 100644 index 945bb03b..00000000 --- a/src/Genocs.Tracing.Jaeger.RabbitMQ/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# .NET query builder library - -This package contains a query builder that is agnostic about the persistence layer. The library is designed by Genocs. -The libraries are built using .NET standard 2.1. - -## Description - -Persistence agnostic query builder service. - - -## Support - -Please check the GitHub repository getting more info. - - -## Release notes - -### [2023-03-12] 5.0.0-preview.5.0 -- Implemented MongoDB repository interfaces - -### [2023-03-12] 5.0.0 -- New Architecture - -### [2023-03-12] 3.1.0 -- Added Builders - -### [2023-03-12] 3.0.0 -- Refactory to implement CQRS pattern - -### [2023-03-04] 2.4.1 -- Updated System.Text.Json - -### [2023-01-23] 1.1.0 -- Refactory enum - -### [2023-01-13] 1.0.0 -- First Release \ No newline at end of file diff --git a/src/Genocs.Tracing.Jaeger.RabbitMQ/README_NUGET.md b/src/Genocs.Tracing.Jaeger.RabbitMQ/README_NUGET.md new file mode 100644 index 00000000..6ffbc96c --- /dev/null +++ b/src/Genocs.Tracing.Jaeger.RabbitMQ/README_NUGET.md @@ -0,0 +1,65 @@ +# .NET query builder library + +This package contains a query builder that is agnostic about the persistence layer. The library is designed by Genocs. +The libraries are built using .NET standard 2.1. + +## Description + +Persistence agnostic query builder service. + + +## Support + +Please check the GitHub repository getting more info. + + +## Release notes + +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 +- Implemented MongoDB repository interfaces + +### [2023-03-12] 5.0.0 +- New Architecture + +### [2023-03-12] 3.1.0 +- Added Builders + +### [2023-03-12] 3.0.0 +- Refactory to implement CQRS pattern + +### [2023-03-04] 2.4.1 +- Updated System.Text.Json + +### [2023-01-23] 1.1.0 +- Refactory enum + +### [2023-01-13] 1.0.0 +- First Release \ No newline at end of file diff --git a/src/Genocs.Tracing/Extensions.cs b/src/Genocs.Tracing/Extensions.cs index f6328503..9ff0aef4 100644 --- a/src/Genocs.Tracing/Extensions.cs +++ b/src/Genocs.Tracing/Extensions.cs @@ -1,143 +1,158 @@ using Azure.Monitor.OpenTelemetry.Exporter; -using Genocs.Common.Options; +using Genocs.Common.Configurations; using Genocs.Core.Builders; -using Genocs.Logging.Options; -using Genocs.Tracing.Jaeger.Options; -using Jaeger.Samplers; -using Jaeger.Senders.Thrift; +using Genocs.Logging.Configurations; +using Genocs.Tracing.Jaeger.Configurations; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using OpenTelemetry; +using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; namespace Genocs.Tracing; /// -/// The Open Telemetry extensions +/// The Open Telemetry extensions. /// public static class Extensions { /// - /// Custom settings for OpenTelemetry + /// Custom settings for OpenTelemetry. /// - /// The genocs builder - /// the builder + /// The Genocs builder. + /// The Genocs builder you can use for chain. public static IGenocsBuilder AddOpenTelemetry(this IGenocsBuilder builder) { - var appOptions = builder.GetOptions(AppSettings.Position); + AppOptions options = builder.GetOptions(AppOptions.Position); // No OpenTelemetryTracing in case of missing ServiceName - if (string.IsNullOrWhiteSpace(appOptions.Service)) + if (string.IsNullOrWhiteSpace(options.Service)) { return builder; } - var services = builder.Services; + //builder.Logging.AddOpenTelemetry(logging => + //{ + // logging.IncludeFormattedMessage = true; + // logging.IncludeScopes = true; + //}); + LoggerOptions loggerOptions = builder.GetOptions(LoggerOptions.Position); - // Set Custom Open telemetry - services.AddOpenTelemetry().WithTracing(x => + if (loggerOptions is null) { - TracerProviderBuilder provider = x.SetResourceBuilder(ResourceBuilder.CreateDefault() - .AddService(appOptions.Service) - .AddTelemetrySdk() - .AddEnvironmentVariableDetector()) - .AddSource("*"); - - // TODO> add flag to enable feature MongoDB.Driver.Core.Extensions.OpenTelemetry - provider.AddMongoDBInstrumentation(); - - - var loggerOptions = builder.GetOptions(LoggerSettings.Position); - + return builder; + } - // Check for Console config - if (loggerOptions.Console != null && loggerOptions.Console.Enabled) - { - // OpenTelemetry.Exporter.Console NuGet package - provider.AddConsoleExporter(); - } + var services = builder.Services; - // Check for Azure ApplicationInsights config - if (loggerOptions.Azure != null && loggerOptions.Azure.Enabled) + // Set Custom Open telemetry + services.AddOpenTelemetry() + .WithTracing(x => { - provider.AddAzureMonitorTraceExporter(o => + TracerProviderBuilder provider = x.SetResourceBuilder(ResourceBuilder.CreateDefault() + .AddService(serviceName: options.Service, serviceVersion: options.Version, serviceInstanceId: options.Instance) + .AddTelemetrySdk() + .AddEnvironmentVariableDetector()) + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddSource("*"); + + // No OpenTelemetryTracing in case of missing LoggerSettings + if (loggerOptions.Mongo != null && loggerOptions.Mongo.Enabled) { - o.ConnectionString = loggerOptions.Azure.ConnectionString; - }); - } + // Check for MongoDB config + provider.AddSource("MongoDB.Driver.Core.Extensions.DiagnosticSources"); + } - var jaegerOptions = builder.GetOptions(JaegerSettings.Position); + // Check for Console config + if (loggerOptions.Console != null && loggerOptions.Console.Enabled) + { + // you should add OpenTelemetry.Exporter.Console NuGet package + // Any OTEL supportable exporter can be used here + provider.AddConsoleExporter(); + } - if (jaegerOptions != null && jaegerOptions.Enabled) - { - provider.AddJaegerExporter(o => + // Check for Azure ApplicationInsights config + if (loggerOptions.Azure != null && loggerOptions.Azure.Enabled) { - o.AgentHost = jaegerOptions.UdpHost; - o.AgentPort = jaegerOptions.UdpPort; - o.MaxPayloadSizeInBytes = jaegerOptions.MaxPacketSize; - o.ExportProcessorType = ExportProcessorType.Batch; - o.BatchExportProcessorOptions = new BatchExportProcessorOptions + provider.AddAzureMonitorTraceExporter(o => { - MaxQueueSize = 2048, - ScheduledDelayMilliseconds = 5000, - ExporterTimeoutMilliseconds = 30000, - MaxExportBatchSize = 512, - }; - }); - } - }); - - return builder; - } + o.ConnectionString = loggerOptions.Azure.ConnectionString; + }); + } - private static ISampler GetSampler(JaegerSettings options) - { - switch (options.Sampler) - { - case "const": return new ConstSampler(true); - case "rate": return new RateLimitingSampler(options.MaxTracesPerSecond); - case "probabilistic": return new ProbabilisticSampler(options.SamplingRate); - default: return new ConstSampler(true); - } - } + var jaegerOptions = builder.GetOptions(JaegerOptions.Position); - private static HttpSender BuildHttpSender(JaegerSettings.HttpSenderSettings? options) - { - if (options is null) - { - throw new Exception("Missing Jaeger HTTP sender options."); - } + if (jaegerOptions != null && jaegerOptions.Enabled) + { + provider.AddOtlpExporter(o => + { + o.Endpoint = new Uri(jaegerOptions.Endpoint); + + // Parse enum + o.Protocol = Enum.Parse(jaegerOptions.Protocol); + o.ExportProcessorType = Enum.Parse(jaegerOptions.ProcessorType); + + // Check if Batch Exporter before setting options + o.BatchExportProcessorOptions = new BatchExportProcessorOptions + { + MaxQueueSize = jaegerOptions.MaxQueueSize, + ScheduledDelayMilliseconds = jaegerOptions.ScheduledDelayMilliseconds, + ExporterTimeoutMilliseconds = jaegerOptions.ExporterTimeoutMilliseconds, + MaxExportBatchSize = jaegerOptions.MaxExportBatchSize + }; + }); + } + + /* + Action appResourceBuilder = + resource => resource + .AddDetector(new ContainerResourceDetector()); + + builder.Services.AddOpenTelemetry() + .ConfigureResource(appResourceBuilder) + .WithTracing(tracerBuilder => tracerBuilder + .AddRedisInstrumentation( + cartStore.GetConnection(), + options => options.SetVerboseDatabaseStatements = true) + .AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation() + .AddOtlpExporter()) + + */ + }).WithMetrics(x => + { + MeterProviderBuilder provider = x.SetResourceBuilder(ResourceBuilder.CreateDefault()); - if (string.IsNullOrWhiteSpace(options.Endpoint)) - { - throw new Exception("Missing Jaeger HTTP sender endpoint."); - } + provider.AddAspNetCoreInstrumentation(); - var builder = new HttpSender.Builder(options.Endpoint); - if (options.MaxPacketSize > 0) - { - builder = builder.WithMaxPacketSize(options.MaxPacketSize); - } + // provider.AddRuntimeInstrumentation(); + provider.AddHttpClientInstrumentation(); + provider.AddOtlpExporter(); - if (!string.IsNullOrWhiteSpace(options.AuthToken)) - { - builder = builder.WithAuth(options.AuthToken); - } - if (!string.IsNullOrWhiteSpace(options.Username) && !string.IsNullOrWhiteSpace(options.Password)) - { - builder = builder.WithAuth(options.Username, options.Password); - } + // Check for Console config + if (loggerOptions.Console != null && loggerOptions.Console.Enabled) + { + // you should add OpenTelemetry.Exporter.Console NuGet package + // Any OTEL supportable exporter can be used here + provider.AddConsoleExporter(); + } - if (!string.IsNullOrWhiteSpace(options.UserAgent)) - { - builder = builder.WithUserAgent(options.Username); - } + // Check for Azure ApplicationInsights config + if (loggerOptions.Azure != null && loggerOptions.Azure.Enabled) + { + provider.AddAzureMonitorMetricExporter(o => + { + o.ConnectionString = loggerOptions.Azure.ConnectionString; + }); + } + }); - return builder.Build(); + return builder; } } diff --git a/src/Genocs.Tracing/Genocs.Tracing.csproj b/src/Genocs.Tracing/Genocs.Tracing.csproj index c887dff2..5c92a7dd 100644 --- a/src/Genocs.Tracing/Genocs.Tracing.csproj +++ b/src/Genocs.Tracing/Genocs.Tracing.csproj @@ -1,70 +1,41 @@  - - net6.0;net7.0 - enable - enable - Genocs.Tracing - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The tracing library to use Azure Service Bus. - The tracing library to use Azure Service Bus. - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - - - - - True - \ - - - True - \ - - - True - \ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + net8.0;net7.0;net6.0 + Genocs.Tracing + Genocs.Tracing + Genocs.Tracing + The tracing library to use Azure Service Bus. + The tracing library to use Azure Service Bus. + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Genocs.Tracing/Jaeger/Builders/JaegerOptionsBuilder.cs b/src/Genocs.Tracing/Jaeger/Builders/JaegerOptionsBuilder.cs index 831f4f07..a5b1b3eb 100644 --- a/src/Genocs.Tracing/Jaeger/Builders/JaegerOptionsBuilder.cs +++ b/src/Genocs.Tracing/Jaeger/Builders/JaegerOptionsBuilder.cs @@ -1,10 +1,10 @@ -using Genocs.Tracing.Jaeger.Options; +using Genocs.Tracing.Jaeger.Configurations; namespace Genocs.Tracing.Jaeger.Builders; internal sealed class JaegerOptionsBuilder : IJaegerOptionsBuilder { - private readonly JaegerSettings _options = new(); + private readonly JaegerOptions _options = new(); public IJaegerOptionsBuilder Enable(bool enabled) { @@ -18,42 +18,54 @@ public IJaegerOptionsBuilder WithServiceName(string serviceName) return this; } - public IJaegerOptionsBuilder WithUdpHost(string udpHost) + public IJaegerOptionsBuilder WithEndpoint(string endpoint) { - _options.UdpHost = udpHost; + _options.Endpoint = endpoint; return this; } - public IJaegerOptionsBuilder WithUdpPort(int udpPort) + public IJaegerOptionsBuilder WithProtocol(string protocol) { - _options.UdpPort = udpPort; + _options.Protocol = protocol; return this; } - public IJaegerOptionsBuilder WithMaxPacketSize(int maxPacketSize) + public IJaegerOptionsBuilder WithProcessorType(string processorType) { - _options.MaxPacketSize = maxPacketSize; + _options.ProcessorType = processorType; return this; } - public IJaegerOptionsBuilder WithSampler(string sampler) + public IJaegerOptionsBuilder WithMaxQueueSize(int maxQueueSize) { - _options.Sampler = sampler; + _options.MaxQueueSize = maxQueueSize; return this; } - public IJaegerOptionsBuilder WithMaxTracesPerSecond(double maxTracesPerSecond) + public IJaegerOptionsBuilder MaxQueueSize(int maxQueueSize) { - _options.MaxTracesPerSecond = maxTracesPerSecond; + _options.MaxQueueSize = maxQueueSize; return this; } - public IJaegerOptionsBuilder WithSamplingRate(double samplingRate) + public IJaegerOptionsBuilder WithScheduledDelayMilliseconds(int scheduledDelayMilliseconds) { - _options.SamplingRate = samplingRate; + _options.ScheduledDelayMilliseconds = scheduledDelayMilliseconds; return this; } - public JaegerSettings Build() + public IJaegerOptionsBuilder WithExporterTimeoutMilliseconds(int exporterTimeoutMilliseconds) + { + _options.ExporterTimeoutMilliseconds = exporterTimeoutMilliseconds; + return this; + } + + public IJaegerOptionsBuilder WithMaxExportBatchSize(int maxExportBatchSize) + { + _options.MaxExportBatchSize = maxExportBatchSize; + return this; + } + + public JaegerOptions Build() => _options; } \ No newline at end of file diff --git a/src/Genocs.Tracing/Jaeger/Configurations/IJaegerOptionsBuilder.cs b/src/Genocs.Tracing/Jaeger/Configurations/IJaegerOptionsBuilder.cs new file mode 100644 index 00000000..859449f8 --- /dev/null +++ b/src/Genocs.Tracing/Jaeger/Configurations/IJaegerOptionsBuilder.cs @@ -0,0 +1,15 @@ +namespace Genocs.Tracing.Jaeger.Configurations; + +public interface IJaegerOptionsBuilder +{ + IJaegerOptionsBuilder Enable(bool enabled); + IJaegerOptionsBuilder WithServiceName(string serviceName); + IJaegerOptionsBuilder WithEndpoint(string endpoint); + IJaegerOptionsBuilder WithProtocol(string protocol); + IJaegerOptionsBuilder WithProcessorType(string processorType); + IJaegerOptionsBuilder WithMaxQueueSize(int maxQueueSize); + IJaegerOptionsBuilder WithScheduledDelayMilliseconds(int scheduledDelayMilliseconds); + IJaegerOptionsBuilder WithExporterTimeoutMilliseconds(int exporterTimeoutMilliseconds); + IJaegerOptionsBuilder WithMaxExportBatchSize(int maxExportBatchSize); + JaegerOptions Build(); +} \ No newline at end of file diff --git a/src/Genocs.Tracing/Jaeger/Configurations/JaegerOptions.cs b/src/Genocs.Tracing/Jaeger/Configurations/JaegerOptions.cs new file mode 100644 index 00000000..9263f4ae --- /dev/null +++ b/src/Genocs.Tracing/Jaeger/Configurations/JaegerOptions.cs @@ -0,0 +1,41 @@ +namespace Genocs.Tracing.Jaeger.Configurations; + +/// +/// Jaeger Settings. +/// +public class JaegerOptions +{ + /// + /// Default section name. + /// + public const string Position = "jaeger"; + + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + public string? ServiceName { get; set; } + + /// + /// The Jaeger agent endpoint. + /// + public string Endpoint { get; set; } = "http://localhost:4317"; + + /// + /// The used OtlpExportProtocol. + /// IT could be [Grpc|HttpProtobuf]. + /// + public string Protocol { get; set; } = "Grpc"; + + /// + /// The used ExportProcessorType. + /// It could be [Simple|Batch]. + /// + public string ProcessorType { get; set; } = "Batch"; + + public int MaxQueueSize { get; set; } = 2048; + public int ScheduledDelayMilliseconds { get; set; } = 5000; + public int ExporterTimeoutMilliseconds { get; set; } = 30000; + public int MaxExportBatchSize { get; set; } = 512; +} \ No newline at end of file diff --git a/src/Genocs.Tracing/Jaeger/Extensions.cs b/src/Genocs.Tracing/Jaeger/Extensions.cs deleted file mode 100644 index a4f03bd0..00000000 --- a/src/Genocs.Tracing/Jaeger/Extensions.cs +++ /dev/null @@ -1,147 +0,0 @@ -using Genocs.Core.Builders; -using Genocs.Tracing.Jaeger.Options; -using Genocs.Tracing.Jaeger.Tracers; -using Jaeger; -using Jaeger.Reporters; -using Jaeger.Samplers; -using Jaeger.Senders; -using Jaeger.Senders.Thrift; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using OpenTracing; -using OpenTracing.Util; - -namespace Genocs.Tracing.Jaeger; - - -/// -/// The Open Tracing -/// -public static class Extensions -{ - private static int _initialized; - private const string RegistryName = "tracing.jaeger"; - - - /// - /// Add Jaeger Tracer - /// - /// - /// - /// - /// - public static IGenocsBuilder AddJaeger(this IGenocsBuilder builder, string sectionName = JaegerSettings.Position) - { - if (Interlocked.Exchange(ref _initialized, 1) == 1) - { - return builder; - } - - - var options = builder.GetOptions(sectionName); - - builder.Services.AddSingleton(options); - - if (!options.Enabled) - { - var defaultTracer = GenocsDefaultTracer.Create(); - builder.Services.AddSingleton(defaultTracer); - return builder; - } - - if (!builder.TryRegister(RegistryName)) - { - return builder; - } - - - builder.Services.AddSingleton(sp => - { - var loggerFactory = sp.GetRequiredService(); - var maxPacketSize = options.MaxPacketSize <= 0 ? 64967 : options.MaxPacketSize; - var senderType = string.IsNullOrWhiteSpace(options.Sender) ? "udp" : options.Sender?.ToLowerInvariant(); - ISender sender = senderType switch - { - "http" => BuildHttpSender(options.HttpSender), - "udp" => new UdpSender(options.UdpHost, options.UdpPort, maxPacketSize), - _ => throw new Exception($"Invalid Jaeger sender type: '{senderType}'.") - }; - - var reporter = new RemoteReporter.Builder() - .WithSender(sender) - .WithLoggerFactory(loggerFactory) - .Build(); - - var sampler = GetSampler(options); - - var tracer = new Tracer.Builder(options.ServiceName) - .WithLoggerFactory(loggerFactory) - .WithReporter(reporter) - .WithSampler(sampler) - .Build(); - - GlobalTracer.Register(tracer); - - return tracer; - }); - - return builder; - } - - private static HttpSender BuildHttpSender(JaegerSettings.HttpSenderSettings? options) - { - if (options is null) - { - throw new Exception("Missing Jaeger HTTP sender options."); - } - - if (string.IsNullOrWhiteSpace(options.Endpoint)) - { - throw new Exception("Missing Jaeger HTTP sender endpoint."); - } - - var builder = new HttpSender.Builder(options.Endpoint); - if (options.MaxPacketSize > 0) - { - builder = builder.WithMaxPacketSize(options.MaxPacketSize); - } - - if (!string.IsNullOrWhiteSpace(options.AuthToken)) - { - builder = builder.WithAuth(options.AuthToken); - } - - if (!string.IsNullOrWhiteSpace(options.Username) && !string.IsNullOrWhiteSpace(options.Password)) - { - builder = builder.WithAuth(options.Username, options.Password); - } - - if (!string.IsNullOrWhiteSpace(options.UserAgent)) - { - builder = builder.WithUserAgent(options.Username); - } - - return builder.Build(); - } - - public static IApplicationBuilder UseJaeger(this IApplicationBuilder app) - { - // Could be extended with some additional middleware - using var scope = app.ApplicationServices.CreateScope(); - var options = scope.ServiceProvider.GetRequiredService(); - - return app; - } - - private static ISampler GetSampler(JaegerSettings options) - { - switch (options.Sampler) - { - case "const": return new ConstSampler(true); - case "rate": return new RateLimitingSampler(options.MaxTracesPerSecond); - case "probabilistic": return new ProbabilisticSampler(options.SamplingRate); - default: return new ConstSampler(true); - } - } -} \ No newline at end of file diff --git a/src/Genocs.Tracing/Jaeger/Options/IJaegerOptionsBuilder.cs b/src/Genocs.Tracing/Jaeger/Options/IJaegerOptionsBuilder.cs deleted file mode 100644 index a1792dc6..00000000 --- a/src/Genocs.Tracing/Jaeger/Options/IJaegerOptionsBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Genocs.Tracing.Jaeger.Options; - -public interface IJaegerOptionsBuilder -{ - IJaegerOptionsBuilder Enable(bool enabled); - IJaegerOptionsBuilder WithServiceName(string serviceName); - IJaegerOptionsBuilder WithUdpHost(string udpHost); - IJaegerOptionsBuilder WithUdpPort(int udpPort); - IJaegerOptionsBuilder WithMaxPacketSize(int maxPacketSize); - IJaegerOptionsBuilder WithSampler(string sampler); - IJaegerOptionsBuilder WithMaxTracesPerSecond(double maxTracesPerSecond); - IJaegerOptionsBuilder WithSamplingRate(double samplingRate); - JaegerSettings Build(); -} \ No newline at end of file diff --git a/src/Genocs.Tracing/Jaeger/Options/JaegerSettings.cs b/src/Genocs.Tracing/Jaeger/Options/JaegerSettings.cs deleted file mode 100644 index 94ef5b0b..00000000 --- a/src/Genocs.Tracing/Jaeger/Options/JaegerSettings.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace Genocs.Tracing.Jaeger.Options; - -/// -/// Jaeger Settings -/// -public class JaegerSettings -{ - /// - /// Default section name - /// - public const string Position = "jaeger"; - - /// - /// - /// - public bool Enabled { get; set; } - public string? ServiceName { get; set; } - public string? UdpHost { get; set; } - public int UdpPort { get; set; } - public int MaxPacketSize { get; set; } = 64967; - public string? Sampler { get; set; } - public double MaxTracesPerSecond { get; set; } = 5; - public double SamplingRate { get; set; } = 0.2; - public IEnumerable? ExcludePaths { get; set; } - public string? Sender { get; set; } - public HttpSenderSettings? HttpSender { get; set; } - - public class HttpSenderSettings - { - public string? Endpoint { get; set; } - public string? AuthToken { get; set; } - public string? Username { get; set; } - public string? Password { get; set; } - public string? UserAgent { get; set; } - public int MaxPacketSize { get; set; } = 1048576; - } -} \ No newline at end of file diff --git a/src/Genocs.Tracing/Jaeger/RabbitMQ/Extensions.cs b/src/Genocs.Tracing/Jaeger/RabbitMQ/Extensions.cs deleted file mode 100644 index 2b53101a..00000000 --- a/src/Genocs.Tracing/Jaeger/RabbitMQ/Extensions.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Genocs.MessageBrokers.RabbitMQ; -using Genocs.Tracing.Jaeger.RabbitMQ.Plugins; - -namespace Genocs.Tracing.Jaeger.RabbitMQ; - -public static class Extensions -{ - public static IRabbitMqPluginsRegistry AddJaegerRabbitMqPlugin(this IRabbitMqPluginsRegistry registry) - { - registry.Add(); - return registry; - } -} \ No newline at end of file diff --git a/src/Genocs.Tracing/Jaeger/RabbitMQ/Plugins/JaegerPlugin.cs b/src/Genocs.Tracing/Jaeger/RabbitMQ/Plugins/JaegerPlugin.cs deleted file mode 100644 index b4504082..00000000 --- a/src/Genocs.Tracing/Jaeger/RabbitMQ/Plugins/JaegerPlugin.cs +++ /dev/null @@ -1,68 +0,0 @@ -using Genocs.Core.Extensions; -using Genocs.MessageBrokers.RabbitMQ; -using Jaeger; -using OpenTracing; -using OpenTracing.Tag; -using RabbitMQ.Client.Events; -using System.Text; - -namespace Genocs.Tracing.Jaeger.RabbitMQ.Plugins; - -internal sealed class JaegerPlugin : RabbitMQPlugin -{ - private readonly ITracer _tracer; - private readonly string _spanContextHeader; - - public JaegerPlugin(ITracer tracer, RabbitMQOptions options) - { - _tracer = tracer; - _spanContextHeader = options.GetSpanContextHeader(); - } - - public override async Task HandleAsync(object message, object correlationContext, - BasicDeliverEventArgs args) - { - var messageName = message.GetType().Name.Underscore(); - var messageId = args.BasicProperties.MessageId; - var spanContext = string.Empty; - if (args.BasicProperties.Headers is { } && - args.BasicProperties.Headers.TryGetValue(_spanContextHeader, out var spanContextHeader) && - spanContextHeader is byte[] spanContextBytes) - { - spanContext = Encoding.UTF8.GetString(spanContextBytes); - } - - using var scope = BuildScope(messageName, spanContext); - var span = scope.Span; - span.Log($"Started processing a message: '{messageName}' [id: '{messageId}']."); - try - { - await Next(message, correlationContext, args); - } - catch (Exception ex) - { - span.SetTag(Tags.Error, true); - span.Log(ex.Message); - } - - span.Log($"Finished processing a message: '{messageName}' [id: '{messageId}']."); - } - - private IScope BuildScope(string messageName, string serializedSpanContext) - { - var spanBuilder = _tracer - .BuildSpan($"processing-{messageName}") - .WithTag("message-type", messageName); - - if (string.IsNullOrEmpty(serializedSpanContext)) - { - return spanBuilder.StartActive(true); - } - - var spanContext = SpanContext.ContextFromString(serializedSpanContext); - - return spanBuilder - .AddReference(References.FollowsFrom, spanContext) - .StartActive(true); - } -} \ No newline at end of file diff --git a/src/Genocs.Tracing/Jaeger/Tracers/GenocsDefaultTracer.cs b/src/Genocs.Tracing/Jaeger/Tracers/GenocsDefaultTracer.cs deleted file mode 100644 index a257f619..00000000 --- a/src/Genocs.Tracing/Jaeger/Tracers/GenocsDefaultTracer.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Jaeger; -using Jaeger.Reporters; -using Jaeger.Samplers; -using OpenTracing; -using System.Reflection; - -namespace Genocs.Tracing.Jaeger.Tracers; - -internal sealed class GenocsDefaultTracer -{ - public static ITracer Create() - => new Tracer.Builder(Assembly.GetEntryAssembly().FullName) - .WithReporter(new NoopReporter()) - .WithSampler(new ConstSampler(false)) - .Build(); -} \ No newline at end of file diff --git a/src/Genocs.Tracing/README.md b/src/Genocs.Tracing/README.md deleted file mode 100644 index c96381ac..00000000 --- a/src/Genocs.Tracing/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# .NET Core Base library - -This package contains a set of base functionalities designed by Genocs. -The libraries are built using .NET standard 2.1. - - -## Description - -Core NuGet package contains general purpose functionalities to be used on DDD services. - - -## Support - -Please check the GitHub repository getting more info. - - -## Release notes - -### [2023-03-12] 5.0.0-preview.5.0 -- Implemented MongoDB repository interfaces - -### [2023-03-12] 5.0.0 -- New Architecture - -### [2023-03-12] 3.1.0 -- Added Builders - -### [2023-03-12] 3.0.0 -- Refactory to implement CQRS pattern - -### [2023-03-04] 2.4.1 -- Updated System.Text.Json diff --git a/src/Genocs.Tracing/README_NUGET.md b/src/Genocs.Tracing/README_NUGET.md new file mode 100644 index 00000000..cf06bc9b --- /dev/null +++ b/src/Genocs.Tracing/README_NUGET.md @@ -0,0 +1,60 @@ +# .NET Core Base library + +This package contains a set of base functionalities designed by Genocs. +The libraries are built using .NET standard 2.1. + + +## Description + +Core NuGet package contains general purpose functionalities to be used on DDD services. + + +## Support + +Please check the GitHub repository getting more info. + + +## Release notes + +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 +- Implemented MongoDB repository interfaces + +### [2023-03-12] 5.0.0 +- New Architecture + +### [2023-03-12] 3.1.0 +- Added Builders + +### [2023-03-12] 3.0.0 +- Refactory to implement CQRS pattern + +### [2023-03-04] 2.4.1 +- Updated System.Text.Json diff --git a/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs index 52a4864a..96f572d6 100644 --- a/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs +++ b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs @@ -15,20 +15,28 @@ public DispatcherEndpointsBuilder(IEndpointsBuilder builder) _builder = builder; } - public IDispatcherEndpointsBuilder Get(string path, Func? context = null, - Action? endpoint = null, bool auth = false, string? roles = null, - params string[] policies) + public IDispatcherEndpointsBuilder Get( + string path, + Func? context = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) { _builder.Get(path, context, endpoint, auth, roles, policies); return this; } - public IDispatcherEndpointsBuilder Get(string path, - Func? beforeDispatch = null, - Func? afterDispatch = null, - Action? endpoint = null, bool auth = false, string? roles = null, - params string[] policies) where TQuery : class, IQuery + public IDispatcherEndpointsBuilder Get( + string path, + Func? beforeDispatch = null, + Func? afterDispatch = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) + where TQuery : class, IQuery { _builder.Get(path, async (query, ctx) => { diff --git a/src/Genocs.WebApi.CQRS/Extensions.cs b/src/Genocs.WebApi.CQRS/Extensions.cs index 292ec808..83fb7946 100644 --- a/src/Genocs.WebApi.CQRS/Extensions.cs +++ b/src/Genocs.WebApi.CQRS/Extensions.cs @@ -19,9 +19,11 @@ public static IGenocsBuilder AddInMemoryDispatcher(this IGenocsBuilder builder) return builder; } - public static IApplicationBuilder UseDispatcherEndpoints(this IApplicationBuilder app, - Action builder, bool useAuthorization = true, - Action middleware = null) + public static IApplicationBuilder UseDispatcherEndpoints( + this IApplicationBuilder app, + Action builder, + bool useAuthorization = true, + Action? middleware = null) { var definitions = app.ApplicationServices.GetRequiredService(); app.UseRouting(); diff --git a/src/Genocs.WebApi.CQRS/Genocs.WebApi.CQRS.csproj b/src/Genocs.WebApi.CQRS/Genocs.WebApi.CQRS.csproj index a808b4db..f43a5c4c 100644 --- a/src/Genocs.WebApi.CQRS/Genocs.WebApi.CQRS.csproj +++ b/src/Genocs.WebApi.CQRS/Genocs.WebApi.CQRS.csproj @@ -1,57 +1,30 @@  - - net6.0;net7.0 - enable - enable - Genocs.WebApi.CQRS - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The web api CORS library - The web api CORS library - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.WebApi.CQRS + Genocs.WebApi.CQRS + Genocs.WebApi.CQRS + The web api CORS library + The web api CORS library + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + - - - True - \ - - - True - \ - - - True - \ - - + + + + - - - - - - - - - - - + + + + diff --git a/src/Genocs.WebApi.CQRS/IDispatcher.cs b/src/Genocs.WebApi.CQRS/IDispatcher.cs index d7c817de..032bc337 100644 --- a/src/Genocs.WebApi.CQRS/IDispatcher.cs +++ b/src/Genocs.WebApi.CQRS/IDispatcher.cs @@ -6,7 +6,11 @@ namespace Genocs.WebApi.CQRS; public interface IDispatcher { - Task SendAsync(T command, CancellationToken cancellationToken = default) where T : class, ICommand; - Task PublishAsync(T @event, CancellationToken cancellationToken = default) where T : class, IEvent; - Task QueryAsync(IQuery query, CancellationToken cancellationToken = default); + Task SendAsync(T command, CancellationToken cancellationToken = default) + where T : class, ICommand; + + Task PublishAsync(T @event, CancellationToken cancellationToken = default) + where T : class, IEvent; + + Task QueryAsync(IQuery query, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/Genocs.WebApi.CQRS/IDispatcherEndpointsBuilder.cs b/src/Genocs.WebApi.CQRS/IDispatcherEndpointsBuilder.cs index a048339f..67de3369 100644 --- a/src/Genocs.WebApi.CQRS/IDispatcherEndpointsBuilder.cs +++ b/src/Genocs.WebApi.CQRS/IDispatcherEndpointsBuilder.cs @@ -7,43 +7,75 @@ namespace Genocs.WebApi.CQRS; public interface IDispatcherEndpointsBuilder { - IDispatcherEndpointsBuilder Get(string path, Func? context = null, - Action? endpoint = null, bool auth = false, string? roles = null, - params string[] policies); - - IDispatcherEndpointsBuilder Get(string path, - Func? beforeDispatch = null, - Func? afterDispatch = null, - Action? endpoint = null, bool auth = false, string? roles = null, - params string[] policies) where TQuery : class, IQuery; - - IDispatcherEndpointsBuilder Post(string path, Func? context = null, - Action? endpoint = null, bool auth = false, string? roles = null, - params string[] policies); - - IDispatcherEndpointsBuilder Post(string path, Func? beforeDispatch = null, - Func? afterDispatch = null, Action? endpoint = null, - bool auth = false, string? roles = null, - params string[] policies) + IDispatcherEndpointsBuilder Get( + string path, + Func? context = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies); + + IDispatcherEndpointsBuilder Get( + string path, + Func? beforeDispatch = null, + Func? afterDispatch = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) + where TQuery : class, IQuery; + + IDispatcherEndpointsBuilder Post( + string path, + Func? context = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies); + + IDispatcherEndpointsBuilder Post( + string path, + Func? beforeDispatch = null, + Func? afterDispatch = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) where T : class, ICommand; - IDispatcherEndpointsBuilder Put(string path, Func? context = null, - Action? endpoint = null, bool auth = false, string? roles = null, - params string[] policies); + IDispatcherEndpointsBuilder Put( + string path, + Func? context = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies); - IDispatcherEndpointsBuilder Put(string path, Func? beforeDispatch = null, - Func? afterDispatch = null, Action? endpoint = null, - bool auth = false, string? roles = null, - params string[] policies) + IDispatcherEndpointsBuilder Put( + string path, + Func? beforeDispatch = null, + Func? afterDispatch = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) where T : class, ICommand; - IDispatcherEndpointsBuilder Delete(string path, Func? context = null, - Action? endpoint = null, bool auth = false, string? roles = null, - params string[] policies); + IDispatcherEndpointsBuilder Delete( + string path, + Func? context = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies); - IDispatcherEndpointsBuilder Delete(string path, Func? beforeDispatch = null, - Func? afterDispatch = null, Action? endpoint = null, - bool auth = false, string? roles = null, - params string[] policies) + IDispatcherEndpointsBuilder Delete( + string path, + Func? beforeDispatch = null, + Func? afterDispatch = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) where T : class, ICommand; } \ No newline at end of file diff --git a/src/Genocs.WebApi.CQRS/InMemoryDispatcher.cs b/src/Genocs.WebApi.CQRS/InMemoryDispatcher.cs index 17a2ed47..0499d41d 100644 --- a/src/Genocs.WebApi.CQRS/InMemoryDispatcher.cs +++ b/src/Genocs.WebApi.CQRS/InMemoryDispatcher.cs @@ -10,20 +10,24 @@ public class InMemoryDispatcher : IDispatcher private readonly IEventDispatcher _eventDispatcher; private readonly IQueryDispatcher _queryDispatcher; - public InMemoryDispatcher(ICommandDispatcher commandDispatcher, IEventDispatcher eventDispatcher, - IQueryDispatcher queryDispatcher) + public InMemoryDispatcher( + ICommandDispatcher commandDispatcher, + IEventDispatcher eventDispatcher, + IQueryDispatcher queryDispatcher) { _commandDispatcher = commandDispatcher; _eventDispatcher = eventDispatcher; _queryDispatcher = queryDispatcher; } - public Task SendAsync(T command, CancellationToken cancellationToken = default) where T : class, ICommand + public Task SendAsync(T command, CancellationToken cancellationToken = default) + where T : class, ICommand => _commandDispatcher.SendAsync(command, cancellationToken); - public Task PublishAsync(T @event, CancellationToken cancellationToken = default) where T : class, IEvent + public Task PublishAsync(T @event, CancellationToken cancellationToken = default) + where T : class, IEvent => _eventDispatcher.PublishAsync(@event, cancellationToken); - public Task QueryAsync(IQuery query, CancellationToken cancellationToken = default) + public Task QueryAsync(IQuery query, CancellationToken cancellationToken = default) => _queryDispatcher.QueryAsync(query, cancellationToken); } \ No newline at end of file diff --git a/src/Genocs.WebApi.CQRS/Middlewares/PublicContractsMiddleware.cs b/src/Genocs.WebApi.CQRS/Middlewares/PublicContractsMiddleware.cs index e7a2a25a..01caf803 100644 --- a/src/Genocs.WebApi.CQRS/Middlewares/PublicContractsMiddleware.cs +++ b/src/Genocs.WebApi.CQRS/Middlewares/PublicContractsMiddleware.cs @@ -27,8 +27,7 @@ public class PublicContractsMiddleware private static int _initialized; private static string _serializedContracts = "{}"; - public PublicContractsMiddleware(RequestDelegate next, string endpoint, Type attributeType, - bool attributeRequired) + public PublicContractsMiddleware(RequestDelegate next, string endpoint, Type attributeType, bool attributeRequired) { _next = next; _endpoint = endpoint; @@ -68,29 +67,36 @@ private void Load(Type attributeType) foreach (var command in contracts.Where(t => typeof(ICommand).IsAssignableFrom(t))) { - var instance = command.GetDefaultInstance(); - var name = instance.GetType().Name; + object? instance = command.GetDefaultInstance(); + string? name = instance?.GetType().Name; - if (Contracts.Commands.ContainsKey(name)) + if (!string.IsNullOrWhiteSpace(name) && instance != null) { - throw new InvalidOperationException($"Command: '{name}' already exists."); + if (Contracts.Commands.ContainsKey(name)) + { + throw new InvalidOperationException($"Command: '{name}' already exists."); + } + + Contracts.Commands[name] = instance; } - Contracts.Commands[name] = instance; } foreach (var @event in contracts.Where(t => typeof(IEvent).IsAssignableFrom(t) && t != typeof(RejectedEvent))) { - var instance = @event.GetDefaultInstance(); - var name = instance.GetType().Name; + object? instance = @event.GetDefaultInstance(); + string? name = instance?.GetType().Name; - if (Contracts.Events.ContainsKey(name)) + if (!string.IsNullOrWhiteSpace(name) && instance != null) { - throw new InvalidOperationException($"Event: '{name}' already exists."); - } + if (Contracts.Events.ContainsKey(name)) + { + throw new InvalidOperationException($"Event: '{name}' already exists."); + } - Contracts.Events[name] = instance; + Contracts.Events[name] = instance; + } } _serializedContracts = JsonSerializer.Serialize(Contracts, SerializerOptions); diff --git a/src/Genocs.WebApi.CQRS/README.md b/src/Genocs.WebApi.CQRS/README.md deleted file mode 100644 index c96381ac..00000000 --- a/src/Genocs.WebApi.CQRS/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# .NET Core Base library - -This package contains a set of base functionalities designed by Genocs. -The libraries are built using .NET standard 2.1. - - -## Description - -Core NuGet package contains general purpose functionalities to be used on DDD services. - - -## Support - -Please check the GitHub repository getting more info. - - -## Release notes - -### [2023-03-12] 5.0.0-preview.5.0 -- Implemented MongoDB repository interfaces - -### [2023-03-12] 5.0.0 -- New Architecture - -### [2023-03-12] 3.1.0 -- Added Builders - -### [2023-03-12] 3.0.0 -- Refactory to implement CQRS pattern - -### [2023-03-04] 2.4.1 -- Updated System.Text.Json diff --git a/src/Genocs.WebApi.CQRS/README_NUGET.md b/src/Genocs.WebApi.CQRS/README_NUGET.md new file mode 100644 index 00000000..cf06bc9b --- /dev/null +++ b/src/Genocs.WebApi.CQRS/README_NUGET.md @@ -0,0 +1,60 @@ +# .NET Core Base library + +This package contains a set of base functionalities designed by Genocs. +The libraries are built using .NET standard 2.1. + + +## Description + +Core NuGet package contains general purpose functionalities to be used on DDD services. + + +## Support + +Please check the GitHub repository getting more info. + + +## Release notes + +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 +- Implemented MongoDB repository interfaces + +### [2023-03-12] 5.0.0 +- New Architecture + +### [2023-03-12] 3.1.0 +- Added Builders + +### [2023-03-12] 3.0.0 +- Refactory to implement CQRS pattern + +### [2023-03-04] 2.4.1 +- Updated System.Text.Json diff --git a/src/Genocs.WebApi.Security/CertificateMiddleware.cs b/src/Genocs.WebApi.Security/CertificateMiddleware.cs index 42c07ed7..aefdeb61 100644 --- a/src/Genocs.WebApi.Security/CertificateMiddleware.cs +++ b/src/Genocs.WebApi.Security/CertificateMiddleware.cs @@ -1,10 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography.X509Certificates; -using System.Threading.Tasks; +using Genocs.WebApi.Security.Configurations; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; +using System.Security.Cryptography.X509Certificates; namespace Genocs.WebApi.Security; @@ -14,48 +11,65 @@ internal sealed class CertificateMiddleware : IMiddleware private readonly ILogger _logger; private readonly SecurityOptions.CertificateOptions _options; private readonly HashSet _allowedHosts; - private readonly IDictionary _acl; + private readonly IDictionary _acl = new Dictionary(); private readonly IDictionary _subjects = new Dictionary(); private readonly bool _validateAcl; private readonly bool _skipRevocationCheck; - public CertificateMiddleware(ICertificatePermissionValidator certificatePermissionValidator, - SecurityOptions options, ILogger logger) + public CertificateMiddleware( + ICertificatePermissionValidator certificatePermissionValidator, + SecurityOptions options, + ILogger logger) { _certificatePermissionValidator = certificatePermissionValidator; _logger = logger; + + if (options.Certificate is null) + { + throw new ArgumentNullException(nameof(options.Certificate)); + } + _options = options.Certificate; + _allowedHosts = new HashSet(_options.AllowedHosts ?? Array.Empty()); _validateAcl = _options.Acl is not null && _options.Acl.Any(); _skipRevocationCheck = options.Certificate.SkipRevocationCheck; + if (!_validateAcl) { return; } - _acl = new Dictionary(); - foreach (var (key, acl) in _options.Acl) + if (_options.Acl != null) { - if (!string.IsNullOrWhiteSpace(acl.ValidIssuer) && !acl.ValidIssuer.StartsWith("CN=")) + foreach (var (key, acl) in _options.Acl) { - acl.ValidIssuer = $"CN={acl.ValidIssuer}"; - } + if (!string.IsNullOrWhiteSpace(acl.ValidIssuer) && !acl.ValidIssuer.StartsWith("CN=")) + { + acl.ValidIssuer = $"CN={acl.ValidIssuer}"; + } - var subject = key.StartsWith("CN=") ? key : $"CN={key}"; - if (_options.AllowSubdomains) - { - foreach (var domain in options.Certificate.AllowedDomains ?? Enumerable.Empty()) + string subject = key.StartsWith("CN=") ? key : $"CN={key}"; + if (_options.AllowSubdomains) { - _subjects.Add($"{subject}.{domain}", key); + foreach (string domain in options.Certificate.AllowedDomains ?? Enumerable.Empty()) + { + _subjects.Add($"{subject}.{domain}", key); + } } - } - _acl.Add(_subjects.Any() ? key : subject, acl); + _acl.Add(_subjects.Any() ? key : subject, acl); + } } } public Task InvokeAsync(HttpContext context, RequestDelegate next) { + if (_options is null) + { + return next(context); + } + if (!_options.Enabled) { return next(context); @@ -79,7 +93,7 @@ public Task InvokeAsync(HttpContext context, RequestDelegate next) } SecurityOptions.CertificateOptions.AclOptions acl; - if (_subjects.TryGetValue(certificate.Subject, out var subject)) + if (_subjects.TryGetValue(certificate.Subject, out string? subject)) { if (!_acl.TryGetValue(subject, out var existingAcl)) { @@ -141,17 +155,19 @@ private bool Verify(X509Certificate2 certificate) RevocationMode = _skipRevocationCheck ? X509RevocationMode.NoCheck : X509RevocationMode.Online, } }; - var chainBuilt = chain.Build(certificate); + + bool chainBuilt = chain.Build(certificate); + foreach (var chainElement in chain.ChainElements) { chainElement.Certificate.Dispose(); } - + if (chainBuilt) { return true; } - + _logger.LogError("Certificate validation failed"); foreach (var chainStatus in chain.ChainStatus) { @@ -163,7 +179,7 @@ private bool Verify(X509Certificate2 certificate) private bool IsAllowedHost(HttpContext context) { - var host = context.Request.Host.Host; + string host = context.Request.Host.Host; if (_allowedHosts.Contains(host)) { return true; diff --git a/src/Genocs.WebApi.Security/Configurations/SecurityOptions.cs b/src/Genocs.WebApi.Security/Configurations/SecurityOptions.cs new file mode 100644 index 00000000..54ef2aa2 --- /dev/null +++ b/src/Genocs.WebApi.Security/Configurations/SecurityOptions.cs @@ -0,0 +1,28 @@ +namespace Genocs.WebApi.Security.Configurations; + +public class SecurityOptions +{ + public CertificateOptions? Certificate { get; set; } + + public class CertificateOptions + { + public bool Enabled { get; set; } + public string? Header { get; set; } + public bool AllowSubdomains { get; set; } + public IEnumerable? AllowedDomains { get; set; } + public IEnumerable? AllowedHosts { get; set; } + public IDictionary? Acl { get; set; } + public bool SkipRevocationCheck { get; set; } + + public string GetHeaderName() + => string.IsNullOrWhiteSpace(Header) ? "Certificate" : Header; + + public class AclOptions + { + public string? ValidIssuer { get; set; } + public string? ValidThumbprint { get; set; } + public string? ValidSerialNumber { get; set; } + public IEnumerable? Permissions { get; set; } + } + } +} \ No newline at end of file diff --git a/src/Genocs.WebApi.Security/DefaultCertificatePermissionValidator.cs b/src/Genocs.WebApi.Security/DefaultCertificatePermissionValidator.cs index f5deed44..753271d7 100644 --- a/src/Genocs.WebApi.Security/DefaultCertificatePermissionValidator.cs +++ b/src/Genocs.WebApi.Security/DefaultCertificatePermissionValidator.cs @@ -1,6 +1,5 @@ -using System.Collections.Generic; -using System.Security.Cryptography.X509Certificates; using Microsoft.AspNetCore.Http; +using System.Security.Cryptography.X509Certificates; namespace Genocs.WebApi.Security; diff --git a/src/Genocs.WebApi.Security/Extensions.cs b/src/Genocs.WebApi.Security/Extensions.cs index ed05cd0d..d06d2b29 100644 --- a/src/Genocs.WebApi.Security/Extensions.cs +++ b/src/Genocs.WebApi.Security/Extensions.cs @@ -1,4 +1,5 @@ using Genocs.Core.Builders; +using Genocs.WebApi.Security.Configurations; using Microsoft.AspNetCore.Authentication.Certificate; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; @@ -11,9 +12,17 @@ public static class Extensions private const string SectionName = "security"; private const string RegistryName = "security"; - public static IGenocsBuilder AddCertificateAuthentication(this IGenocsBuilder builder, - string sectionName = SectionName, Type permissionValidatorType = null) + public static IGenocsBuilder AddCertificateAuthentication( + this IGenocsBuilder builder, + string sectionName = SectionName, + Type? permissionValidatorType = null) { + + if (string.IsNullOrWhiteSpace(sectionName)) + { + sectionName = SectionName; + } + var options = builder.GetOptions(sectionName); builder.Services.AddSingleton(options); if (!builder.TryRegister(RegistryName)) @@ -34,10 +43,12 @@ public static IGenocsBuilder AddCertificateAuthentication(this IGenocsBuilder bu { builder.Services.AddSingleton(); } - + builder.Services.AddSingleton(); - builder.Services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme) + builder.Services + .AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme) .AddCertificate(); + builder.Services.AddCertificateForwarding(c => { c.CertificateHeader = options.Certificate.GetHeaderName(); @@ -66,9 +77,10 @@ public static IApplicationBuilder UseCertificateAuthentication(this IApplication private static byte[] StringToByteArray(string hex) { - var numberChars = hex.Length; - var bytes = new byte[numberChars / 2]; - for (var i = 0; i < numberChars; i += 2) + int numberChars = hex.Length; + byte[] bytes = new byte[numberChars / 2]; + + for (int i = 0; i < numberChars; i += 2) { bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); } diff --git a/src/Genocs.WebApi.Security/Genocs.WebApi.Security.csproj b/src/Genocs.WebApi.Security/Genocs.WebApi.Security.csproj index 12a3702e..e5767a51 100644 --- a/src/Genocs.WebApi.Security/Genocs.WebApi.Security.csproj +++ b/src/Genocs.WebApi.Security/Genocs.WebApi.Security.csproj @@ -1,61 +1,40 @@  - - net6.0;net7.0 - enable - enable - Genocs.WebApi.Security - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The web api Security library - The web api Security library - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.WebApi.Security + Genocs.WebApi.Security + Genocs.WebApi.Security + The web api Security library + The web api Security library + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + - - - True - \ - - - True - \ - - - True - \ - - + + + - - - + + + - - - + + + - - - + + + + + + + - - - - diff --git a/src/Genocs.WebApi.Security/ICertificatePermissionValidator.cs b/src/Genocs.WebApi.Security/ICertificatePermissionValidator.cs index 578a6b80..8e0ccf12 100644 --- a/src/Genocs.WebApi.Security/ICertificatePermissionValidator.cs +++ b/src/Genocs.WebApi.Security/ICertificatePermissionValidator.cs @@ -1,6 +1,5 @@ -using System.Collections.Generic; -using System.Security.Cryptography.X509Certificates; using Microsoft.AspNetCore.Http; +using System.Security.Cryptography.X509Certificates; namespace Genocs.WebApi.Security; diff --git a/src/Genocs.WebApi.Security/README.md b/src/Genocs.WebApi.Security/README.md deleted file mode 100644 index c96381ac..00000000 --- a/src/Genocs.WebApi.Security/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# .NET Core Base library - -This package contains a set of base functionalities designed by Genocs. -The libraries are built using .NET standard 2.1. - - -## Description - -Core NuGet package contains general purpose functionalities to be used on DDD services. - - -## Support - -Please check the GitHub repository getting more info. - - -## Release notes - -### [2023-03-12] 5.0.0-preview.5.0 -- Implemented MongoDB repository interfaces - -### [2023-03-12] 5.0.0 -- New Architecture - -### [2023-03-12] 3.1.0 -- Added Builders - -### [2023-03-12] 3.0.0 -- Refactory to implement CQRS pattern - -### [2023-03-04] 2.4.1 -- Updated System.Text.Json diff --git a/src/Genocs.WebApi.Security/README_NUGET.md b/src/Genocs.WebApi.Security/README_NUGET.md new file mode 100644 index 00000000..cf06bc9b --- /dev/null +++ b/src/Genocs.WebApi.Security/README_NUGET.md @@ -0,0 +1,60 @@ +# .NET Core Base library + +This package contains a set of base functionalities designed by Genocs. +The libraries are built using .NET standard 2.1. + + +## Description + +Core NuGet package contains general purpose functionalities to be used on DDD services. + + +## Support + +Please check the GitHub repository getting more info. + + +## Release notes + +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 +- Implemented MongoDB repository interfaces + +### [2023-03-12] 5.0.0 +- New Architecture + +### [2023-03-12] 3.1.0 +- Added Builders + +### [2023-03-12] 3.0.0 +- Refactory to implement CQRS pattern + +### [2023-03-04] 2.4.1 +- Updated System.Text.Json diff --git a/src/Genocs.WebApi.Security/SecurityOptions.cs b/src/Genocs.WebApi.Security/SecurityOptions.cs deleted file mode 100644 index 9a106639..00000000 --- a/src/Genocs.WebApi.Security/SecurityOptions.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Generic; - -namespace Genocs.WebApi.Security; - -public class SecurityOptions -{ - public CertificateOptions Certificate { get; set; } - - public class CertificateOptions - { - public bool Enabled { get; set; } - public string Header { get; set; } - public bool AllowSubdomains { get; set; } - public IEnumerable AllowedDomains { get; set; } - public IEnumerable AllowedHosts { get; set; } - public IDictionary Acl { get; set; } - public bool SkipRevocationCheck { get; set; } - - public string GetHeaderName() => string.IsNullOrWhiteSpace(Header) ? "Certificate" : Header; - - public class AclOptions - { - public string ValidIssuer { get; set; } - public string ValidThumbprint { get; set; } - public string ValidSerialNumber { get; set; } - public IEnumerable Permissions { get; set; } - } - } -} \ No newline at end of file diff --git a/src/Genocs.WebApi.Swagger/Docs/Builders/SwaggerOptionsBuilder.cs b/src/Genocs.WebApi.Swagger/Builders/SwaggerOptionsBuilder.cs similarity index 79% rename from src/Genocs.WebApi.Swagger/Docs/Builders/SwaggerOptionsBuilder.cs rename to src/Genocs.WebApi.Swagger/Builders/SwaggerOptionsBuilder.cs index 90e21984..3f782a5e 100644 --- a/src/Genocs.WebApi.Swagger/Docs/Builders/SwaggerOptionsBuilder.cs +++ b/src/Genocs.WebApi.Swagger/Builders/SwaggerOptionsBuilder.cs @@ -1,3 +1,5 @@ +using Genocs.WebApi.Swagger.Docs.Configurations; + namespace Genocs.WebApi.Swagger.Docs.Builders; internal sealed class SwaggerOptionsBuilder : ISwaggerOptionsBuilder @@ -46,6 +48,18 @@ public ISwaggerOptionsBuilder IncludeSecurity(bool includeSecurity) return this; } + public ISwaggerOptionsBuilder WithDescription(string description) + { + _options.Description = description; + return this; + } + + public ISwaggerOptionsBuilder WithContactName(string contactName) + { + _options.ContactName = contactName; + return this; + } + public ISwaggerOptionsBuilder SerializeAsOpenApiV2(bool serializeAsOpenApiV2) { _options.SerializeAsOpenApiV2 = serializeAsOpenApiV2; @@ -53,4 +67,6 @@ public ISwaggerOptionsBuilder SerializeAsOpenApiV2(bool serializeAsOpenApiV2) } public SwaggerOptions Build() => _options; + + } \ No newline at end of file diff --git a/src/Genocs.WebApi.Swagger/Docs/ISwaggerOptionsBuilder.cs b/src/Genocs.WebApi.Swagger/Docs/Configurations/ISwaggerOptionsBuilder.cs similarity index 75% rename from src/Genocs.WebApi.Swagger/Docs/ISwaggerOptionsBuilder.cs rename to src/Genocs.WebApi.Swagger/Docs/Configurations/ISwaggerOptionsBuilder.cs index 1527d2a4..caddc6c4 100644 --- a/src/Genocs.WebApi.Swagger/Docs/ISwaggerOptionsBuilder.cs +++ b/src/Genocs.WebApi.Swagger/Docs/Configurations/ISwaggerOptionsBuilder.cs @@ -1,5 +1,4 @@ -namespace Genocs.WebApi.Swagger.Docs; - +namespace Genocs.WebApi.Swagger.Docs.Configurations; public interface ISwaggerOptionsBuilder { ISwaggerOptionsBuilder Enable(bool enabled); @@ -7,7 +6,9 @@ public interface ISwaggerOptionsBuilder ISwaggerOptionsBuilder WithName(string name); ISwaggerOptionsBuilder WithTitle(string title); ISwaggerOptionsBuilder WithVersion(string version); + ISwaggerOptionsBuilder WithDescription(string description); ISwaggerOptionsBuilder WithRoutePrefix(string routePrefix); + ISwaggerOptionsBuilder WithContactName(string contactName); ISwaggerOptionsBuilder IncludeSecurity(bool includeSecurity); ISwaggerOptionsBuilder SerializeAsOpenApiV2(bool serializeAsOpenApiV2); SwaggerOptions Build(); diff --git a/src/Genocs.WebApi.Swagger/Docs/Configurations/SwaggerOptions.cs b/src/Genocs.WebApi.Swagger/Docs/Configurations/SwaggerOptions.cs new file mode 100644 index 00000000..c700bdcd --- /dev/null +++ b/src/Genocs.WebApi.Swagger/Docs/Configurations/SwaggerOptions.cs @@ -0,0 +1,101 @@ +namespace Genocs.WebApi.Swagger.Docs.Configurations; + +public class SwaggerOptions +{ + /// + /// The flag to enable or disable Swagger. + /// + public bool Enabled { get; set; } + + /// + /// The flag to enable or disable ReDoc. + /// + public bool ReDocEnabled { get; set; } + + /// + /// The name of the API. + /// + public string? Name { get; set; } + + /// + /// The title of the API. You can use this field to set the title by using markdown. + /// + public string? Title { get; set; } + + /// + /// The version of the API. + /// + public string? Version { get; set; } + + /// + /// The description of the API. You can use this field to set the description by using markdown. + /// + public string? Description { get; set; } + + /// + /// The route prefix of the API. + /// + public string? RoutePrefix { get; set; } + + /// + /// The contact name of the API. + /// + public string? ContactName { get; set; } + + /// + /// The contact email of the API. + /// + public string? ContactEmail { get; set; } + + /// + /// The contact URL of the API. + /// + public string? ContactUrl { get; set; } + + /// + /// The license name of the API. + /// + public string? LicenseName { get; set; } + + /// + /// The license URL of the API. + /// + public string? LicenseUrl { get; set; } + + /// + /// The terms of service of the API. you can use this field to set the terms of service by using markdown or as url link. + /// + public string? TermsOfService { get; set; } + + /// + /// The flag to include security. By using this flag, you can include the security information in the Swagger document. + /// + public bool IncludeSecurity { get; set; } + + /// + /// The flag to serialize the Swagger document as OpenAPI version 2.0. + /// + public bool SerializeAsOpenApiV2 { get; set; } + + /// + /// List of servers that support this API. + /// + public List? Servers { get; set; } + + /// + /// Internal class to represent the server information in the Swagger document. + /// + public class OpenApiServer + { + + /// + /// The URL of the server. + /// + public string? Url { get; set; } + + /// + /// The description of the server. + /// + public string? Description { get; set; } + } +} \ No newline at end of file diff --git a/src/Genocs.WebApi.Swagger/Docs/Extensions.cs b/src/Genocs.WebApi.Swagger/Docs/Extensions.cs index f77b8564..aa3c37db 100644 --- a/src/Genocs.WebApi.Swagger/Docs/Extensions.cs +++ b/src/Genocs.WebApi.Swagger/Docs/Extensions.cs @@ -1,6 +1,9 @@ +using System.Reflection; using Genocs.Core.Builders; using Genocs.WebApi.Swagger.Docs.Builders; +using Genocs.WebApi.Swagger.Docs.Configurations; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; @@ -18,39 +21,126 @@ public static IGenocsBuilder AddSwaggerDocs(this IGenocsBuilder builder, string sectionName = SectionName; } - var options = builder.GetOptions(sectionName); - return builder.AddSwaggerDocs(options); + SwaggerOptions settings = builder.GetOptions(sectionName); + + if (settings is null) + { + return builder; + } + + return builder.AddSwaggerDocs(settings); } - public static IGenocsBuilder AddSwaggerDocs(this IGenocsBuilder builder, - Func buildOptions) + public static IGenocsBuilder AddSwaggerDocs(this IGenocsBuilder builder, Func buildOptions) { - var options = buildOptions(new SwaggerOptionsBuilder()).Build(); - return builder.AddSwaggerDocs(options); + SwaggerOptions settings = buildOptions(new SwaggerOptionsBuilder()).Build(); + + if (settings is null) + { + return builder; + } + + return builder.AddSwaggerDocs(settings); } - public static IGenocsBuilder AddSwaggerDocs(this IGenocsBuilder builder, SwaggerOptions options) + public static IGenocsBuilder AddSwaggerDocs(this IGenocsBuilder builder, SwaggerOptions settings) { - if (!options.Enabled || !builder.TryRegister(RegistryName)) + if (!settings.Enabled || !builder.TryRegister(RegistryName)) { return builder; } - builder.Services.AddSingleton(options); + // TODO: Double-check if this is necessary + builder.Services.AddSingleton(settings); + + // Register the Swagger generator, defining 1 or more Swagger documents builder.Services.AddSwaggerGen(c => { c.EnableAnnotations(); - c.SwaggerDoc(options.Name, new OpenApiInfo { Title = options.Title, Version = options.Version }); - if (options.IncludeSecurity) + + c.SwaggerDoc( + settings.Name, + new OpenApiInfo + { + Version = settings.Version, + Title = settings.Title, + Description = settings.Description, + TermsOfService = new Uri(settings.TermsOfService ?? "https://www.genocs.com/terms_and_conditions.html"), + Contact = new OpenApiContact + { + Name = settings.ContactName, + Email = settings.ContactEmail, + Url = new Uri(settings.ContactUrl ?? "https://www.genocs.com") + }, + License = new OpenApiLicense + { + Name = settings.LicenseName, + Url = new Uri(settings.LicenseUrl ?? "https://opensource.org/license/mit/") + } + }); + + if (settings.IncludeSecurity) { c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", Name = "Authorization", In = ParameterLocation.Header, - Type = SecuritySchemeType.ApiKey + Type = SecuritySchemeType.ApiKey, + Scheme = "Bearer" }); + + c.AddSecurityRequirement(new OpenApiSecurityRequirement() + { + { + new OpenApiSecurityScheme + { + Name = "Bearer", + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + }, + Scheme = "oauth2", + In = ParameterLocation.Header + }, + new List() + } + }); + } + + // This is required to make the custom operation ids work + // It's required to be used by LangChain tools + c.CustomOperationIds(oid => + { + if (oid.ActionDescriptor is not ControllerActionDescriptor actionDescriptor) + { + return null; // default behavior + } + + return oid.GroupName switch + { + "v1" => $"{actionDescriptor.ActionName}", + _ => $"_{actionDescriptor.ActionName}", // default behavior + }; + }); + + // Add list of servers + + if (settings.Servers != null) + { + foreach (var server in settings.Servers) + { + c.AddServer(new OpenApiServer() { Url = server.Url, Description = server.Description }); + } } + + // c.AddServer(new OpenApiServer() { Url = "http://localhost:5300", Description = "Local version to be used for development" }); + // c.AddServer(new OpenApiServer() { Url = "http://fiscanner-api", Description = "Containerized version to be used into with docker or k8s" }); + // c.AddServer(new OpenApiServer() { Url = "https://fiscanner-api.azurewebsites.net", Description = "Production deployed on Azure" }); + + string documentationFile = Path.Combine(AppContext.BaseDirectory, $"{Assembly.GetEntryAssembly()?.GetName().Name}.xml"); + c.IncludeXmlComments(documentationFile); }); return builder; @@ -64,7 +154,7 @@ public static IApplicationBuilder UseSwaggerDocs(this IApplicationBuilder builde return builder; } - var routePrefix = string.IsNullOrWhiteSpace(options.RoutePrefix) ? string.Empty : options.RoutePrefix; + string routePrefix = string.IsNullOrWhiteSpace(options.RoutePrefix) ? string.Empty : options.RoutePrefix; builder.UseStaticFiles() .UseSwagger(c => @@ -88,7 +178,7 @@ public static IApplicationBuilder UseSwaggerDocs(this IApplicationBuilder builde } /// - /// Replaces leading double forward slash caused by an empty route prefix + /// Replaces leading double forward slash caused by an empty route prefix. /// /// /// diff --git a/src/Genocs.WebApi.Swagger/Docs/SwaggerOptions.cs b/src/Genocs.WebApi.Swagger/Docs/SwaggerOptions.cs deleted file mode 100644 index 68a333aa..00000000 --- a/src/Genocs.WebApi.Swagger/Docs/SwaggerOptions.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Genocs.WebApi.Swagger.Docs; - -public class SwaggerOptions -{ - public bool Enabled { get; set; } - public bool ReDocEnabled { get; set; } - public string Name { get; set; } - public string Title { get; set; } - public string Version { get; set; } - public string RoutePrefix { get; set; } - public bool IncludeSecurity { get; set; } - public bool SerializeAsOpenApiV2 { get; set; } -} \ No newline at end of file diff --git a/src/Genocs.WebApi.Swagger/Extensions.cs b/src/Genocs.WebApi.Swagger/Extensions.cs index 6fa0f8f8..43e09e75 100644 --- a/src/Genocs.WebApi.Swagger/Extensions.cs +++ b/src/Genocs.WebApi.Swagger/Extensions.cs @@ -1,5 +1,6 @@ using Genocs.Core.Builders; using Genocs.WebApi.Swagger.Docs; +using Genocs.WebApi.Swagger.Docs.Configurations; using Genocs.WebApi.Swagger.Filters; using Microsoft.Extensions.DependencyInjection; @@ -19,8 +20,7 @@ public static IGenocsBuilder AddWebApiSwaggerDocs(this IGenocsBuilder builder, s return builder.AddWebApiSwaggerDocs(b => b.AddSwaggerDocs(sectionName)); } - public static IGenocsBuilder AddWebApiSwaggerDocs(this IGenocsBuilder builder, - Func buildOptions) + public static IGenocsBuilder AddWebApiSwaggerDocs(this IGenocsBuilder builder, Func buildOptions) => builder.AddWebApiSwaggerDocs(b => b.AddSwaggerDocs(buildOptions)); public static IGenocsBuilder AddWebApiSwaggerDocs(this IGenocsBuilder builder, SwaggerOptions options) diff --git a/src/Genocs.WebApi.Swagger/Filters/WebApiDocumentFilter.cs b/src/Genocs.WebApi.Swagger/Filters/WebApiDocumentFilter.cs index c088ae44..c6c4e570 100644 --- a/src/Genocs.WebApi.Swagger/Filters/WebApiDocumentFilter.cs +++ b/src/Genocs.WebApi.Swagger/Filters/WebApiDocumentFilter.cs @@ -7,11 +7,13 @@ namespace Genocs.WebApi.Swagger.Filters; internal sealed class WebApiDocumentFilter : IDocumentFilter { - private readonly WebApiEndpointDefinitions _definitions; private const string InBody = "body"; private const string InQuery = "query"; - private readonly Func _getOperation = (item, path) => + private readonly WebApiEndpointDefinitions _definitions; + + + private readonly Func _getOperation = (item, path) => { switch (path) { diff --git a/src/Genocs.WebApi.Swagger/Genocs.WebApi.Swagger.csproj b/src/Genocs.WebApi.Swagger/Genocs.WebApi.Swagger.csproj index 3e5ca529..c794b33e 100644 --- a/src/Genocs.WebApi.Swagger/Genocs.WebApi.Swagger.csproj +++ b/src/Genocs.WebApi.Swagger/Genocs.WebApi.Swagger.csproj @@ -1,61 +1,36 @@  - - net6.0 - enable - enable - Genocs.WebApi.Swagger - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The web api Swagger library - The web api Swagger library - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.WebApi.Swagger + Genocs.WebApi.Swagger + Genocs.WebApi.Swagger + The web api Swagger library + The web api Swagger library + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + - - - True - \ - - - True - \ - - - True - \ - - + + + - - - + + + - - - - - - - - - - - + + + + + + + diff --git a/src/Genocs.WebApi.Swagger/README.md b/src/Genocs.WebApi.Swagger/README.md deleted file mode 100644 index c96381ac..00000000 --- a/src/Genocs.WebApi.Swagger/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# .NET Core Base library - -This package contains a set of base functionalities designed by Genocs. -The libraries are built using .NET standard 2.1. - - -## Description - -Core NuGet package contains general purpose functionalities to be used on DDD services. - - -## Support - -Please check the GitHub repository getting more info. - - -## Release notes - -### [2023-03-12] 5.0.0-preview.5.0 -- Implemented MongoDB repository interfaces - -### [2023-03-12] 5.0.0 -- New Architecture - -### [2023-03-12] 3.1.0 -- Added Builders - -### [2023-03-12] 3.0.0 -- Refactory to implement CQRS pattern - -### [2023-03-04] 2.4.1 -- Updated System.Text.Json diff --git a/src/Genocs.WebApi.Swagger/README_NUGET.md b/src/Genocs.WebApi.Swagger/README_NUGET.md new file mode 100644 index 00000000..cf06bc9b --- /dev/null +++ b/src/Genocs.WebApi.Swagger/README_NUGET.md @@ -0,0 +1,60 @@ +# .NET Core Base library + +This package contains a set of base functionalities designed by Genocs. +The libraries are built using .NET standard 2.1. + + +## Description + +Core NuGet package contains general purpose functionalities to be used on DDD services. + + +## Support + +Please check the GitHub repository getting more info. + + +## Release notes + +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 +- Implemented MongoDB repository interfaces + +### [2023-03-12] 5.0.0 +- New Architecture + +### [2023-03-12] 3.1.0 +- Added Builders + +### [2023-03-12] 3.0.0 +- Refactory to implement CQRS pattern + +### [2023-03-04] 2.4.1 +- Updated System.Text.Json diff --git a/src/Genocs.WebApi/Configurations/WebApiConfigureOptions.cs b/src/Genocs.WebApi/Configurations/WebApiConfigureOptions.cs new file mode 100644 index 00000000..6b43b8e4 --- /dev/null +++ b/src/Genocs.WebApi/Configurations/WebApiConfigureOptions.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.Options; + +namespace Genocs.WebApi.Configurations; + +/// +/// The WebApiOptions definition. +/// +public class WebApiConfigureOptions : IConfigureNamedOptions +{ + private readonly WebApiOptions _options; + + public WebApiConfigureOptions(IOptions options) + { + _options = options.Value; + } + + public void Configure(string? name, WebApiOptions options) + { + Configure(options); + } + + public void Configure(WebApiOptions options) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/Genocs.WebApi/Configurations/WebApiOptions.cs b/src/Genocs.WebApi/Configurations/WebApiOptions.cs new file mode 100644 index 00000000..677c055e --- /dev/null +++ b/src/Genocs.WebApi/Configurations/WebApiOptions.cs @@ -0,0 +1,9 @@ +namespace Genocs.WebApi.Configurations; + +/// +/// The WebApiOptions definition. +/// +public class WebApiOptions +{ + public bool BindRequestFromRoute { get; set; } +} \ No newline at end of file diff --git a/src/Genocs.WebApi/EndpointsBuilder.cs b/src/Genocs.WebApi/EndpointsBuilder.cs index 0a55176d..efad9aa9 100644 --- a/src/Genocs.WebApi/EndpointsBuilder.cs +++ b/src/Genocs.WebApi/EndpointsBuilder.cs @@ -17,14 +17,15 @@ public EndpointsBuilder(IEndpointRouteBuilder routeBuilder, WebApiEndpointDefini _definitions = definitions; } - public IEndpointsBuilder Get(string path, - Func? context = null, - Action? endpoint = null, - bool auth = false, - string? roles = null, - params string[] policies) + public IEndpointsBuilder Get( + string path, + Func? context = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) { - var builder = _routeBuilder.MapGet(path, ctx => context.Invoke(ctx)); + var builder = _routeBuilder.MapGet(path, ctx => context?.Invoke(ctx)); endpoint?.Invoke(builder); ApplyAuthRolesAndPolicies(builder, auth, roles, policies); AddEndpointDefinition(HttpMethods.Get, path); @@ -46,7 +47,7 @@ public IEndpointsBuilder Get(string path, Func? context } public IEndpointsBuilder Get(string path, Func context = null, - Action endpoint = null, bool auth = false, string roles = null, + Action endpoint = null, bool auth = false, string? roles = null, params string[] policies) where TRequest : class { @@ -59,7 +60,7 @@ public IEndpointsBuilder Get(string path, Func context = null, - Action endpoint = null, bool auth = false, string roles = null, + Action endpoint = null, bool auth = false, string? roles = null, params string[] policies) { var builder = _routeBuilder.MapPost(path, ctx => context?.Invoke(ctx)); @@ -71,7 +72,7 @@ public IEndpointsBuilder Post(string path, Func context = nul } public IEndpointsBuilder Post(string path, Func context = null, - Action endpoint = null, bool auth = false, string roles = null, + Action endpoint = null, bool auth = false, string? roles = null, params string[] policies) where T : class { @@ -84,7 +85,7 @@ public IEndpointsBuilder Post(string path, Func context } public IEndpointsBuilder Put(string path, Func context = null, - Action endpoint = null, bool auth = false, string roles = null, + Action endpoint = null, bool auth = false, string? roles = null, params string[] policies) { var builder = _routeBuilder.MapPut(path, ctx => context?.Invoke(ctx)); @@ -96,7 +97,7 @@ public IEndpointsBuilder Put(string path, Func context = null } public IEndpointsBuilder Put(string path, Func context = null, - Action endpoint = null, bool auth = false, string roles = null, + Action endpoint = null, bool auth = false, string? roles = null, params string[] policies) where T : class { @@ -108,8 +109,8 @@ public IEndpointsBuilder Put(string path, Func context return this; } - public IEndpointsBuilder Delete(string path, Func context = null, - Action endpoint = null, bool auth = false, string roles = null, + public IEndpointsBuilder Delete(string path, Func? context = null, + Action endpoint = null, bool auth = false, string? roles = null, params string[] policies) { var builder = _routeBuilder.MapDelete(path, ctx => context?.Invoke(ctx)); @@ -120,8 +121,8 @@ public IEndpointsBuilder Delete(string path, Func context = n return this; } - public IEndpointsBuilder Delete(string path, Func context = null, - Action endpoint = null, bool auth = false, string roles = null, + public IEndpointsBuilder Delete(string path, Func? context = null, + Action endpoint = null, bool auth = false, string? roles = null, params string[] policies) where T : class { @@ -202,8 +203,8 @@ private void AddEndpointDefinition(string method, string path) private void AddEndpointDefinition(string method, string path) => AddEndpointDefinition(method, path, typeof(T), null); - private void AddEndpointDefinition(string method, string path) - => AddEndpointDefinition(method, path, typeof(T), typeof(U)); + private void AddEndpointDefinition(string method, string path) + => AddEndpointDefinition(method, path, typeof(Ta), typeof(Tu)); private void AddEndpointDefinition(string method, string path, Type input, Type? output) { diff --git a/src/Genocs.WebApi/Exceptions/ErrorHandlerMiddleware.cs b/src/Genocs.WebApi/Exceptions/ErrorHandlerMiddleware.cs index f2e23ee0..ba4e01e1 100644 --- a/src/Genocs.WebApi/Exceptions/ErrorHandlerMiddleware.cs +++ b/src/Genocs.WebApi/Exceptions/ErrorHandlerMiddleware.cs @@ -11,8 +11,10 @@ internal sealed class ErrorHandlerMiddleware : IMiddleware private readonly IJsonSerializer _jsonSerializer; private readonly ILogger _logger; - public ErrorHandlerMiddleware(IExceptionToResponseMapper exceptionToResponseMapper, - IJsonSerializer jsonSerializer, ILogger logger) + public ErrorHandlerMiddleware( + IExceptionToResponseMapper exceptionToResponseMapper, + IJsonSerializer jsonSerializer, + ILogger logger) { _exceptionToResponseMapper = exceptionToResponseMapper; _jsonSerializer = jsonSerializer; @@ -36,7 +38,7 @@ private async Task HandleErrorAsync(HttpContext context, Exception exception) { var exceptionResponse = _exceptionToResponseMapper.Map(exception); context.Response.StatusCode = (int)(exceptionResponse?.StatusCode ?? HttpStatusCode.BadRequest); - var response = exceptionResponse?.Response; + object? response = exceptionResponse?.Response; if (response is null) { await context.Response.WriteAsync(string.Empty); diff --git a/src/Genocs.WebApi/Extensions.cs b/src/Genocs.WebApi/Extensions.cs index acc4d596..18e8c96c 100644 --- a/src/Genocs.WebApi/Extensions.cs +++ b/src/Genocs.WebApi/Extensions.cs @@ -1,9 +1,9 @@ using Genocs.Common.Types; using Genocs.Core.Builders; using Genocs.WebApi; +using Genocs.WebApi.Configurations; using Genocs.WebApi.Exceptions; using Genocs.WebApi.Formatters; -using Genocs.WebApi.Options; using Genocs.WebApi.Requests; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; @@ -27,34 +27,21 @@ namespace Genocs.WebApi; public static class Extensions { - private static readonly byte[] InvalidJsonRequestBytes = Encoding.UTF8.GetBytes("An invalid JSON was sent."); private const string SectionName = "webApi"; private const string RegistryName = "webApi"; private const string EmptyJsonObject = "{}"; private const string LocationHeader = "Location"; + private const string JsonContentType = "application/json"; + private static readonly byte[] InvalidJsonRequestBytes = Encoding.UTF8.GetBytes("An invalid JSON was sent."); private static bool _bindRequestFromRoute; - public static IApplicationBuilder UseEndpoints(this IApplicationBuilder app, Action build, - bool useAuthorization = true, Action middleware = null) - { - var definitions = app.ApplicationServices.GetRequiredService(); - app.UseRouting(); - if (useAuthorization) - { - app.UseAuthorization(); - } - - middleware?.Invoke(app); - - app.UseEndpoints(router => build(new EndpointsBuilder(router, definitions))); - - return app; - } - [Description("By default System JSON serializer is being used. If Newtonsoft JSON serializer is used then it sets Kestrel's and IIS ServerOptions AllowSynchronousIO = true")] - public static IGenocsBuilder AddWebApi(this IGenocsBuilder builder, Action configureMvc = null, - IJsonSerializer jsonSerializer = null, string sectionName = SectionName) + public static IGenocsBuilder AddWebApi( + this IGenocsBuilder builder, + Action? configureMvc = null, + IJsonSerializer? jsonSerializer = null, + string sectionName = SectionName) { if (string.IsNullOrWhiteSpace(sectionName)) { @@ -76,8 +63,6 @@ public static IGenocsBuilder AddWebApi(this IGenocsBuilder builder, Action(); builder.Services.AddSingleton(new WebApiEndpointDefinitions()); - var options = builder.GetOptions(sectionName); + + var options = builder.GetOptions(sectionName); builder.Services.AddSingleton(options); + _bindRequestFromRoute = options.BindRequestFromRoute; var mvcCoreBuilder = builder.Services - .AddLogging() - .AddMvcCore(); + .AddLogging() + .AddMvcCore(); mvcCoreBuilder.AddMvcOptions(o => { @@ -139,16 +126,39 @@ public static IGenocsBuilder AddErrorHandler(this IGenocsBuilder builder) return builder; } + public static IApplicationBuilder UseEndpoints( + this IApplicationBuilder app, + Action build, + bool useAuthorization = true, + Action? middleware = null) + { + WebApiEndpointDefinitions definitions = app.ApplicationServices.GetRequiredService(); + + app.UseRouting(); + if (useAuthorization) + { + app.UseAuthorization(); + } + + middleware?.Invoke(app); + + app.UseEndpoints(router => build(new EndpointsBuilder(router, definitions))); + + return app; + } + public static IApplicationBuilder UseErrorHandler(this IApplicationBuilder builder) => builder.UseMiddleware(); - public static IApplicationBuilder UseAllForwardedHeaders(this IApplicationBuilder builder, - bool resetKnownNetworksAndProxies = true) + public static IApplicationBuilder UseAllForwardedHeaders( + this IApplicationBuilder builder, + bool resetKnownNetworksAndProxies = true) { - var forwardingOptions = new ForwardedHeadersOptions + ForwardedHeadersOptions forwardingOptions = new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.All }; + if (resetKnownNetworksAndProxies) { forwardingOptions.KnownNetworks.Clear(); @@ -171,8 +181,7 @@ public static T BindId(this T model, Expression> expression) public static T BindId(this T model, Expression> expression) => model.Bind(expression, Guid.NewGuid().ToString("N")); - private static TModel Bind(this TModel model, Expression> expression, - object value) + private static TModel Bind(this TModel model, Expression> expression, object value) { if (!(expression.Body is MemberExpression memberExpression)) { @@ -184,10 +193,12 @@ private static TModel Bind(this TModel model, Expression x.Name.ToLowerInvariant().StartsWith($"<{propertyName}>")); + .SingleOrDefault(x => x.Name.ToLowerInvariant().StartsWith($"<{propertyName}>")); + if (field is null) { return model; @@ -198,15 +209,16 @@ private static TModel Bind(this TModel model, Expression ReadJsonAsync(this HttpContext httpContext) } } - public static T ReadQuery(this HttpContext context) where T : class + public static T ReadQuery(this HttpContext context) + where T : class { var request = context.Request; - RouteValueDictionary values = null; + RouteValueDictionary? values = null; if (request.HasRouteData()) { values = request.HttpContext.GetRouteData().Values; @@ -363,7 +376,7 @@ public static T ReadQuery(this HttpContext context) where T : class { var queryString = HttpUtility.ParseQueryString(request.HttpContext.Request.QueryString.Value); values ??= new RouteValueDictionary(); - foreach (var key in queryString.AllKeys) + foreach (string? key in queryString.AllKeys) { values.TryAdd(key, queryString[key]); } @@ -375,7 +388,7 @@ public static T ReadQuery(this HttpContext context) where T : class return serializer.Deserialize(EmptyJsonObject); } - var serialized = serializer.Serialize(values.ToDictionary(k => k.Key, k => k.Value)) + string? serialized = serializer.Serialize(values.ToDictionary(k => k.Key, k => k.Value)) ?.Replace("\\\"", "\"") .Replace("\"{", "{") .Replace("}\"", "}") diff --git a/src/Genocs.WebApi/Formatters/JsonInputFormatter.cs b/src/Genocs.WebApi/Formatters/JsonInputFormatter.cs index 81780df7..e262e90b 100644 --- a/src/Genocs.WebApi/Formatters/JsonInputFormatter.cs +++ b/src/Genocs.WebApi/Formatters/JsonInputFormatter.cs @@ -33,7 +33,7 @@ public async Task ReadAsync(InputFormatterContext context) } var request = context.HttpContext.Request; - var json = string.Empty; + string json = string.Empty; if (request.Body is not null) { using var streamReader = new StreamReader(request.Body); @@ -45,7 +45,7 @@ public async Task ReadAsync(InputFormatterContext context) json = EmptyJson; } - var result = method.Invoke(_serializer, new object[] { json }); + object? result = method.Invoke(_serializer, new object[] { json }); return await InputFormatterResult.SuccessAsync(result); } diff --git a/src/Genocs.WebApi/Genocs.WebApi.csproj b/src/Genocs.WebApi/Genocs.WebApi.csproj index 300ac44a..aa0b5a0c 100644 --- a/src/Genocs.WebApi/Genocs.WebApi.csproj +++ b/src/Genocs.WebApi/Genocs.WebApi.csproj @@ -1,63 +1,36 @@  - - net6.0;net7.0 - enable - enable - Genocs.WebApi - 10.0 - true - 5.0.0-preview.4.0 - 5.0.0 - Nocco Giovanni Emanuele - Genocs - The web api library - The web api library - Genocs 2023 - LICENSE - https://github.com/Genocs/genocs-library - https://github.com/Genocs/genocs-library.git - icon.png - git - aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles - README.md - Aligned to the ecosystem - True - latest - True - + + net8.0;net7.0;net6.0 + Genocs.WebApi + Genocs.WebApi + Genocs.WebApi + The web api library + The web api library + true + 5.0.0 + Nocco Giovanni Emanuele + aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Aligned to the ecosystem + True + latest + - - - True - \ - - - True - \ - - - True - \ - - + + + + + + - - - + + + + - - - - - - - - - - - - - + + + diff --git a/src/Genocs.WebApi/IEndpointsBuilder.cs b/src/Genocs.WebApi/IEndpointsBuilder.cs index e7ea4b53..ab7e9fda 100644 --- a/src/Genocs.WebApi/IEndpointsBuilder.cs +++ b/src/Genocs.WebApi/IEndpointsBuilder.cs @@ -5,56 +5,80 @@ namespace Genocs.WebApi; public interface IEndpointsBuilder { - IEndpointsBuilder Get(string path, - Func? context = null, - Action? endpoint = null, - bool auth = false, - string? roles = null, - params string[] policies); - - IEndpointsBuilder Get(string path, - Func? context = null, - Action? endpoint = null, - bool auth = false, - string? roles = null, - params string[] policies) + IEndpointsBuilder Get( + string path, + Func? context = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies); + + IEndpointsBuilder Get( + string path, + Func? context = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) where T : class; - IEndpointsBuilder Get(string path, - Func? context = null, - Action? endpoint = null, - bool auth = false, - string? roles = null, - params string[] policies) + IEndpointsBuilder Get( + string path, + Func? context = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) where TRequest : class; - IEndpointsBuilder Post(string path, - Func? context = null, - Action? endpoint = null, - bool auth = false, - string? roles = null, - params string[] policies); + IEndpointsBuilder Post( + string path, + Func? context = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies); - IEndpointsBuilder Post(string path, Func? context = null, - Action? endpoint = null, bool auth = false, string? roles = null, - params string[] policies) + IEndpointsBuilder Post( + string path, + Func? context = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) where T : class; - IEndpointsBuilder Put(string path, Func? context = null, - Action? endpoint = null, bool auth = false, string? roles = null, - params string[] policies); + IEndpointsBuilder Put( + string path, + Func? context = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies); - IEndpointsBuilder Put(string path, Func? context = null, - Action? endpoint = null, bool auth = false, string? roles = null, - params string[] policies) + IEndpointsBuilder Put( + string path, + Func? context = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) where T : class; - IEndpointsBuilder Delete(string path, Func? context = null, - Action? endpoint = null, bool auth = false, string? roles = null, - params string[] policies); + IEndpointsBuilder Delete( + string path, + Func? context = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies); - IEndpointsBuilder Delete(string path, Func? context = null, - Action? endpoint = null, bool auth = false, string? roles = null, - params string[] policies) + IEndpointsBuilder Delete( + string path, + Func? context = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) where T : class; } \ No newline at end of file diff --git a/src/Genocs.WebApi/Options/WebApiSettings.cs b/src/Genocs.WebApi/Options/WebApiSettings.cs deleted file mode 100644 index 666d7ec3..00000000 --- a/src/Genocs.WebApi/Options/WebApiSettings.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Genocs.WebApi.Options; - -/// -/// The WebApiSettings definiiton -/// -public class WebApiSettings -{ - public bool BindRequestFromRoute { get; set; } -} \ No newline at end of file diff --git a/src/Genocs.WebApi/Parsers/JsonParser.cs b/src/Genocs.WebApi/Parsers/JsonParser.cs index bf9afeed..ae61aff9 100644 --- a/src/Genocs.WebApi/Parsers/JsonParser.cs +++ b/src/Genocs.WebApi/Parsers/JsonParser.cs @@ -3,9 +3,9 @@ namespace Genocs.WebApi.Parsers; -//Credits goes to .NET Foundation Team. -//JSON parser is based on JsonConfigurationFileParser found in Microsoft.Extensions.Configuration.Json library. -//https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Configuration.Json/src/JsonConfigurationFileParser.cs +// Credits goes to .NET Foundation Team. +// JSON parser is based on JsonConfigurationFileParser found in Microsoft.Extensions.Configuration.Json library. +// https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Configuration.Json/src/JsonConfigurationFileParser.cs public class JsonParser { private readonly Dictionary _data = new(StringComparer.OrdinalIgnoreCase); @@ -24,7 +24,7 @@ public IDictionary Parse(string json) { if (doc.RootElement.ValueKind != JsonValueKind.Object) { - throw new FormatException($"Invalidtop level JSON element: {doc.RootElement.ValueKind}"); + throw new FormatException($"Invalid top level JSON element: {doc.RootElement.ValueKind}"); } VisitElement(doc.RootElement); @@ -35,7 +35,7 @@ public IDictionary Parse(string json) private void VisitElement(JsonElement element) { - var isEmpty = true; + bool isEmpty = true; foreach (JsonProperty property in element.EnumerateObject()) { @@ -78,7 +78,7 @@ private void VisitValue(JsonElement value) case JsonValueKind.True: case JsonValueKind.False: case JsonValueKind.Null: - var key = _stack.Peek(); + string key = _stack.Peek(); if (_data.ContainsKey(key)) { diff --git a/src/Genocs.WebApi/README.md b/src/Genocs.WebApi/README.md deleted file mode 100644 index c96381ac..00000000 --- a/src/Genocs.WebApi/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# .NET Core Base library - -This package contains a set of base functionalities designed by Genocs. -The libraries are built using .NET standard 2.1. - - -## Description - -Core NuGet package contains general purpose functionalities to be used on DDD services. - - -## Support - -Please check the GitHub repository getting more info. - - -## Release notes - -### [2023-03-12] 5.0.0-preview.5.0 -- Implemented MongoDB repository interfaces - -### [2023-03-12] 5.0.0 -- New Architecture - -### [2023-03-12] 3.1.0 -- Added Builders - -### [2023-03-12] 3.0.0 -- Refactory to implement CQRS pattern - -### [2023-03-04] 2.4.1 -- Updated System.Text.Json diff --git a/src/Genocs.WebApi/README_NUGET.md b/src/Genocs.WebApi/README_NUGET.md new file mode 100644 index 00000000..cf06bc9b --- /dev/null +++ b/src/Genocs.WebApi/README_NUGET.md @@ -0,0 +1,60 @@ +# .NET Core Base library + +This package contains a set of base functionalities designed by Genocs. +The libraries are built using .NET standard 2.1. + + +## Description + +Core NuGet package contains general purpose functionalities to be used on DDD services. + + +## Support + +Please check the GitHub repository getting more info. + + +## Release notes + +### [2024-01-23] 5.0.6 +- Refactory Settings +- Updated nuget packages + +### [2023-11-25] 5.0.5 +- Moved to NET8 + +### [yyyy-mm-dd] 5.0.4 +- + +### [yyyy-mm-dd] 5.0.3 +- + +### [yyyy-mm-dd] 5.0.2 +- + +### [yyyy-mm-dd] 5.0.1 +- + +### [2023-11-25] 5.0.0 +- Moved to NET8 + +### [2023-10-13] 5.0.0-preview.5.0 +- Added [editorconfig](https://editorconfig.org/) +- Added StyleCop +- Updated logo +- Updated readme + +### [2023-03-12] 5.0.0-preview.4.0 +- Implemented MongoDB repository interfaces + +### [2023-03-12] 5.0.0 +- New Architecture + +### [2023-03-12] 3.1.0 +- Added Builders + +### [2023-03-12] 3.0.0 +- Refactory to implement CQRS pattern + +### [2023-03-04] 2.4.1 +- Updated System.Text.Json diff --git a/src/Genocs.WebApi/Requests/IRequestHandler.cs b/src/Genocs.WebApi/Requests/IRequestHandler.cs index 98b70df5..976bdd62 100644 --- a/src/Genocs.WebApi/Requests/IRequestHandler.cs +++ b/src/Genocs.WebApi/Requests/IRequestHandler.cs @@ -1,6 +1,7 @@ namespace Genocs.WebApi.Requests; -public interface IRequestHandler where TRequest : class, IRequest +public interface IRequestHandler + where TRequest : class, IRequest { Task HandleAsync(TRequest request, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/Genocs.WebApi/Requests/RequestDispatcher.cs b/src/Genocs.WebApi/Requests/RequestDispatcher.cs index c6fb597b..5195bfa0 100644 --- a/src/Genocs.WebApi/Requests/RequestDispatcher.cs +++ b/src/Genocs.WebApi/Requests/RequestDispatcher.cs @@ -11,8 +11,8 @@ public RequestDispatcher(IServiceProvider serviceProvider) _serviceProvider = serviceProvider; } - public async Task DispatchAsync(TRequest request, - CancellationToken cancellationToken = default) where TRequest : class, IRequest + public async Task DispatchAsync(TRequest request, CancellationToken cancellationToken = default) + where TRequest : class, IRequest { using var scope = _serviceProvider.CreateScope(); var handler = scope.ServiceProvider.GetRequiredService>(); diff --git a/src/apps/api-gateway/Genocs.APIGateway/Configurations/MessagingOptions.cs b/src/apps/api-gateway/Genocs.APIGateway/Configurations/MessagingOptions.cs new file mode 100644 index 00000000..1db1d293 --- /dev/null +++ b/src/apps/api-gateway/Genocs.APIGateway/Configurations/MessagingOptions.cs @@ -0,0 +1,24 @@ +namespace Genocs.APIGateway.Configurations; + +internal class MessagingOptions +{ + /// + /// Default section name. + /// + public const string Position = "messaging"; + + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + public IEnumerable? Endpoints { get; set; } + + internal class EndpointOptions + { + public string? Method { get; set; } + public string? Path { get; set; } + public string? Exchange { get; set; } + public string? RoutingKey { get; set; } + } +} \ No newline at end of file diff --git a/src/apps/api-gateway/Genocs.APIGateway/Framework/CorrelationContextBuilder.cs b/src/apps/api-gateway/Genocs.APIGateway/Framework/CorrelationContextBuilder.cs index 0a89e5e3..0b96c4a2 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Framework/CorrelationContextBuilder.cs +++ b/src/apps/api-gateway/Genocs.APIGateway/Framework/CorrelationContextBuilder.cs @@ -4,8 +4,12 @@ namespace Genocs.APIGateway.Framework; internal sealed class CorrelationContextBuilder : ICorrelationContextBuilder { - public CorrelationContext Build(HttpContext context, string correlationId, string spanContext, - string name = null, string resourceId = null) + public CorrelationContext Build( + HttpContext context, + string correlationId, + string spanContext, + string? name = null, + string? resourceId = null) => new CorrelationContext { CorrelationId = correlationId, diff --git a/src/apps/api-gateway/Genocs.APIGateway/Framework/ICorrelationContextBuilder.cs b/src/apps/api-gateway/Genocs.APIGateway/Framework/ICorrelationContextBuilder.cs index c64456f7..9618311f 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Framework/ICorrelationContextBuilder.cs +++ b/src/apps/api-gateway/Genocs.APIGateway/Framework/ICorrelationContextBuilder.cs @@ -2,6 +2,10 @@ namespace Genocs.APIGateway.Framework; internal interface ICorrelationContextBuilder { - CorrelationContext Build(HttpContext context, string correlationId, string spanContext, string? name = null, - string? resourceId = null); + CorrelationContext Build( + HttpContext context, + string correlationId, + string spanContext, + string? name = null, + string? resourceId = null); } \ No newline at end of file diff --git a/src/apps/api-gateway/Genocs.APIGateway/Framework/LogContextMiddleware.cs b/src/apps/api-gateway/Genocs.APIGateway/Framework/LogContextMiddleware.cs index 7d306530..a1fe66e6 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Framework/LogContextMiddleware.cs +++ b/src/apps/api-gateway/Genocs.APIGateway/Framework/LogContextMiddleware.cs @@ -13,7 +13,7 @@ public LogContextMiddleware(CorrelationIdFactory correlationIdFactory) public async Task InvokeAsync(HttpContext context, RequestDelegate next) { - var correlationId = _correlationIdFactory.Create(); + string correlationId = _correlationIdFactory.Create(); using (LogContext.PushProperty("CorrelationId", correlationId)) { await next(context); diff --git a/src/apps/api-gateway/Genocs.APIGateway/Framework/MessagingMiddleware.cs b/src/apps/api-gateway/Genocs.APIGateway/Framework/MessagingMiddleware.cs index bb224d67..9c97684a 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Framework/MessagingMiddleware.cs +++ b/src/apps/api-gateway/Genocs.APIGateway/Framework/MessagingMiddleware.cs @@ -1,9 +1,9 @@ +using System.Collections.Concurrent; +using Genocs.APIGateway.Configurations; using Genocs.MessageBrokers.RabbitMQ; using Genocs.MessageBrokers.RabbitMQ.Conventions; using Microsoft.Extensions.Options; using Newtonsoft.Json; -using OpenTracing; -using System.Collections.Concurrent; namespace Genocs.APIGateway.Framework; @@ -12,14 +12,16 @@ internal class MessagingMiddleware : IMiddleware private static readonly ConcurrentDictionary Conventions = new(); private readonly IRabbitMQClient _rabbitMQClient; private readonly RouteMatcher _routeMatcher; - private readonly ITracer _tracer; private readonly ICorrelationContextBuilder _correlationContextBuilder; private readonly CorrelationIdFactory _correlationIdFactory; private readonly IDictionary> _endpoints; - public MessagingMiddleware(IRabbitMQClient rabbitMQClient, RouteMatcher routeMatcher, ITracer tracer, - ICorrelationContextBuilder correlationContextBuilder, CorrelationIdFactory correlationIdFactory, - IOptions messagingOptions) + public MessagingMiddleware( + IRabbitMQClient rabbitMQClient, + RouteMatcher routeMatcher, + ICorrelationContextBuilder correlationContextBuilder, + CorrelationIdFactory correlationIdFactory, + IOptions messagingOptions) { if (messagingOptions is null) { @@ -28,7 +30,6 @@ public MessagingMiddleware(IRabbitMQClient rabbitMQClient, RouteMatcher routeMat _rabbitMQClient = rabbitMQClient ?? throw new ArgumentNullException(nameof(rabbitMQClient)); _routeMatcher = routeMatcher ?? throw new ArgumentNullException(nameof(routeMatcher)); - _tracer = tracer ?? throw new ArgumentNullException(nameof(tracer)); _correlationContextBuilder = correlationContextBuilder ?? throw new ArgumentNullException(nameof(correlationContextBuilder)); _correlationIdFactory = correlationIdFactory ?? throw new ArgumentNullException(nameof(correlationIdFactory)); _endpoints = messagingOptions.Value.Endpoints?.Any() is true @@ -60,16 +61,29 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) Conventions.TryAdd(key, conventions); } - var spanContext = _tracer.ActiveSpan is null ? string.Empty : _tracer.ActiveSpan.Context.ToString(); - var messageId = Guid.NewGuid().ToString("N"); - var correlationId = _correlationIdFactory.Create(); - var resourceId = Guid.NewGuid().ToString("N"); - var correlationContext = _correlationContextBuilder.Build(context, correlationId, spanContext, - endpoint.RoutingKey, resourceId); + string? spanContext = "TODO: Genocs"; + string messageId = Guid.NewGuid().ToString("N"); + string correlationId = _correlationIdFactory.Create(); + string resourceId = Guid.NewGuid().ToString("N"); + + var correlationContext = _correlationContextBuilder.Build( + context, + correlationId, + spanContext, + endpoint.RoutingKey, + resourceId); + + string content = await new StreamReader(context.Request.Body).ReadToEndAsync(); + object? message = JsonConvert.DeserializeObject(content); + + _rabbitMQClient.Send( + message, + conventions, + messageId, + correlationId, + spanContext, + correlationContext); - var content = await new StreamReader(context.Request.Body).ReadToEndAsync(); - var message = JsonConvert.DeserializeObject(content); - _rabbitMQClient.Send(message, conventions, messageId, correlationId, spanContext, correlationContext); context.Response.StatusCode = StatusCodes.Status202Accepted; return; } diff --git a/src/apps/api-gateway/Genocs.APIGateway/Framework/MessagingOptions.cs b/src/apps/api-gateway/Genocs.APIGateway/Framework/MessagingOptions.cs deleted file mode 100644 index 9f30b83e..00000000 --- a/src/apps/api-gateway/Genocs.APIGateway/Framework/MessagingOptions.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Genocs.APIGateway.Framework; - -internal class MessagingOptions -{ - public bool Enabled { get; set; } - public IEnumerable? Endpoints { get; set; } - - internal class EndpointOptions - { - public string Method { get; set; } - public string Path { get; set; } - public string Exchange { get; set; } - public string RoutingKey { get; set; } - } -} \ No newline at end of file diff --git a/src/apps/api-gateway/Genocs.APIGateway/Framework/UserMiddleware.cs b/src/apps/api-gateway/Genocs.APIGateway/Framework/UserMiddleware.cs index d2b85bb6..deb13ecb 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Framework/UserMiddleware.cs +++ b/src/apps/api-gateway/Genocs.APIGateway/Framework/UserMiddleware.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Authentication; +using System.Net; using System.Text; using System.Text.Json; @@ -10,7 +11,7 @@ public class UserMiddleware : IMiddleware { "POST", "PUT", "PATCH" }; - + public async Task InvokeAsync(HttpContext context, RequestDelegate next) { var request = context.Request; @@ -26,7 +27,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) return; } - var path = context.Request.Path.Value; + string? path = context.Request.Path.Value; if (path is not null && (path.Contains("sign-in") || path.Contains("sign-up"))) { await next(context); @@ -36,7 +37,8 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) var authenticateResult = await context.AuthenticateAsync(); if (!authenticateResult.Succeeded || authenticateResult.Principal is null) { - context.Response.StatusCode = 401; + // Set the response code to 401. + context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; return; } @@ -47,7 +49,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) content = await reader.ReadToEndAsync(); } - if(string.IsNullOrWhiteSpace(content)) + if (string.IsNullOrWhiteSpace(content)) { await next(context); return; @@ -61,7 +63,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) } payload["userId"] = Guid.Parse(context.User.Identity.Name); - var json = JsonSerializer.Serialize(payload); + string json = JsonSerializer.Serialize(payload); await using var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json)); context.Request.Body = memoryStream; context.Request.ContentLength = json.Length; diff --git a/src/apps/api-gateway/Genocs.APIGateway/Genocs.APIGateway.csproj b/src/apps/api-gateway/Genocs.APIGateway/Genocs.APIGateway.csproj index 29981233..6d8537c6 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Genocs.APIGateway.csproj +++ b/src/apps/api-gateway/Genocs.APIGateway/Genocs.APIGateway.csproj @@ -1,52 +1,52 @@  - - net7.0 - enable - enable - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ - True - - - - - - + + net8.0 + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ + True + + + + + + diff --git a/src/apps/api-gateway/Genocs.APIGateway/Startup.cs b/src/apps/api-gateway/Genocs.APIGateway/Startup.cs index 191217ac..0cb08426 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Startup.cs +++ b/src/apps/api-gateway/Genocs.APIGateway/Startup.cs @@ -1,13 +1,13 @@ +using Genocs.APIGateway.Configurations; using Genocs.APIGateway.Framework; using Genocs.Auth; -using Genocs.Common.Options; +using Genocs.Common.Configurations; using Genocs.Core.Builders; using Genocs.MessageBrokers.RabbitMQ; using Genocs.Metrics.Prometheus; using Genocs.Security; using Genocs.Tracing; using Genocs.Tracing.Jaeger; -using Genocs.Tracing.Jaeger.RabbitMQ; using Genocs.WebApi; using Yarp.ReverseProxy.Forwarder; @@ -21,7 +21,7 @@ public Startup(IConfiguration configuration) { Configuration = configuration; } - + public void ConfigureServices(IServiceCollection services) { services.AddScoped(); @@ -31,21 +31,20 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.Configure(Configuration.GetSection("messaging")); + services.Configure(Configuration.GetSection(MessagingOptions.Position)); services.AddReverseProxy() .LoadFromConfig(Configuration.GetSection("ReverseProxy")); services.AddSingleton(); services .AddGenocs() .AddOpenTelemetry() - .AddJaeger() .AddJwt() .AddPrometheus() - .AddRabbitMq(plugins: p => p.AddJaegerRabbitMqPlugin()) + .AddRabbitMq() .AddSecurity() .AddWebApi() .Build(); - + services.AddAuthorization(options => { options.AddPolicy("authenticatedUser", policy => @@ -61,6 +60,8 @@ public void ConfigureServices(IServiceCollection services) .WithHeaders("Content-Type", "Authorization"); }); }); + + services.AddHealthChecks(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) @@ -73,7 +74,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseMiddleware(); app.UseCors("cors"); app.UseGenocs(); - app.UseJaeger(); app.UsePrometheus(); app.UseAccessTokenValidator(); app.UseAuthentication(); @@ -87,9 +87,11 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { endpoints.MapGet("/", async context => { - await context.Response.WriteAsync(context.RequestServices.GetService()?.Name ?? "Service"); + await context.Response.WriteAsync(context.RequestServices.GetService()?.Name ?? "Service"); }); endpoints.MapReverseProxy(); + + endpoints.MapHealthChecks("/hc"); }); } } diff --git a/src/apps/api-gateway/Genocs.APIGateway/appsettings.Docker.json b/src/apps/api-gateway/Genocs.APIGateway/appsettings.Docker.json index f846cdd3..258ab8cd 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/appsettings.Docker.json +++ b/src/apps/api-gateway/Genocs.APIGateway/appsettings.Docker.json @@ -22,8 +22,14 @@ }, "jaeger": { "enabled": true, - "udpHost": "jaeger", - "serviceName": "api-gateway" + "serviceName": "orders", + "endpoint": "http://localhost:4317", + "protocol": "Grpc", + "processorType": "Batch", + "maxQueueSize": 2048, + "scheduledDelayMilliseconds": 5000, + "exporterTimeoutMilliseconds": 30000, + "maxExportBatchSize": 512 }, "metrics": { "enabled": true, diff --git a/src/apps/api-gateway/Genocs.APIGateway/appsettings.development.json b/src/apps/api-gateway/Genocs.APIGateway/appsettings.development.json index c428f22c..12ee80a7 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/appsettings.development.json +++ b/src/apps/api-gateway/Genocs.APIGateway/appsettings.development.json @@ -12,7 +12,7 @@ "enabled": true }, "prometheus": { - "enabled": true + "enabled": false }, "vault": { "enabled": false, diff --git a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json index 4183191b..fc009e6c 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json +++ b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json @@ -1,20 +1,17 @@ { "app": { "name": "API Gateway", - "service": "api-gateway" + "service": "api-gateway", + "instance": "000001", + "version": "v1.0", + "displayBanner": true, + "displayVersion": true }, "jaeger": { "enabled": true, "serviceName": "api-gateway", "udpHost": "localhost", - "udpPort": 6831, - "maxPacketSize": 65000, - "sampler": "const", - "excludePaths": [ - "/", - "/ping", - "/metrics" - ] + "maxPacketSize": 65000 }, "jwt": { "certificate": { @@ -28,7 +25,7 @@ "expiry": "01:00:00" }, "logger": { - "level": "information", + "level": "debug", "excludePaths": [ "/", "/ping", @@ -85,7 +82,7 @@ ] }, "prometheus": { - "enabled": true, + "enabled": false, "endpoint": "/metrics" }, "rabbitMq": { diff --git a/src/apps/apigateway.dockerfile b/src/apps/apigateway.dockerfile index 9d96bc30..fcc5007e 100644 --- a/src/apps/apigateway.dockerfile +++ b/src/apps/apigateway.dockerfile @@ -1,20 +1,11 @@ #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. - -#FROM mcr.microsoft.com/dotnet/core/aspnet:3.0 -#FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim -#FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim -#FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine AS base -FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 -#FROM mcr.microsoft.com/dotnet/core/sdk:3.0 AS build-env -#FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build-env -# FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build-env -#FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build-env -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env WORKDIR /src COPY ["api-gateway/Genocs.APIGateway", "Genocs.APIGateway/"] diff --git a/src/apps/identity-webapi.dockerfile b/src/apps/identity-webapi.dockerfile index d7525095..6ea34df3 100644 --- a/src/apps/identity-webapi.dockerfile +++ b/src/apps/identity-webapi.dockerfile @@ -1,20 +1,11 @@ #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. - -#FROM mcr.microsoft.com/dotnet/core/aspnet:3.0 -#FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim -#FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim -#FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine -FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 -#FROM mcr.microsoft.com/dotnet/core/sdk:3.0 AS build-env -#FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build-env -# FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build-env -#FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build-env -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env WORKDIR /src COPY ["identity/Genocs.Identities.WebApi", "Genocs.Identities.WebApi/"] COPY ["identity/Genocs.Identities.Application", "Genocs.Identities.Application/"] diff --git a/src/apps/identity/Genocs.Identities.Application/CorrelationIdFactory.cs b/src/apps/identity/Genocs.Identities.Application/CorrelationIdFactory.cs index b9231895..c535997a 100644 --- a/src/apps/identity/Genocs.Identities.Application/CorrelationIdFactory.cs +++ b/src/apps/identity/Genocs.Identities.Application/CorrelationIdFactory.cs @@ -1,5 +1,5 @@ using Genocs.HTTP; -using Genocs.HTTP.Options; +using Genocs.HTTP.Configurations; using Genocs.MessageBrokers; using Microsoft.AspNetCore.Http; @@ -11,17 +11,24 @@ internal class CorrelationIdFactory : ICorrelationIdFactory private readonly IMessagePropertiesAccessor _messagePropertiesAccessor; private readonly IHttpContextAccessor _httpContextAccessor; - private readonly string _header; + private readonly string? _header; - public CorrelationIdFactory(IMessagePropertiesAccessor messagePropertiesAccessor, - IHttpContextAccessor httpContextAccessor, HttpClientSettings httpClientOptions) + public CorrelationIdFactory( + IMessagePropertiesAccessor messagePropertiesAccessor, + IHttpContextAccessor httpContextAccessor, + HttpClientOptions httpClientOptions) { - _messagePropertiesAccessor = messagePropertiesAccessor; - _httpContextAccessor = httpContextAccessor; + if (httpClientOptions is null) + { + throw new ArgumentNullException(nameof(httpClientOptions)); + } + + _messagePropertiesAccessor = messagePropertiesAccessor ?? throw new ArgumentNullException(nameof(messagePropertiesAccessor)); + _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); _header = httpClientOptions.CorrelationIdHeader; } - private static string CorrelationId + private static string? CorrelationId { get => Holder.Value?.Id; set @@ -41,7 +48,7 @@ private static string CorrelationId private class CorrelationIdHolder { - public string Id; + public string? Id; } public string Create() @@ -51,7 +58,7 @@ public string Create() return CorrelationId; } - var correlationId = _messagePropertiesAccessor.MessageProperties?.CorrelationId; + string? correlationId = _messagePropertiesAccessor.MessageProperties?.CorrelationId; if (!string.IsNullOrWhiteSpace(correlationId)) { CorrelationId = correlationId; diff --git a/src/apps/identity/Genocs.Identities.Application/DTO/AuthDto.cs b/src/apps/identity/Genocs.Identities.Application/DTO/AuthDto.cs index 403e9fd5..a5652115 100644 --- a/src/apps/identity/Genocs.Identities.Application/DTO/AuthDto.cs +++ b/src/apps/identity/Genocs.Identities.Application/DTO/AuthDto.cs @@ -1,11 +1,14 @@ namespace Genocs.Identities.Application.DTO; +/// +/// The AuthDto class. +/// public class AuthDto { public Guid UserId { get; set; } public string Username { get; set; } - public string Role { get; set; } - public string AccessToken { get; set; } + public string? Role { get; set; } + public string? AccessToken { get; set; } public string RefreshToken { get; set; } public long Expires { get; set; } } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Decorators/OutboxCommandHandlerDecorator.cs b/src/apps/identity/Genocs.Identities.Application/Decorators/OutboxCommandHandlerDecorator.cs index 27216af2..5401d00c 100644 --- a/src/apps/identity/Genocs.Identities.Application/Decorators/OutboxCommandHandlerDecorator.cs +++ b/src/apps/identity/Genocs.Identities.Application/Decorators/OutboxCommandHandlerDecorator.cs @@ -2,7 +2,7 @@ using Genocs.Core.CQRS.Commands; using Genocs.MessageBrokers; using Genocs.MessageBrokers.Outbox; -using Genocs.MessageBrokers.Outbox.Options; +using Genocs.MessageBrokers.Outbox.Configurations; namespace Genocs.Identities.Application.Decorators; @@ -16,7 +16,7 @@ internal sealed class OutboxCommandHandlerDecorator : ICommandHandler< private readonly bool _enabled; public OutboxCommandHandlerDecorator(ICommandHandler handler, IMessageOutbox outbox, - OutboxSettings outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) + OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) { _handler = handler; _outbox = outbox; diff --git a/src/apps/identity/Genocs.Identities.Application/Decorators/OutboxEventHandlerDecorator.cs b/src/apps/identity/Genocs.Identities.Application/Decorators/OutboxEventHandlerDecorator.cs index 1c3ef1dd..0bad3e59 100644 --- a/src/apps/identity/Genocs.Identities.Application/Decorators/OutboxEventHandlerDecorator.cs +++ b/src/apps/identity/Genocs.Identities.Application/Decorators/OutboxEventHandlerDecorator.cs @@ -2,7 +2,7 @@ using Genocs.Core.CQRS.Events; using Genocs.MessageBrokers; using Genocs.MessageBrokers.Outbox; -using Genocs.MessageBrokers.Outbox.Options; +using Genocs.MessageBrokers.Outbox.Configurations; namespace Genocs.Identities.Application.Decorators; @@ -16,7 +16,7 @@ internal sealed class OutboxEventHandlerDecorator : IEventHandler handler, IMessageOutbox outbox, - OutboxSettings outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) + OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) { _handler = handler; _outbox = outbox; diff --git a/src/apps/identity/Genocs.Identities.Application/Domain/Entities/RefreshToken.cs b/src/apps/identity/Genocs.Identities.Application/Domain/Entities/RefreshToken.cs index 7b17070e..5f0835ba 100644 --- a/src/apps/identity/Genocs.Identities.Application/Domain/Entities/RefreshToken.cs +++ b/src/apps/identity/Genocs.Identities.Application/Domain/Entities/RefreshToken.cs @@ -10,8 +10,13 @@ public class RefreshToken : AggregateRoot public DateTime? RevokedAt { get; private set; } public bool Revoked => RevokedAt.HasValue; - public RefreshToken(AggregateId id, AggregateId userId, string token, DateTime createdAt, - DateTime? revokedAt = null) : base(id) + public RefreshToken( + AggregateId id, + AggregateId userId, + string token, + DateTime createdAt, + DateTime? revokedAt = null) + : base(id) { if (string.IsNullOrWhiteSpace(token)) { diff --git a/src/apps/identity/Genocs.Identities.Application/Domain/Entities/User.cs b/src/apps/identity/Genocs.Identities.Application/Domain/Entities/User.cs index 46bbe004..9e69be07 100644 --- a/src/apps/identity/Genocs.Identities.Application/Domain/Entities/User.cs +++ b/src/apps/identity/Genocs.Identities.Application/Domain/Entities/User.cs @@ -12,8 +12,16 @@ public class User : AggregateRoot public IEnumerable? Permissions { get; private set; } public bool Locked { get; private set; } - public User(Guid id, string email, string name, string password, string role, DateTime createdAt, - IEnumerable? permissions = null, bool locked = false) : base(id) + public User( + Guid id, + string email, + string name, + string password, + string role, + DateTime createdAt, + IEnumerable? permissions = null, + bool locked = false) + : base(id) { if (string.IsNullOrWhiteSpace(email)) { @@ -45,7 +53,6 @@ public User(Guid id, string email, string name, string password, string role, Da Locked = locked; } - public bool Lock() { if (Locked) diff --git a/src/apps/identity/Genocs.Identities.Application/Domain/Exceptions/InvalidNameException.cs b/src/apps/identity/Genocs.Identities.Application/Domain/Exceptions/InvalidNameException.cs index f0f3fb66..4d64eb2e 100644 --- a/src/apps/identity/Genocs.Identities.Application/Domain/Exceptions/InvalidNameException.cs +++ b/src/apps/identity/Genocs.Identities.Application/Domain/Exceptions/InvalidNameException.cs @@ -2,7 +2,8 @@ namespace Genocs.Identities.Application.Domain.Exceptions; public class InvalidNameException : DomainException { - public InvalidNameException(string name) : base($"Invalid name: {name}.") + public InvalidNameException(string name) + : base($"Invalid name: {name}.") { } } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Exceptions/AppException.cs b/src/apps/identity/Genocs.Identities.Application/Exceptions/AppException.cs index cb4315e1..f9336e85 100644 --- a/src/apps/identity/Genocs.Identities.Application/Exceptions/AppException.cs +++ b/src/apps/identity/Genocs.Identities.Application/Exceptions/AppException.cs @@ -2,7 +2,8 @@ namespace Genocs.Identities.Application.Exceptions; public abstract class AppException : Exception { - protected AppException(string message) : base(message) + protected AppException(string message) + : base(message) { } } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Exceptions/ExceptionToResponseMapper.cs b/src/apps/identity/Genocs.Identities.Application/Exceptions/ExceptionToResponseMapper.cs index 1175fb72..e11452f4 100644 --- a/src/apps/identity/Genocs.Identities.Application/Exceptions/ExceptionToResponseMapper.cs +++ b/src/apps/identity/Genocs.Identities.Application/Exceptions/ExceptionToResponseMapper.cs @@ -13,24 +13,24 @@ public class ExceptionToResponseMapper : IExceptionToResponseMapper public ExceptionResponse Map(Exception exception) => exception switch { - DomainException ex => new ExceptionResponse(new { code = GetCode(ex), reason = ex.Message }, - HttpStatusCode.BadRequest), - AppException ex => new ExceptionResponse(new { code = GetCode(ex), reason = ex.Message }, - HttpStatusCode.BadRequest), - _ => new ExceptionResponse(new { code = "error", reason = "There was an error." }, - HttpStatusCode.BadRequest) + DomainException ex => new ExceptionResponse(new { code = GetCode(ex), reason = ex.Message }, HttpStatusCode.BadRequest), + AppException ex => new ExceptionResponse(new { code = GetCode(ex), reason = ex.Message }, HttpStatusCode.BadRequest), + _ => new ExceptionResponse(new { code = "error", reason = "There was an error." }, HttpStatusCode.BadRequest) }; - private static string GetCode(Exception exception) + private static string? GetCode(Exception exception) { var type = exception.GetType(); - if (Codes.TryGetValue(type, out var code)) + if (Codes.TryGetValue(type, out string? code)) { return code; } - var exceptionCode = exception.GetType().Name.Underscore().Replace("_exception", string.Empty); - Codes.TryAdd(type, exceptionCode); + string? exceptionCode = exception.GetType().Name.Underscore()?.Replace("_exception", string.Empty); + if (!string.IsNullOrWhiteSpace(exceptionCode)) + { + Codes.TryAdd(type, exceptionCode); + } return exceptionCode; } diff --git a/src/apps/identity/Genocs.Identities.Application/Extensions.cs b/src/apps/identity/Genocs.Identities.Application/Extensions.cs index 593c58a1..1fe24b00 100644 --- a/src/apps/identity/Genocs.Identities.Application/Extensions.cs +++ b/src/apps/identity/Genocs.Identities.Application/Extensions.cs @@ -1,5 +1,5 @@ using Genocs.Auth; -using Genocs.Common.Options; +using Genocs.Common.Configurations; using Genocs.Core.Builders; using Genocs.Core.CQRS.Commands; using Genocs.Core.CQRS.Events; @@ -68,7 +68,6 @@ public static IGenocsBuilder AddCore(this IGenocsBuilder builder) .AddMongo() .AddRedis() .AddOpenTelemetry() - .AddJaeger() .AddMetrics() .AddMongoRepository("refreshTokens") .AddMongoRepository("users") @@ -106,7 +105,7 @@ public static IApplicationBuilder UseCore(this IApplicationBuilder app) } public static async Task GetAppName(this HttpContext httpContext) - => await httpContext.Response.WriteAsync(httpContext.RequestServices?.GetService()?.Name ?? string.Empty); + => await httpContext.Response.WriteAsync(httpContext.RequestServices?.GetService()?.Name ?? string.Empty); internal static CorrelationContext GetCorrelationContext(this IHttpContextAccessor accessor) => accessor.HttpContext?.Request.Headers.TryGetValue("Correlation-Context", out var json) is true diff --git a/src/apps/identity/Genocs.Identities.Application/Genocs.Identities.Application.csproj b/src/apps/identity/Genocs.Identities.Application/Genocs.Identities.Application.csproj index 8bc44ddb..9704def8 100644 --- a/src/apps/identity/Genocs.Identities.Application/Genocs.Identities.Application.csproj +++ b/src/apps/identity/Genocs.Identities.Application/Genocs.Identities.Application.csproj @@ -1,44 +1,35 @@  - - net7.0 - enable - enable - + + net8.0 + false + false + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - diff --git a/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/RefreshTokenDocument.cs b/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/RefreshTokenDocument.cs index 9b97b073..48820859 100644 --- a/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/RefreshTokenDocument.cs +++ b/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/RefreshTokenDocument.cs @@ -1,9 +1,9 @@ -using Genocs.Common.Types; +using Genocs.Core.Domain.Entities; using Genocs.Identities.Application.Domain.Entities; namespace Genocs.Identities.Application.Mongo.Documents; -public class RefreshTokenDocument : IIdentifiable +public class RefreshTokenDocument : IEntity { public Guid Id { get; set; } public Guid UserId { get; set; } diff --git a/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/UserDocument.cs b/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/UserDocument.cs index 8090b063..06ddeb7d 100644 --- a/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/UserDocument.cs +++ b/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/UserDocument.cs @@ -1,9 +1,10 @@ using Genocs.Common.Types; +using Genocs.Core.Domain.Entities; using Genocs.Identities.Application.Domain.Entities; namespace Genocs.Identities.Application.Mongo.Documents; -public class UserDocument : IIdentifiable +public class UserDocument : IEntity { public Guid Id { get; set; } public string Email { get; set; } diff --git a/src/apps/identity/Genocs.Identities.Application/Mongo/Extensions.cs b/src/apps/identity/Genocs.Identities.Application/Mongo/Extensions.cs index cc9c8d93..97185239 100644 --- a/src/apps/identity/Genocs.Identities.Application/Mongo/Extensions.cs +++ b/src/apps/identity/Genocs.Identities.Application/Mongo/Extensions.cs @@ -1,5 +1,5 @@ using Genocs.Identities.Application.Mongo.Documents; -using Genocs.Persistence.MongoDb.Repositories; +using Genocs.Persistence.MongoDb.Domain.Repositories; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using MongoDB.Driver; @@ -11,17 +11,26 @@ public static class Extensions public static IApplicationBuilder UseMongo(this IApplicationBuilder builder) { using var scope = builder.ApplicationServices.CreateScope(); - var users = scope.ServiceProvider.GetService>().Collection; + var users = scope.ServiceProvider.GetService>()?.Collection; + + if (users is null) + { + return builder; + } + var userBuilder = Builders.IndexKeys; + Task.Run(async () => await users.Indexes.CreateManyAsync( new[] { - new CreateIndexModel(userBuilder.Ascending(i => i.Email), + new CreateIndexModel( + userBuilder.Ascending(i => i.Email), new CreateIndexOptions { Unique = true }), - new CreateIndexModel(userBuilder.Ascending(i => i.Name), + new CreateIndexModel( + userBuilder.Ascending(i => i.Name), new CreateIndexOptions { Unique = true diff --git a/src/apps/identity/Genocs.Identities.Application/Mongo/Queries/Handlers/GetUserHandler.cs b/src/apps/identity/Genocs.Identities.Application/Mongo/Queries/Handlers/GetUserHandler.cs index 5edbedbb..ff43d3fb 100644 --- a/src/apps/identity/Genocs.Identities.Application/Mongo/Queries/Handlers/GetUserHandler.cs +++ b/src/apps/identity/Genocs.Identities.Application/Mongo/Queries/Handlers/GetUserHandler.cs @@ -2,15 +2,15 @@ using Genocs.Identities.Application.DTO; using Genocs.Identities.Application.Mongo.Documents; using Genocs.Identities.Application.Queries; -using Genocs.Persistence.MongoDb.Repositories; +using Genocs.Persistence.MongoDb.Domain.Repositories; namespace Genocs.Identities.Application.Mongo.Queries.Handlers; public class GetUserHandler : IQueryHandler { - private readonly IMongoRepository _userRepository; + private readonly IMongoDbBaseRepository _userRepository; - public GetUserHandler(IMongoRepository userRepository) + public GetUserHandler(IMongoDbBaseRepository userRepository) { _userRepository = userRepository; } diff --git a/src/apps/identity/Genocs.Identities.Application/Mongo/Repositories/RefreshTokenRepository.cs b/src/apps/identity/Genocs.Identities.Application/Mongo/Repositories/RefreshTokenRepository.cs index dea8a35e..d449921a 100644 --- a/src/apps/identity/Genocs.Identities.Application/Mongo/Repositories/RefreshTokenRepository.cs +++ b/src/apps/identity/Genocs.Identities.Application/Mongo/Repositories/RefreshTokenRepository.cs @@ -1,15 +1,15 @@ using Genocs.Identities.Application.Domain.Entities; using Genocs.Identities.Application.Domain.Repositories; using Genocs.Identities.Application.Mongo.Documents; -using Genocs.Persistence.MongoDb.Repositories; +using Genocs.Persistence.MongoDb.Domain.Repositories; namespace Genocs.Identities.Application.Mongo.Repositories; public class RefreshTokenRepository : IRefreshTokenRepository { - private readonly IMongoRepository _repository; + private readonly IMongoDbBaseRepository _repository; - public RefreshTokenRepository(IMongoRepository repository) + public RefreshTokenRepository(IMongoDbBaseRepository repository) { _repository = repository; } diff --git a/src/apps/identity/Genocs.Identities.Application/Mongo/Repositories/UserRepository.cs b/src/apps/identity/Genocs.Identities.Application/Mongo/Repositories/UserRepository.cs index b5a52111..4228fed7 100644 --- a/src/apps/identity/Genocs.Identities.Application/Mongo/Repositories/UserRepository.cs +++ b/src/apps/identity/Genocs.Identities.Application/Mongo/Repositories/UserRepository.cs @@ -1,15 +1,15 @@ using Genocs.Identities.Application.Domain.Entities; using Genocs.Identities.Application.Domain.Repositories; using Genocs.Identities.Application.Mongo.Documents; -using Genocs.Persistence.MongoDb.Repositories; +using Genocs.Persistence.MongoDb.Domain.Repositories; namespace Genocs.Identities.Application.Mongo.Repositories; public class UserRepository : IUserRepository { - private readonly IMongoRepository _repository; + private readonly IMongoDbBaseRepository _repository; - public UserRepository(IMongoRepository repository) + public UserRepository(IMongoDbBaseRepository repository) { _repository = repository; } diff --git a/src/apps/identity/Genocs.Identities.Application/Services/IJwtProvider.cs b/src/apps/identity/Genocs.Identities.Application/Services/IJwtProvider.cs index b06d8093..eca33933 100644 --- a/src/apps/identity/Genocs.Identities.Application/Services/IJwtProvider.cs +++ b/src/apps/identity/Genocs.Identities.Application/Services/IJwtProvider.cs @@ -3,10 +3,15 @@ namespace Genocs.Identities.Application.Services; /// -/// JwtProvider interface definition +/// JwtProvider interface definition. /// public interface IJwtProvider { - AuthDto Create(Guid userId, string username, string role, string? audience = null, - IDictionary>? claims = null); + AuthDto Create( + Guid userId, + string username, + string role, + string? audience = null, + IDictionary>? claims = null); } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Services/JwtProvider.cs b/src/apps/identity/Genocs.Identities.Application/Services/JwtProvider.cs index ff76ec60..093b1a16 100644 --- a/src/apps/identity/Genocs.Identities.Application/Services/JwtProvider.cs +++ b/src/apps/identity/Genocs.Identities.Application/Services/JwtProvider.cs @@ -12,11 +12,12 @@ public JwtProvider(IJwtHandler jwtHandler) _jwtHandler = jwtHandler; } - public AuthDto Create(Guid userId, - string username, - string role, - string? audience = null, - IDictionary>? claims = null) + public AuthDto Create( + Guid userId, + string username, + string role, + string? audience = null, + IDictionary>? claims = null) { var jwt = _jwtHandler.CreateToken(userId.ToString("N"), role, audience, claims); diff --git a/src/apps/identity/Genocs.Identities.Application/Services/MessageBroker.cs b/src/apps/identity/Genocs.Identities.Application/Services/MessageBroker.cs index 653bf74f..d2ae9507 100644 --- a/src/apps/identity/Genocs.Identities.Application/Services/MessageBroker.cs +++ b/src/apps/identity/Genocs.Identities.Application/Services/MessageBroker.cs @@ -21,10 +21,15 @@ internal class MessageBroker : IMessageBroker private readonly ILogger _logger; private readonly string _spanContextHeader; - public MessageBroker(IBusPublisher busPublisher, IMessageOutbox outbox, - ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor, - IMessagePropertiesAccessor messagePropertiesAccessor, ICorrelationIdFactory correlationIdFactory, - RabbitMQOptions options, ILogger logger) + public MessageBroker( + IBusPublisher busPublisher, + IMessageOutbox outbox, + ICorrelationContextAccessor contextAccessor, + IHttpContextAccessor httpContextAccessor, + IMessagePropertiesAccessor messagePropertiesAccessor, + ICorrelationIdFactory correlationIdFactory, + RabbitMQOptions options, + ILogger logger) { if (options is null) { @@ -53,11 +58,12 @@ private async Task PublishAsync(IEnumerable? events) } var messageProperties = _messagePropertiesAccessor.MessageProperties; - var originatedMessageId = messageProperties?.MessageId; - var correlationId = _correlationIdFactory.Create(); - var spanContext = messageProperties?.GetSpanContext(_spanContextHeader); - var correlationContext = _contextAccessor.CorrelationContext ?? + string? originatedMessageId = messageProperties?.MessageId; + string? correlationId = _correlationIdFactory.Create(); + string? spanContext = messageProperties?.GetSpanContext(_spanContextHeader); + object correlationContext = _contextAccessor.CorrelationContext ?? _httpContextAccessor.GetCorrelationContext(); + var headers = new Dictionary(); foreach (var @event in events) @@ -67,11 +73,12 @@ private async Task PublishAsync(IEnumerable? events) continue; } - var messageId = Guid.NewGuid().ToString("N"); + string messageId = Guid.NewGuid().ToString("N"); _logger.LogTrace($"Publishing integration event: {@event.GetType().Name.Underscore()} [ID: '{messageId}']."); if (_outbox.Enabled) { - await _outbox.SendAsync(@event, + await _outbox.SendAsync( + @event, originatedMessageId, messageId, correlationId, @@ -81,12 +88,13 @@ await _outbox.SendAsync(@event, continue; } - await _busPublisher.PublishAsync(@event, - messageId, - correlationId, - spanContext, - correlationContext, - headers); + await _busPublisher.PublishAsync( + @event, + messageId, + correlationId, + spanContext, + correlationContext, + headers); } } } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.WebApi/Genocs.Identities.WebApi.csproj b/src/apps/identity/Genocs.Identities.WebApi/Genocs.Identities.WebApi.csproj index a5eff4b5..1a601e87 100644 --- a/src/apps/identity/Genocs.Identities.WebApi/Genocs.Identities.WebApi.csproj +++ b/src/apps/identity/Genocs.Identities.WebApi/Genocs.Identities.WebApi.csproj @@ -1,30 +1,24 @@  - - net7.0 - enable - enable - _genocs - Linux - ..\.. - + + net8.0 + false + false + _genocs + Linux + ..\.. + - - - - - + + + - - - - - - - - - - - + + + + + + + diff --git a/src/apps/identity/Genocs.Identities.WebApi/Program.cs b/src/apps/identity/Genocs.Identities.WebApi/Program.cs index 8e6cb25e..c6b2994c 100644 --- a/src/apps/identity/Genocs.Identities.WebApi/Program.cs +++ b/src/apps/identity/Genocs.Identities.WebApi/Program.cs @@ -9,16 +9,8 @@ using Genocs.WebApi; using Genocs.WebApi.CQRS; using Serilog; -using Serilog.Events; - -Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .MinimumLevel.Override("Microsoft", LogEventLevel.Information) - .MinimumLevel.Override("MassTransit", LogEventLevel.Information) - .Enrich.FromLogContext() - .WriteTo.Console() - .CreateLogger(); +StaticLogger.EnsureInitialized(); var builder = WebApplication.CreateBuilder(args); @@ -26,9 +18,14 @@ .UseLogging() .UseVault(); - var services = builder.Services; +builder.Logging.AddOpenTelemetry(logging => +{ + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; +}); + services.AddGenocs(builder.Configuration) .AddWebApi() .AddCore() @@ -38,7 +35,7 @@ app.UseCore(); app.UseDispatcherEndpoints(endpoints => endpoints - .Get("", ctx => ctx.GetAppName()) + .Get(string.Empty, ctx => ctx.GetAppName()) .Post("sign-in", afterDispatch: (cmd, ctx) => { var auth = ctx.RequestServices.GetRequiredService().Get(cmd.Id); @@ -46,7 +43,7 @@ }) .Post("sign-up", afterDispatch: (cmd, ctx) => { - ctx.Response.Headers.Add("user-id", cmd.UserId.ToString()); + ctx.Response.Headers.Append("user-id", cmd.UserId.ToString()); return Task.CompletedTask; }) .Post("access-tokens/revoke", auth: true, roles: "admin") @@ -63,5 +60,4 @@ app.Run(); -Log.CloseAndFlush(); - +Log.CloseAndFlush(); \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.WebApi/appsettings.json b/src/apps/identity/Genocs.Identities.WebApi/appsettings.json index 79f7ff97..e08e5fc8 100644 --- a/src/apps/identity/Genocs.Identities.WebApi/appsettings.json +++ b/src/apps/identity/Genocs.Identities.WebApi/appsettings.json @@ -73,7 +73,7 @@ "Token" ], "console": { - "enabled": true + "enabled": false }, "elk": { "enabled": false, diff --git a/src/apps/order-webapi.dockerfile b/src/apps/order-webapi.dockerfile index 4ae55469..c0554e39 100644 --- a/src/apps/order-webapi.dockerfile +++ b/src/apps/order-webapi.dockerfile @@ -1,20 +1,11 @@ #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. - -#FROM mcr.microsoft.com/dotnet/core/aspnet:3.0 -#FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim -#FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim -#FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine -FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 -#FROM mcr.microsoft.com/dotnet/core/sdk:3.0 AS build-env -#FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build-env -# FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build-env -#FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build-env -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env WORKDIR /src COPY ["orders/Genocs.Orders.WebApi", "Genocs.Orders.WebApi/"] diff --git a/src/apps/orders/Genocs.Orders.WebApi/Commands/Handlers/CreateOrderHandler.cs b/src/apps/orders/Genocs.Orders.WebApi/Commands/Handlers/CreateOrderHandler.cs index df8a7c13..880aa814 100644 --- a/src/apps/orders/Genocs.Orders.WebApi/Commands/Handlers/CreateOrderHandler.cs +++ b/src/apps/orders/Genocs.Orders.WebApi/Commands/Handlers/CreateOrderHandler.cs @@ -4,35 +4,35 @@ using Genocs.Orders.WebApi.Domain; using Genocs.Orders.WebApi.Events; using Genocs.Orders.WebApi.Services; -using Genocs.Persistence.MongoDb.Repositories; -using OpenTracing; +using Genocs.Persistence.MongoDb.Domain.Repositories; namespace Genocs.Orders.WebApi.Commands.Handlers; public class CreateOrderHandler : ICommandHandler { - private readonly IMongoRepository _repository; + private readonly IMongoDbBaseRepository _repository; private readonly IBusPublisher _publisher; private readonly IMessageOutbox _outbox; private readonly IProductServiceClient _productServiceClient; private readonly ILogger _logger; - private readonly ITracer _tracer; - public CreateOrderHandler(IMongoRepository repository, IBusPublisher publisher, - IMessageOutbox outbox, IProductServiceClient productServiceClient, ITracer tracer, - ILogger logger) + public CreateOrderHandler( + IMongoDbBaseRepository repository, + IBusPublisher publisher, + IMessageOutbox outbox, + IProductServiceClient productServiceClient, + ILogger logger) { _repository = repository; _publisher = publisher; _outbox = outbox; _productServiceClient = productServiceClient; - _tracer = tracer; _logger = logger; } public async Task HandleAsync(CreateOrder command, CancellationToken cancellationToken = default) { - var exists = await _repository.ExistsAsync(o => o.Id == command.OrderId); + bool exists = await _repository.ExistsAsync(o => o.Id == command.OrderId); if (exists) { throw new InvalidOperationException($"Order with given id: {command.OrderId} already exists!"); @@ -49,10 +49,10 @@ public async Task HandleAsync(CreateOrder command, CancellationToken cancellatio var order = new Order(command.OrderId, command.CustomerId, productDto.UnitPrice); await _repository.AddAsync(order); - + _logger.LogInformation($"Created order '{command.OrderId}' for customer '{command.CustomerId}'."); - var spanContext = _tracer.ActiveSpan?.Context.ToString(); + string? spanContext = "TODO: Genocs"; var @event = new OrderCreated(order.Id); if (_outbox.Enabled) { diff --git a/src/apps/orders/Genocs.Orders.WebApi/Domain/Order.cs b/src/apps/orders/Genocs.Orders.WebApi/Domain/Order.cs index 3a1b1f69..ad0cfb06 100644 --- a/src/apps/orders/Genocs.Orders.WebApi/Domain/Order.cs +++ b/src/apps/orders/Genocs.Orders.WebApi/Domain/Order.cs @@ -1,8 +1,8 @@ -using Genocs.Common.Types; +using Genocs.Core.Domain.Entities; namespace Genocs.Orders.WebApi.Domain; -public class Order : IIdentifiable +public class Order : IEntity { public Guid Id { get; private set; } public Guid CustomerId { get; private set; } diff --git a/src/apps/orders/Genocs.Orders.WebApi/Genocs.Orders.WebApi.csproj b/src/apps/orders/Genocs.Orders.WebApi/Genocs.Orders.WebApi.csproj index 8f7c4c0d..7018f219 100644 --- a/src/apps/orders/Genocs.Orders.WebApi/Genocs.Orders.WebApi.csproj +++ b/src/apps/orders/Genocs.Orders.WebApi/Genocs.Orders.WebApi.csproj @@ -1,49 +1,50 @@  - - net7.0 - enable - enable - _genocs - Linux - ..\.. - + + net8.0 + false + false + _genocs + Linux + ..\.. + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - diff --git a/src/apps/orders/Genocs.Orders.WebApi/Program.cs b/src/apps/orders/Genocs.Orders.WebApi/Program.cs index bb23d735..bc5ed90f 100644 --- a/src/apps/orders/Genocs.Orders.WebApi/Program.cs +++ b/src/apps/orders/Genocs.Orders.WebApi/Program.cs @@ -10,6 +10,7 @@ using Genocs.MessageBrokers.Outbox; using Genocs.MessageBrokers.Outbox.MongoDB; using Genocs.MessageBrokers.RabbitMQ; +using Genocs.Metrics.AppMetrics; using Genocs.Metrics.Prometheus; using Genocs.Orders.WebApi; using Genocs.Orders.WebApi.Commands; @@ -21,24 +22,14 @@ using Genocs.Persistence.Redis; using Genocs.Secrets.Vault; using Genocs.Tracing; -using Genocs.Tracing.Jaeger; -using Genocs.Tracing.Jaeger.RabbitMQ; using Genocs.WebApi; using Genocs.WebApi.CQRS; using Genocs.WebApi.Security; using Genocs.WebApi.Swagger; using Genocs.WebApi.Swagger.Docs; using Serilog; -using Serilog.Events; - -Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .MinimumLevel.Override("Microsoft", LogEventLevel.Information) - .MinimumLevel.Override("MassTransit", LogEventLevel.Information) - .Enrich.FromLogContext() - .WriteTo.Console() - .CreateLogger(); +StaticLogger.EnsureInitialized(); var builder = WebApplication.CreateBuilder(args); @@ -46,6 +37,12 @@ .UseLogging() .UseVault(); +builder.Logging.AddOpenTelemetry(logging => +{ + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; +}); + var services = builder.Services; services.AddGenocs() @@ -56,7 +53,7 @@ .AddConsul() .AddFabio() .AddOpenTelemetry() - .AddJaeger() + .AddMetrics() .AddMongo() .AddMongoRepository("orders") .AddCommandHandlers() @@ -67,7 +64,7 @@ .AddInMemoryQueryDispatcher() .AddPrometheus() .AddRedis() - .AddRabbitMq(plugins: p => p.AddJaegerRabbitMqPlugin()) + .AddRabbitMq() .AddMessageOutbox(o => o.AddMongo()) .AddWebApi() .AddSwaggerDocs() @@ -84,17 +81,14 @@ .UseCertificateAuthentication() .UseEndpoints(r => r.MapControllers()) .UseDispatcherEndpoints(endpoints => endpoints - .Get("", ctx => ctx.Response.WriteAsync("Orders Service")) + .Get(string.Empty, ctx => ctx.Response.WriteAsync("Orders Service")) .Get("ping", ctx => ctx.Response.WriteAsync("pong")) .Get("orders/{orderId}") - .Post("orders", - afterDispatch: (cmd, ctx) => ctx.Response.Created($"orders/{cmd.OrderId}"))) - .UseJaeger() + .Post("orders", afterDispatch: (cmd, ctx) => ctx.Response.Created($"orders/{cmd.OrderId}"))) .UseSwaggerDocs() .UseRabbitMq() .SubscribeEvent(); app.Run(); -Log.CloseAndFlush(); - +Log.CloseAndFlush(); \ No newline at end of file diff --git a/src/apps/orders/Genocs.Orders.WebApi/Queries/Handlers/GetOrderHandler.cs b/src/apps/orders/Genocs.Orders.WebApi/Queries/Handlers/GetOrderHandler.cs index d0f67e04..2efca4c6 100644 --- a/src/apps/orders/Genocs.Orders.WebApi/Queries/Handlers/GetOrderHandler.cs +++ b/src/apps/orders/Genocs.Orders.WebApi/Queries/Handlers/GetOrderHandler.cs @@ -1,24 +1,24 @@ using Genocs.Core.CQRS.Queries; using Genocs.Orders.WebApi.Domain; using Genocs.Orders.WebApi.DTO; -using Genocs.Persistence.MongoDb.Repositories; +using Genocs.Persistence.MongoDb.Domain.Repositories; namespace Genocs.Orders.WebApi.Queries.Handlers; public class GetOrderHandler : IQueryHandler { - private readonly IMongoRepository _repository; + private readonly IMongoDbBaseRepository _repository; - public GetOrderHandler(IMongoRepository repository) + public GetOrderHandler(IMongoDbBaseRepository repository) { _repository = repository; } /// - /// GetOrder query handler + /// GetOrder query handler. /// - /// The query - /// The cancellation token + /// The query. + /// The cancellation token. /// public async Task HandleAsync(GetOrder query, CancellationToken cancellationToken = default) { diff --git a/src/apps/orders/Genocs.Orders.WebApi/Services/IProductServiceClient.cs b/src/apps/orders/Genocs.Orders.WebApi/Services/IProductServiceClient.cs index bd26538f..636fbf4a 100644 --- a/src/apps/orders/Genocs.Orders.WebApi/Services/IProductServiceClient.cs +++ b/src/apps/orders/Genocs.Orders.WebApi/Services/IProductServiceClient.cs @@ -3,14 +3,14 @@ namespace Genocs.Orders.WebApi.Services; /// -/// The Product WebApi client definition +/// The Product WebApi client definition. /// public interface IProductServiceClient { /// - /// Get the product based on the productId + /// Get the product based on the productId. /// - /// The ProductId - /// The Product Response + /// The ProductId. + /// The Product Response. Task GetAsync(Guid productId); } \ No newline at end of file diff --git a/src/apps/orders/Genocs.Orders.WebApi/Services/ProductServiceClient.cs b/src/apps/orders/Genocs.Orders.WebApi/Services/ProductServiceClient.cs index c31bcd90..4530716a 100644 --- a/src/apps/orders/Genocs.Orders.WebApi/Services/ProductServiceClient.cs +++ b/src/apps/orders/Genocs.Orders.WebApi/Services/ProductServiceClient.cs @@ -1,15 +1,14 @@ using Genocs.HTTP; -using Genocs.HTTP.Options; +using Genocs.HTTP.Configurations; using Genocs.Orders.WebApi.DTO; using Genocs.Secrets.Vault; -using Genocs.Secrets.Vault.Options; -using Genocs.WebApi.Security; +using Genocs.Secrets.Vault.Configurations; +using Genocs.WebApi.Security.Configurations; namespace Genocs.Orders.WebApi.Services; - /// -/// The Product WebApi client implementation +/// The Product WebApi client implementation. /// public class ProductServiceClient : IProductServiceClient { @@ -17,43 +16,69 @@ public class ProductServiceClient : IProductServiceClient private readonly string _url; /// - /// The standard constructor + /// The standard constructor. /// - /// - /// + /// The http client. + /// The certification service. /// /// /// - public ProductServiceClient(IHttpClient client, + public ProductServiceClient( + IHttpClient client, ICertificatesService certificatesService, - HttpClientSettings httpClientOptions, - VaultSettings vaultOptions, + HttpClientOptions httpClientOptions, + VaultOptions vaultOptions, SecurityOptions securityOptions) { - _client = client; - _url = httpClientOptions.Services["products"]; + _client = client ?? throw new ArgumentNullException(nameof(client)); + + + if (httpClientOptions is null) + { + throw new ArgumentNullException(nameof(httpClientOptions)); + } + + if (vaultOptions is null) + { + throw new ArgumentNullException(nameof(vaultOptions)); + } + + if (securityOptions is null) + { + throw new ArgumentNullException(nameof(securityOptions)); + } + + string? url = httpClientOptions?.Services?["products"]; + + if (string.IsNullOrWhiteSpace(url)) + { + throw new Exception("products http client option cannot be null"); + } + + _url = url; + if (!vaultOptions.Enabled || vaultOptions.Pki?.Enabled != true || securityOptions.Certificate?.Enabled != true) { return; } - var certificate = certificatesService.Get(vaultOptions.Pki.RoleName); + var certificate = certificatesService?.Get(vaultOptions.Pki.RoleName); if (certificate is null) { return; } - var header = securityOptions.Certificate.GetHeaderName(); - var certificateData = certificate.GetRawCertDataString(); + string header = securityOptions.Certificate.GetHeaderName(); + string certificateData = certificate.GetRawCertDataString(); _client.SetHeaders(h => h.Add(header, certificateData)); } /// - /// Get the product result based on the productId + /// Get the product result based on the productId. /// - /// The ProductId - /// The Product Response + /// The ProductId. + /// The Product Response. public Task GetAsync(Guid productId) => _client.GetAsync($"{_url}/products/{productId}"); } \ No newline at end of file diff --git a/src/apps/orders/Genocs.Orders.WebApi/appsettings.json b/src/apps/orders/Genocs.Orders.WebApi/appsettings.json index aae8e655..36916f8e 100644 --- a/src/apps/orders/Genocs.Orders.WebApi/appsettings.json +++ b/src/apps/orders/Genocs.Orders.WebApi/appsettings.json @@ -32,10 +32,10 @@ }, "logger": { "applicationName": "orders-service", - "excludePaths": ["/ping", "/metrics"], + "excludePaths": [ "/ping", "/metrics" ], "level": "information", "console": { - "enabled": true + "enabled": false }, "file": { "enabled": true, @@ -51,16 +51,19 @@ "jaeger": { "enabled": true, "serviceName": "orders", - "udpHost": "localhost", - "udpPort": 6831, - "maxPacketSize": 0, - "sampler": "const", - "excludePaths": [ "/ping", "/metrics" ] + "endpoint": "http://localhost:4317", + "protocol": "Grpc", + "processorType": "Batch", + "maxQueueSize": 2048, + "scheduledDelayMilliseconds": 5000, + "exporterTimeoutMilliseconds": 30000, + "maxExportBatchSize": 512 }, "metrics": { "enabled": true, "influxEnabled": false, - "prometheusEnabled": true, + "prometheusEnabled": false, + "prometheusFormatter": null, "influxUrl": "http://localhost:8086", "database": "test", "env": "local", @@ -91,7 +94,8 @@ "virtualHost": "/", "port": 5672, "hostnames": [ - "localhost", "rabbitmq" + "localhost", + "rabbitmq" ], "requestedConnectionTimeout": "00:00:30", "requestedHeartbeat": "00:01:00", @@ -129,10 +133,33 @@ "swagger": { "enabled": true, "reDocEnabled": false, - "name": "v1", + "name": "Orders", "title": "Orders Service", - "version": "v1", - "routePrefix": "swagger" + "version": "v002", + "description": "Orders Service", + "routePrefix": "swagger", + "contactName": "Giovanni Nocco", + "contactEmail": "giovanni.nocco@genocs.com", + "contactUrl": "https://www.genocs.com", + "licenseName": "MIT", + "licenseUrl": "https://www.genocs.com/license.html", + "termsOfService": "https://www.genocs.com/terms_and_conditions.html", + "includeSecurity": true, + "serializeAsOpenApiV2": true, + "servers": [ + { + "url": "http://localhost:5300", + "description": "Local version to be used for development" + }, + { + "url": "http://fiscanner-api", + "description": "Containerized version to be used into with docker or k8s" + }, + { + "url": "https://fiscanner-api.azurewebsites.net", + "description": "Production deployed on Azure" + } + ] }, "redis": { "connectionString": "localhost", @@ -142,7 +169,7 @@ "certificate": { "enabled": true, "header": "Certificate", - "allowedHosts": ["localhost", "order"] + "allowedHosts": [ "localhost", "order" ] } }, "vault": { diff --git a/src/apps/product-webapi.dockerfile b/src/apps/product-webapi.dockerfile index c54eb404..ee7efb2f 100644 --- a/src/apps/product-webapi.dockerfile +++ b/src/apps/product-webapi.dockerfile @@ -1,20 +1,11 @@ #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. - -#FROM mcr.microsoft.com/dotnet/core/aspnet:3.0 -#FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim -#FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim -#FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine -FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 -#FROM mcr.microsoft.com/dotnet/core/sdk:3.0 AS build-env -#FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build-env -# FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build-env -#FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build-env -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env WORKDIR /src COPY ["products/Genocs.Products.WebApi", "Genocs.Products.WebApi/"] diff --git a/src/apps/products/Genocs.Products.WebApi/Commands/CreateProduct.cs b/src/apps/products/Genocs.Products.WebApi/Commands/CreateProduct.cs index b8857be4..4f101e6c 100644 --- a/src/apps/products/Genocs.Products.WebApi/Commands/CreateProduct.cs +++ b/src/apps/products/Genocs.Products.WebApi/Commands/CreateProduct.cs @@ -8,7 +8,6 @@ public class CreateProduct : ICommand public string SKU { get; } public decimal UnitPrice { get; } - public CreateProduct(Guid productId, string sku, decimal unitPrice) { ProductId = productId == Guid.Empty ? Guid.NewGuid() : productId; diff --git a/src/apps/products/Genocs.Products.WebApi/Commands/Handlers/CreateProductHandler.cs b/src/apps/products/Genocs.Products.WebApi/Commands/Handlers/CreateProductHandler.cs index 8328a404..51ac3aec 100644 --- a/src/apps/products/Genocs.Products.WebApi/Commands/Handlers/CreateProductHandler.cs +++ b/src/apps/products/Genocs.Products.WebApi/Commands/Handlers/CreateProductHandler.cs @@ -1,35 +1,34 @@ using Genocs.Core.CQRS.Commands; using Genocs.MessageBrokers; using Genocs.MessageBrokers.Outbox; -using Genocs.Persistence.MongoDb.Repositories; +using Genocs.Persistence.MongoDb.Domain.Repositories; using Genocs.Products.WebApi.Domain; using Genocs.Products.WebApi.Events; -using OpenTracing; namespace Genocs.Products.WebApi.Commands.Handlers; public class CreateProductHandler : ICommandHandler { - private readonly IMongoRepository _repository; + private readonly IMongoDbBaseRepository _repository; private readonly IBusPublisher _publisher; private readonly IMessageOutbox _outbox; private readonly ILogger _logger; - private readonly ITracer _tracer; - public CreateProductHandler(IMongoRepository repository, IBusPublisher publisher, - IMessageOutbox outbox, ITracer tracer, - ILogger logger) + public CreateProductHandler( + IMongoDbBaseRepository repository, + IBusPublisher publisher, + IMessageOutbox outbox, + ILogger logger) { _repository = repository; _publisher = publisher; _outbox = outbox; - _tracer = tracer; _logger = logger; } public async Task HandleAsync(CreateProduct command, CancellationToken cancellationToken = default) { - var exists = await _repository.ExistsAsync(o => o.Id == command.ProductId); + bool exists = await _repository.ExistsAsync(o => o.Id == command.ProductId); if (exists) { throw new InvalidOperationException($"Product with given id: {command.ProductId} already exists!"); @@ -40,7 +39,7 @@ public async Task HandleAsync(CreateProduct command, CancellationToken cancellat _logger.LogInformation($"Created a product with id: {command.ProductId}, sku: {command.SKU}, unitPrice: {command.UnitPrice}."); - var spanContext = _tracer.ActiveSpan?.Context.ToString(); + string? spanContext = "TODO: Genocs"; var @event = new ProductCreated(product.Id); if (_outbox.Enabled) { diff --git a/src/apps/products/Genocs.Products.WebApi/Domain/Product.cs b/src/apps/products/Genocs.Products.WebApi/Domain/Product.cs index 65f3dbec..087a076f 100644 --- a/src/apps/products/Genocs.Products.WebApi/Domain/Product.cs +++ b/src/apps/products/Genocs.Products.WebApi/Domain/Product.cs @@ -1,12 +1,11 @@ -using Genocs.Common.Types; +using Genocs.Core.Domain.Entities; namespace Genocs.Products.WebApi.Domain; - /// -/// The product definition +/// The product definition. /// -public class Product : IIdentifiable +public class Product : IEntity { public Guid Id { get; private set; } public string SKU { get; private set; } diff --git a/src/apps/products/Genocs.Products.WebApi/Extensions.cs b/src/apps/products/Genocs.Products.WebApi/Extensions.cs index d96f63f6..d9119d6b 100644 --- a/src/apps/products/Genocs.Products.WebApi/Extensions.cs +++ b/src/apps/products/Genocs.Products.WebApi/Extensions.cs @@ -8,6 +8,7 @@ public static class Extensions public static IGenocsBuilder AddServices(this IGenocsBuilder builder) { builder.AddCertificateAuthentication(); + // Add here services like API client // builder.Services.AddSingleton(); return builder; diff --git a/src/apps/products/Genocs.Products.WebApi/Genocs.Products.WebApi.csproj b/src/apps/products/Genocs.Products.WebApi/Genocs.Products.WebApi.csproj index f560c95f..499fbed1 100644 --- a/src/apps/products/Genocs.Products.WebApi/Genocs.Products.WebApi.csproj +++ b/src/apps/products/Genocs.Products.WebApi/Genocs.Products.WebApi.csproj @@ -1,56 +1,56 @@  - - net7.0 - enable - enable - _genocs - Linux - ..\.. - + + net8.0 + false + false + _genocs + Linux + ..\.. + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - + + + + diff --git a/src/apps/products/Genocs.Products.WebApi/Program.cs b/src/apps/products/Genocs.Products.WebApi/Program.cs index 1000f458..ff5381d3 100644 --- a/src/apps/products/Genocs.Products.WebApi/Program.cs +++ b/src/apps/products/Genocs.Products.WebApi/Program.cs @@ -9,6 +9,7 @@ using Genocs.MessageBrokers.Outbox; using Genocs.MessageBrokers.Outbox.MongoDB; using Genocs.MessageBrokers.RabbitMQ; +using Genocs.Metrics.AppMetrics; using Genocs.Metrics.Prometheus; using Genocs.Persistence.MongoDb.Extensions; using Genocs.Persistence.Redis; @@ -19,24 +20,14 @@ using Genocs.Products.WebApi.Queries; using Genocs.Secrets.Vault; using Genocs.Tracing; -using Genocs.Tracing.Jaeger; -using Genocs.Tracing.Jaeger.RabbitMQ; using Genocs.WebApi; using Genocs.WebApi.CQRS; using Genocs.WebApi.Security; using Genocs.WebApi.Swagger; using Genocs.WebApi.Swagger.Docs; using Serilog; -using Serilog.Events; - -Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .MinimumLevel.Override("Microsoft", LogEventLevel.Information) - .MinimumLevel.Override("MassTransit", LogEventLevel.Information) - .Enrich.FromLogContext() - .WriteTo.Console() - .CreateLogger(); +StaticLogger.EnsureInitialized(); var builder = WebApplication.CreateBuilder(args); @@ -46,6 +37,12 @@ var services = builder.Services; +builder.Logging.AddOpenTelemetry(logging => +{ + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; +}); + services.AddGenocs() .AddErrorHandler() .AddServices() @@ -54,7 +51,7 @@ .AddConsul() .AddFabio() .AddOpenTelemetry() - .AddJaeger() + .AddMetrics() .AddMongo() .AddMongoRepository("products") .AddCommandHandlers() @@ -65,7 +62,7 @@ .AddInMemoryQueryDispatcher() .AddPrometheus() .AddRedis() - .AddRabbitMq(plugins: p => p.AddJaegerRabbitMqPlugin()) + .AddRabbitMq() .AddMessageOutbox(o => o.AddMongo()) .AddWebApi() .AddSwaggerDocs() @@ -82,17 +79,14 @@ .UseCertificateAuthentication() .UseEndpoints(r => r.MapControllers()) .UseDispatcherEndpoints(endpoints => endpoints - .Get("", ctx => ctx.Response.WriteAsync("Products Service")) + .Get(string.Empty, ctx => ctx.Response.WriteAsync("Products Service")) .Get("ping", ctx => ctx.Response.WriteAsync("pong")) .Get>("products") .Get("products/{productId}") - .Post("products", - afterDispatch: (cmd, ctx) => ctx.Response.Created($"products/{cmd.ProductId}"))) - .UseJaeger() + .Post("products", afterDispatch: (cmd, ctx) => ctx.Response.Created($"products/{cmd.ProductId}"))) .UseSwaggerDocs() .UseRabbitMq(); app.Run(); -Log.CloseAndFlush(); - +Log.CloseAndFlush(); \ No newline at end of file diff --git a/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/BrowseProductsHandler.cs b/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/BrowseProductsHandler.cs index 2f410736..5ce5f85a 100644 --- a/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/BrowseProductsHandler.cs +++ b/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/BrowseProductsHandler.cs @@ -4,7 +4,6 @@ using Genocs.Products.WebApi.DTO; using MongoDB.Driver; - namespace Genocs.Products.WebApi.Queries.Handlers; public class BrowseProductsHandler : IQueryHandler> @@ -25,7 +24,6 @@ public BrowseProductsHandler(IMongoDatabase database) var pagedResult = PagedResult.From(result, result.Items.Select(x => Map(x))); return pagedResult; - } private static ProductDto Map(Product product) diff --git a/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/GetProductHandler.cs b/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/GetProductHandler.cs index 9414e599..2fae624e 100644 --- a/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/GetProductHandler.cs +++ b/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/GetProductHandler.cs @@ -1,14 +1,14 @@ using Genocs.Core.CQRS.Queries; -using Genocs.Persistence.MongoDb.Repositories; +using Genocs.Persistence.MongoDb.Domain.Repositories; using Genocs.Products.WebApi.DTO; namespace Genocs.Products.WebApi.Queries.Handlers; public class GetProductHandler : IQueryHandler { - private readonly IMongoRepository _repository; + private readonly IMongoDbBaseRepository _repository; - public GetProductHandler(IMongoRepository repository) + public GetProductHandler(IMongoDbBaseRepository repository) { _repository = repository; } diff --git a/src/apps/products/Genocs.Products.WebApi/appsettings.Docker.json b/src/apps/products/Genocs.Products.WebApi/appsettings.Docker.json index 639155ea..b68c544e 100644 --- a/src/apps/products/Genocs.Products.WebApi/appsettings.Docker.json +++ b/src/apps/products/Genocs.Products.WebApi/appsettings.Docker.json @@ -8,7 +8,7 @@ }, "fabio": { "enabled": true, - "url": "http://localhost:9999", + "url": "http://fabio:9999", "service": "products-service" }, "logger": { @@ -30,7 +30,7 @@ "influxEnabled": true, "prometheusEnabled": true, "influxUrl": "http://influxdb:8086", - "env" :"docker" + "env": "docker" }, "prometheus": { "enabled": true diff --git a/src/apps/products/Genocs.Products.WebApi/appsettings.json b/src/apps/products/Genocs.Products.WebApi/appsettings.json index 0e4f6b37..b8b59a9f 100644 --- a/src/apps/products/Genocs.Products.WebApi/appsettings.json +++ b/src/apps/products/Genocs.Products.WebApi/appsettings.json @@ -11,7 +11,7 @@ "enabled": false, "url": "http://localhost:8500", "service": "products-service", - "address": "docker.for.win.localhost", + "address": "localhost", "port": "5002", "pingEnabled": true, "pingEndpoint": "ping", @@ -21,7 +21,8 @@ "fabio": { "enabled": false, "url": "http://localhost:9999", - "service": "products-service" + "service": "products-service", + "requestRetries": 3 }, "httpClient": { "type": "", @@ -30,9 +31,9 @@ } }, "logger": { + "level": "information", "applicationName": "products-service", "excludePaths": [ "/ping", "/metrics" ], - "level": "information", "console": { "enabled": true }, @@ -59,7 +60,8 @@ "metrics": { "enabled": true, "influxEnabled": false, - "prometheusEnabled": true, + "prometheusEnabled": false, + "prometheusFormatter": null, "influxUrl": "http://localhost:8086", "database": "test", "env": "local", @@ -69,7 +71,7 @@ "connectionString": "mongodb://localhost:27017", "database": "products-service", "seed": false, - "enableTracing" : true + "enableTracing": true }, "prometheus": { "enabled": false @@ -90,7 +92,8 @@ "virtualHost": "/", "port": 5672, "hostnames": [ - "localhost", "rabbitmq" + "localhost", + "rabbitmq" ], "requestedConnectionTimeout": "00:00:30", "requestedHeartbeat": "00:01:00", @@ -141,7 +144,7 @@ "certificate": { "enabled": true, "header": "Certificate", - "allowedHosts": [ "localhost" ,"product"] + "allowedHosts": [ "localhost", "product" ] } }, "vault": { diff --git a/src/apps/signalr-webapi.dockerfile b/src/apps/signalr-webapi.dockerfile index 8fd57672..a7dd7061 100644 --- a/src/apps/signalr-webapi.dockerfile +++ b/src/apps/signalr-webapi.dockerfile @@ -1,20 +1,24 @@ #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. -#FROM mcr.microsoft.com/dotnet/core/aspnet:3.0 -#FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim -#FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim -#FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine -FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base +# FROM mcr.microsoft.com/dotnet/core/aspnet:3.0 +# FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim +# FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim +# FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine +# FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base + WORKDIR /app EXPOSE 80 EXPOSE 443 -#FROM mcr.microsoft.com/dotnet/core/sdk:3.0 AS build-env -#FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build-env +# FROM mcr.microsoft.com/dotnet/core/sdk:3.0 AS build-env +# FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build-env # FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build-env -#FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build-env -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env +# FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build-env +# FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env + WORKDIR /src COPY ["signalr/Genocs.SignalR.WebApi", "Genocs.SignalR.WebApi/"] diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Commands/Handlers/PublishNotificationHandler.cs b/src/apps/signalr/Genocs.SignalR.WebApi/Commands/Handlers/PublishNotificationHandler.cs index c7413bf9..fc1d87c8 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Commands/Handlers/PublishNotificationHandler.cs +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Commands/Handlers/PublishNotificationHandler.cs @@ -4,7 +4,6 @@ using Genocs.SignalR.WebApi.Events; using Genocs.SignalR.WebApi.Hubs; using Microsoft.AspNetCore.SignalR; -using OpenTracing; namespace Genocs.SignalR.WebApi.Commands.Handlers; @@ -13,18 +12,17 @@ public class PublishNotificationHandler : ICommandHandler private readonly IBusPublisher _publisher; private readonly IMessageOutbox _outbox; private readonly ILogger _logger; - private readonly ITracer _tracer; + private readonly IHubContext _hub; - public PublishNotificationHandler(IBusPublisher publisher, + public PublishNotificationHandler( + IBusPublisher publisher, IMessageOutbox outbox, - ITracer tracer, ILogger logger, IHubContext hub) { _publisher = publisher ?? throw new ArgumentNullException(nameof(publisher)); _outbox = outbox ?? throw new ArgumentNullException(nameof(outbox)); - _tracer = tracer ?? throw new ArgumentNullException(nameof(tracer)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _hub = hub ?? throw new ArgumentNullException(nameof(hub)); } @@ -32,7 +30,7 @@ public PublishNotificationHandler(IBusPublisher publisher, public async Task HandleAsync(PublishNotification command, CancellationToken cancellationToken = default) { _logger.LogInformation($"Created a notification with id: {command.NotificationId}, customer: {command.CustomerId}."); - var spanContext = _tracer.ActiveSpan?.Context.ToString(); + string? spanContext = "TODO: Genocs"; var @event = new NotificationPosted(command.NotificationId); // Send the notification diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Commands/PublishNotification.cs b/src/apps/signalr/Genocs.SignalR.WebApi/Commands/PublishNotification.cs index a43fe802..1155fbe9 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Commands/PublishNotification.cs +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Commands/PublishNotification.cs @@ -8,7 +8,6 @@ public class PublishNotification : ICommand public Guid CustomerId { get; } public string Message { get; } - public PublishNotification(Guid notificationId, Guid customerId, string message) { NotificationId = notificationId == Guid.Empty ? Guid.NewGuid() : notificationId; diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Configurations/SignalROptions.cs b/src/apps/signalr/Genocs.SignalR.WebApi/Configurations/SignalROptions.cs new file mode 100644 index 00000000..b0dfd4f1 --- /dev/null +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Configurations/SignalROptions.cs @@ -0,0 +1,20 @@ +namespace Genocs.SignalR.WebApi.Configurations; + +/// +/// The signalR settings definition. +/// +public class SignalROptions +{ + /// + /// Default section name. + /// + public const string Position = "signalR"; + + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + public string? Backplane { get; set; } + public string? Hub { get; set; } +} \ No newline at end of file diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Events/NotificationPosted.cs b/src/apps/signalr/Genocs.SignalR.WebApi/Events/NotificationPosted.cs index 105eebca..78d68fa8 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Events/NotificationPosted.cs +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Events/NotificationPosted.cs @@ -5,7 +5,6 @@ public class NotificationPosted public Guid NotificationId { get; } - public NotificationPosted(Guid notificationId) { NotificationId = notificationId; diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Exceptions/AppException.cs b/src/apps/signalr/Genocs.SignalR.WebApi/Exceptions/AppException.cs index c2c32bfd..c4bae6e2 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Exceptions/AppException.cs +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Exceptions/AppException.cs @@ -2,7 +2,8 @@ public abstract class AppException : Exception { - protected AppException(string message) : base(message) + protected AppException(string message) + : base(message) { } } diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Exceptions/ExceptionToResponseMapper.cs b/src/apps/signalr/Genocs.SignalR.WebApi/Exceptions/ExceptionToResponseMapper.cs index defa89b8..e8b11061 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Exceptions/ExceptionToResponseMapper.cs +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Exceptions/ExceptionToResponseMapper.cs @@ -12,24 +12,25 @@ public class ExceptionToResponseMapper : IExceptionToResponseMapper public ExceptionResponse Map(Exception exception) => exception switch { - //DomainException ex => new ExceptionResponse(new { code = GetCode(ex), reason = ex.Message }, + // DomainException ex => new ExceptionResponse(new { code = GetCode(ex), reason = ex.Message }, // HttpStatusCode.BadRequest), - AppException ex => new ExceptionResponse(new { code = GetCode(ex), reason = ex.Message }, - HttpStatusCode.BadRequest), - _ => new ExceptionResponse(new { code = "error", reason = "There was an error." }, - HttpStatusCode.BadRequest) + AppException ex => new ExceptionResponse(new { code = GetCode(ex), reason = ex.Message }, HttpStatusCode.BadRequest), + _ => new ExceptionResponse(new { code = "error", reason = "There was an error." }, HttpStatusCode.BadRequest) }; - private static string GetCode(Exception exception) + private static string? GetCode(Exception exception) { var type = exception.GetType(); - if (Codes.TryGetValue(type, out var code)) + if (Codes.TryGetValue(type, out string? code)) { return code; } - var exceptionCode = exception.GetType().Name.Underscore().Replace("_exception", string.Empty); - Codes.TryAdd(type, exceptionCode); + string? exceptionCode = exception.GetType().Name.Underscore()?.Replace("_exception", string.Empty); + if (!string.IsNullOrWhiteSpace(exceptionCode)) + { + Codes.TryAdd(type, exceptionCode); + } return exceptionCode; } diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Framework/Extensions.cs b/src/apps/signalr/Genocs.SignalR.WebApi/Framework/Extensions.cs index 6196760c..4a8d6756 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Framework/Extensions.cs +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Framework/Extensions.cs @@ -1,5 +1,5 @@ using Genocs.Core.Builders; -using Genocs.SignalR.WebApi.Options; +using Genocs.SignalR.WebApi.Configurations; namespace Genocs.SignalR.WebApi.Framework; @@ -10,7 +10,7 @@ public static string ToUserGroup(this Guid userId) public static IGenocsBuilder AddSignalR(this IGenocsBuilder builder) { - var options = builder.Configuration.GetOptions("signalr"); + var options = builder.Configuration.GetOptions("signalr"); if (options is not null) { @@ -20,15 +20,14 @@ public static IGenocsBuilder AddSignalR(this IGenocsBuilder builder) return builder; } - - public static IApplicationBuilder UseSignalR(this IApplicationBuilder builder) + public static IGenocsBuilder UseSignalR(this IGenocsBuilder builder) { - // var options = builder.GetOptions("signalr"); + var options = builder.GetOptions("signalr"); - // if (options is not null) - // { - // builder.Services.AddSingleton(options); - // } + if (options is not null) + { + builder.Services.AddSingleton(options); + } return builder; } diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Genocs.SignalR.WebApi.csproj b/src/apps/signalr/Genocs.SignalR.WebApi/Genocs.SignalR.WebApi.csproj index eec9a627..6409fb9b 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Genocs.SignalR.WebApi.csproj +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Genocs.SignalR.WebApi.csproj @@ -1,56 +1,56 @@  - - net7.0 - enable - enable - _genocs - Linux - ..\.. - + + + net8.0 + false + false + _genocs + Linux + ..\.. + - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + \ No newline at end of file diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Handlers/OperationUpdatedHandler.cs b/src/apps/signalr/Genocs.SignalR.WebApi/Handlers/OperationUpdatedHandler.cs index 0d0f5f10..f1870b46 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Handlers/OperationUpdatedHandler.cs +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Handlers/OperationUpdatedHandler.cs @@ -9,7 +9,7 @@ public class OperationUpdatedHandler : IEventHandler, { private readonly IHubService _hubService; - public OperationUpdatedHandler(IHubService hubService) + public OperationUpdatedHandler(IHubService hubService) => _hubService = hubService ?? throw new ArgumentNullException(nameof(hubService)); public async Task HandleAsync(OperationPending @event, CancellationToken cancellationToken = default) @@ -21,5 +21,4 @@ public async Task HandleAsync(OperationCompleted @event, CancellationToken cance public async Task HandleAsync(OperationRejected @event, CancellationToken cancellationToken = default) => await _hubService.PublishOperationRejectedAsync(@event); - } \ No newline at end of file diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Hubs/GenocsHub.cs b/src/apps/signalr/Genocs.SignalR.WebApi/Hubs/GenocsHub.cs index d8ae21eb..7567b642 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Hubs/GenocsHub.cs +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Hubs/GenocsHub.cs @@ -27,7 +27,7 @@ public async Task InitializeAsync(string token) return; } - var group = Guid.Parse(payload.Subject).ToUserGroup(); + string group = Guid.Parse(payload.Subject).ToUserGroup(); await Groups.AddToGroupAsync(Context.ConnectionId, group); await ConnectAsync(); } diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Messages/Events/OperationCompleted.cs b/src/apps/signalr/Genocs.SignalR.WebApi/Messages/Events/OperationCompleted.cs index e4e5fcc5..66c49b9a 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Messages/Events/OperationCompleted.cs +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Messages/Events/OperationCompleted.cs @@ -13,8 +13,11 @@ public class OperationCompleted : IEvent public string Resource { get; } [JsonConstructor] - public OperationCompleted(Guid id, - Guid userId, string name, string resource) + public OperationCompleted( + Guid id, + Guid userId, + string name, + string resource) { Id = id; UserId = userId; diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Messages/Events/OperationPending.cs b/src/apps/signalr/Genocs.SignalR.WebApi/Messages/Events/OperationPending.cs index b98d0206..53848460 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Messages/Events/OperationPending.cs +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Messages/Events/OperationPending.cs @@ -13,8 +13,11 @@ public class OperationPending : IEvent public string Resource { get; } [JsonConstructor] - public OperationPending(Guid id, - Guid userId, string name, string resource) + public OperationPending( + Guid id, + Guid userId, + string name, + string resource) { Id = id; UserId = userId; diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Messages/Events/OperationRejected.cs b/src/apps/signalr/Genocs.SignalR.WebApi/Messages/Events/OperationRejected.cs index 749ac634..f6055ad6 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Messages/Events/OperationRejected.cs +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Messages/Events/OperationRejected.cs @@ -15,9 +15,13 @@ public class OperationRejected : IEvent public string Message { get; } [JsonConstructor] - public OperationRejected(Guid id, - Guid userId, string name, string resource, - string code, string message) + public OperationRejected( + Guid id, + Guid userId, + string name, + string resource, + string code, + string message) { Id = id; UserId = userId; diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Options/SignalRSettings.cs b/src/apps/signalr/Genocs.SignalR.WebApi/Options/SignalRSettings.cs deleted file mode 100644 index 1786da94..00000000 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Options/SignalRSettings.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Genocs.SignalR.WebApi.Options; - -/// -/// The signalR Settings definition -/// -public class SignalRSettings -{ - public string? Backplane { get; set; } - public string? Hub { get; set; } -} \ No newline at end of file diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Program.cs b/src/apps/signalr/Genocs.SignalR.WebApi/Program.cs index eb7b9a00..b0406060 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Program.cs +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Program.cs @@ -7,6 +7,7 @@ using Genocs.MessageBrokers.Outbox; using Genocs.MessageBrokers.Outbox.MongoDB; using Genocs.MessageBrokers.RabbitMQ; +using Genocs.Metrics.AppMetrics; using Genocs.Persistence.MongoDb.Extensions; using Genocs.Secrets.Vault; using Genocs.SignalR.WebApi.Commands; @@ -21,16 +22,8 @@ using Genocs.WebApi.Swagger; using Genocs.WebApi.Swagger.Docs; using Serilog; -using Serilog.Events; - -Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .MinimumLevel.Override("Microsoft", LogEventLevel.Information) - .MinimumLevel.Override("MassTransit", LogEventLevel.Information) - .Enrich.FromLogContext() - .WriteTo.Console() - .CreateLogger(); +StaticLogger.EnsureInitialized(); var builder = WebApplication.CreateBuilder(args); @@ -50,7 +43,7 @@ .AddJwt() .AddErrorHandler() .AddOpenTelemetry() - .AddJaeger() + .AddMetrics() .AddMongo() .AddCommandHandlers() .AddEventHandlers() @@ -67,26 +60,22 @@ var app = builder.Build(); - app.UseGenocs() .UserCorrelationContextLogging() .UseErrorHandler() .UseRouting() - .UseEndpoints(r => { + .UseEndpoints(r => + { r.MapControllers(); r.MapHub("/notificationHub"); }) .UseDispatcherEndpoints(endpoints => endpoints .Get("", ctx => ctx.Response.WriteAsync("SignalR Service")) .Get("ping", ctx => ctx.Response.WriteAsync("pong")) - .Post("notifications", - afterDispatch: (cmd, ctx) => ctx.Response.Created($"notifications/{cmd.NotificationId}")) - ) - .UseJaeger() + .Post("notifications", afterDispatch: (cmd, ctx) => ctx.Response.Created($"notifications/{cmd.NotificationId}"))) .UseSwaggerDocs() .UseRabbitMq(); - app.UseHttpsRedirection(); app.UseStaticFiles(); diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Services/HubService.cs b/src/apps/signalr/Genocs.SignalR.WebApi/Services/HubService.cs index e81fd8f7..99ae7aeb 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Services/HubService.cs +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Services/HubService.cs @@ -6,42 +6,41 @@ public class HubService : IHubService { private readonly IHubWrapper _hubContextWrapper; - public HubService(IHubWrapper hubContextWrapper) + public HubService(IHubWrapper hubContextWrapper) => _hubContextWrapper = hubContextWrapper ?? throw new ArgumentNullException(nameof(hubContextWrapper)); - public async Task PublishOperationPendingAsync(OperationPending @event) - => await _hubContextWrapper.PublishToUserAsync(@event.UserId, - "operation_pending", - new - { - id = @event.Id, - name = @event.Name, - resource = @event.Resource - } - ); + => await _hubContextWrapper.PublishToUserAsync( + @event.UserId, + "operation_pending", + new + { + id = @event.Id, + name = @event.Name, + resource = @event.Resource + }); public async Task PublishOperationCompletedAsync(OperationCompleted @event) - => await _hubContextWrapper.PublishToUserAsync(@event.UserId, - "operation_completed", - new - { - id = @event.Id, - name = @event.Name, - resource = @event.Resource - } - ); + => await _hubContextWrapper.PublishToUserAsync( + @event.UserId, + "operation_completed", + new + { + id = @event.Id, + name = @event.Name, + resource = @event.Resource + }); public async Task PublishOperationRejectedAsync(OperationRejected @event) - => await _hubContextWrapper.PublishToUserAsync(@event.UserId, - "operation_rejected", - new - { - id = @event.Id, - name = @event.Name, - resource = @event.Resource, - code = @event.Code, - reason = @event.Message - } - ); + => await _hubContextWrapper.PublishToUserAsync( + @event.UserId, + "operation_rejected", + new + { + id = @event.Id, + name = @event.Name, + resource = @event.Resource, + code = @event.Code, + reason = @event.Message + }); } \ No newline at end of file diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/appsettings.json b/src/apps/signalr/Genocs.SignalR.WebApi/appsettings.json index 85e29164..dead120e 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/appsettings.json +++ b/src/apps/signalr/Genocs.SignalR.WebApi/appsettings.json @@ -66,12 +66,12 @@ "expiry": "01:00:00" }, "logger": { + "level": "information", "applicationName": "signalr-service", "excludePaths": [ "/ping", "/metrics" ], - "level": "information", "console": { "enabled": true }, @@ -87,7 +87,7 @@ } }, "metrics": { - "enabled": false, + "enabled": true, "influxEnabled": false, "prometheusEnabled": false, "influxUrl": "http://localhost:8086", diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/wwwroot/signalr/js/appV2.js b/src/apps/signalr/Genocs.SignalR.WebApi/wwwroot/signalr/js/appV2.js index 1965d07c..488f0c63 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/wwwroot/signalr/js/appV2.js +++ b/src/apps/signalr/Genocs.SignalR.WebApi/wwwroot/signalr/js/appV2.js @@ -6,7 +6,7 @@ const $messages = document.getElementById("messages"); const connection = new signalR.HubConnectionBuilder() - .withUrl("http://localhost:5007/notificationHub") + .withUrl("https://localhost:5014/notificationHub") .configureLogging(signalR.LogLevel.Information) .build(); @@ -20,6 +20,7 @@ } }; + // Start the connection. $connect.onclick = function () { const jwt = $jwt.value; if (!jwt || /\s/g.test(jwt)) { @@ -30,7 +31,8 @@ appendMessage('Connecting to Genocs Hub...'); connection.start() .then(() => { - //connection.invoke('initializeAsync', $jwt.value); + console.log("SignalR Connected."); + connection.invoke('initializeAsync', $jwt.value); }) .catch(err => appendMessage(err)); } @@ -76,5 +78,5 @@ // Start the connection. - start(); + //start(); })(); \ No newline at end of file diff --git a/stylecop.json b/stylecop.json new file mode 100644 index 00000000..95ceebe8 --- /dev/null +++ b/stylecop.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "orderingRules": { + "systemUsingDirectivesFirst": true, + "usingDirectivesPlacement": "outsideNamespace" + }, + "layoutRules": { + "newlineAtEndOfFile": "omit" + } + } +} \ No newline at end of file diff --git a/templates/README.md b/templates/README.md deleted file mode 100644 index a2b1b571..00000000 --- a/templates/README.md +++ /dev/null @@ -1,30 +0,0 @@ -Genocs Library Template -=== - -This folder contains the templates to be used to implement an entire solution. - - -``` cmd -# Pack template with nuget -nuget pack WebApiTemplate.nuspec -NoDefaultExcludes -OutputDirectory .\nuget - - -# Get the template list -dotnet new list - -# Install with local nuget package -dotnet new install .\nuget\Genocs.ServiceTemplate.5.0.0.nupkg - -# Uninstall local nuget package -dotnet new --uninstall .\nuget\Genocs.ServiceTemplate.5.0.0.nupkg - -# Some uninstall commands -dotnet new uninstall Genocs.ServiceTemplate -dotnet new uninstall Genocs.CleanArchitecture -dotnet new uninstall Genocs.CleanArchitectureTemplate - -dotnet new uninstall Genocs.MicroserviceTemplate - -# Create a new WebApi called 'CommanyName.ServiceName' inside the folder folder -dotnet new gnx-webapi -n CommanyName.ServiceName -o folder -``` \ No newline at end of file diff --git a/templates/WebApiTemplate.nuspec b/templates/WebApiTemplate.nuspec deleted file mode 100644 index ceb176ff..00000000 --- a/templates/WebApiTemplate.nuspec +++ /dev/null @@ -1,70 +0,0 @@ - - - - Genocs.ServiceTemplate - 5.0.0 - Nocco Giovanni Emanuele - [Genocs] Nocco Giovanni Emanuele - https://github.com/Genocs/genocs-library - - MIT - icon.png - false - The service template by Genocs - - A .NET template that can be used in accordance with Genocs Library. - - -Install the package: -dotnet new -i Genocs.ServiceTemplate::5.0.0 - -The full clean project: -$ dotnet new gnx-service - - -- Relesase 5.0.0: - New version aligned to the library - -- Clean Architecture Principles Documentation -- .NET Core 7.0 -- Swashbuckle 5 -- Enterprice Service Bus - - - architecture - boilerplate - clean-architecture - clean-code - ddd - ddd-architecture - design-patterns - docker - domain-driven-design - dotnet - dotnetcore - dotnet-core - dotnet-cli - dotnet-new - dotnet-template - generator - layered - masstransit - microservice - microservices - rabbitmq - solid - solid-principles - templating - webapi - - en-US - - - - - - - - - - diff --git a/templates/icon.png b/templates/icon.png deleted file mode 100644 index 66a5cbe2..00000000 Binary files a/templates/icon.png and /dev/null differ diff --git a/templates/nuget.exe b/templates/nuget.exe deleted file mode 100644 index 0f83b17f..00000000 Binary files a/templates/nuget.exe and /dev/null differ diff --git a/templates/template/.dockerignore b/templates/template/.dockerignore deleted file mode 100644 index 3729ff0c..00000000 --- a/templates/template/.dockerignore +++ /dev/null @@ -1,25 +0,0 @@ -**/.classpath -**/.dockerignore -**/.env -**/.git -**/.gitignore -**/.project -**/.settings -**/.toolstarget -**/.vs -**/.vscode -**/*.*proj.user -**/*.dbmdl -**/*.jfm -**/azds.yaml -**/bin -**/charts -**/docker-compose* -**/Dockerfile* -**/node_modules -**/npm-debug.log -**/obj -**/secrets.dev.yaml -**/values.dev.yaml -LICENSE -README.md \ No newline at end of file diff --git a/templates/template/.editorconfig b/templates/template/.editorconfig deleted file mode 100644 index a5864573..00000000 --- a/templates/template/.editorconfig +++ /dev/null @@ -1,191 +0,0 @@ -# Rules in this file were initially inferred by Visual Studio IntelliCode from the C:\dev\genocs\clean-architecture-template\src\template codebase based on best match to current usage at 7/9/2021 -# You can modify the rules from these initially generated values to suit your own policies -# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference -[*.cs] - - -#Core editorconfig formatting - indentation - -#use soft tabs (spaces) for indentation -indent_style = space - -#Formatting - new line options - -#place catch statements on a new line -csharp_new_line_before_catch = true -#place else statements on a new line -csharp_new_line_before_else = true -#require members of anonymous types to be on separate lines -csharp_new_line_before_members_in_anonymous_types = true -#require members of object intializers to be on separate lines -csharp_new_line_before_members_in_object_initializers = true -#require braces to be on a new line for anonymous_types, methods, control_blocks, types, lambdas, and object_collection_array_initializers (also known as "Allman" style) -csharp_new_line_before_open_brace = anonymous_types, methods, control_blocks, types, lambdas, object_collection_array_initializers - -#Formatting - organize using options - -#do not place System.* using directives before other using directives -dotnet_sort_system_directives_first = false - -#Formatting - spacing options - -#require NO space between a cast and the value -csharp_space_after_cast = false -#require a space before the colon for bases or interfaces in a type declaration -csharp_space_after_colon_in_inheritance_clause = true -#require a space after a keyword in a control flow statement such as a for loop -csharp_space_after_keywords_in_control_flow_statements = true -#require a space before the colon for bases or interfaces in a type declaration -csharp_space_before_colon_in_inheritance_clause = true -#remove space within empty argument list parentheses -csharp_space_between_method_call_empty_parameter_list_parentheses = false -#remove space between method call name and opening parenthesis -csharp_space_between_method_call_name_and_opening_parenthesis = false -#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call -csharp_space_between_method_call_parameter_list_parentheses = false -#remove space within empty parameter list parentheses for a method declaration -csharp_space_between_method_declaration_empty_parameter_list_parentheses = false -#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list. -csharp_space_between_method_declaration_parameter_list_parentheses = false - -#Formatting - wrapping options - -#leave code block on single line -csharp_preserve_single_line_blocks = true - -#Style - Code block preferences - -#prefer curly braces even for one line of code -csharp_prefer_braces = true:suggestion - -#Style - expression bodied member options - -#prefer block bodies for constructors -csharp_style_expression_bodied_constructors = false:suggestion -#prefer expression-bodied members for methods -csharp_style_expression_bodied_methods = true:suggestion - -#Style - expression level options - -#prefer out variables to be declared inline in the argument list of a method call when possible -csharp_style_inlined_variable_declaration = true:suggestion -#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them -dotnet_style_predefined_type_for_member_access = true:suggestion - -#Style - Expression-level preferences - -#prefer objects to be initialized using object initializers when possible -dotnet_style_object_initializer = true:suggestion -#prefer inferred anonymous type member names -dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion - -#Style - implicit and explicit types - -#prefer var over explicit type in all cases, unless overridden by another code style rule -csharp_style_var_elsewhere = true:suggestion -#prefer explicit type over var to declare variables with built-in system types such as int -csharp_style_var_for_built_in_types = false:suggestion -#prefer explicit type over var when the type is already mentioned on the right-hand side of a declaration -csharp_style_var_when_type_is_apparent = false:suggestion - -#Style - language keyword and framework type options - -#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them -dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion - -#Style - Miscellaneous preferences - -#prefer anonymous functions over local functions -csharp_style_pattern_local_over_anonymous_function = false:suggestion - -#Style - modifier options - -#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods. -dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion - -#Style - Modifier preferences - -#when this rule is set to a list of modifiers, prefer the specified ordering. -csharp_preferred_modifier_order = public,private,protected,internal,readonly,async,static,sealed,virtual,override:suggestion - -#Style - Pattern matching - -#prefer pattern matching instead of is expression with type casts -csharp_style_pattern_matching_over_as_with_null_check = true:suggestion - -#Style - qualification options - -#prefer fields not to be prefaced with this. or Me. in Visual Basic -dotnet_style_qualification_for_field = false:suggestion -#prefer methods not to be prefaced with this. or Me. in Visual Basic -dotnet_style_qualification_for_method = false:suggestion -#prefer properties not to be prefaced with this. or Me. in Visual Basic -dotnet_style_qualification_for_property = false:suggestion -csharp_indent_labels = one_less_than_current -csharp_using_directive_placement = outside_namespace:silent -csharp_prefer_simple_using_statement = true:suggestion -csharp_style_namespace_declarations = block_scoped:silent -csharp_style_prefer_method_group_conversion = true:silent -csharp_style_prefer_top_level_statements = true:silent -csharp_style_expression_bodied_operators = false:silent -csharp_style_expression_bodied_properties = true:silent -csharp_style_expression_bodied_indexers = true:silent -csharp_style_expression_bodied_accessors = true:silent -csharp_style_expression_bodied_lambdas = true:silent -csharp_style_expression_bodied_local_functions = false:silent - -[*.{cs,vb}] -dotnet_style_operator_placement_when_wrapping = beginning_of_line -tab_width = 4 -indent_size = 4 -end_of_line = crlf -[*.{cs,vb}] -#### Naming styles #### - -# Naming rules - -dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion -dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface -dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i - -dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.types_should_be_pascal_case.symbols = types -dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case - -dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members -dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case - -# Symbol specifications - -dotnet_naming_symbols.interface.applicable_kinds = interface -dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.interface.required_modifiers = - -dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum -dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.types.required_modifiers = - -dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method -dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.non_field_members.required_modifiers = - -# Naming styles - -dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = -dotnet_naming_style.begins_with_i.capitalization = pascal_case - -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = -dotnet_naming_style.pascal_case.capitalization = pascal_case - -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = -dotnet_naming_style.pascal_case.capitalization = pascal_case -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion diff --git a/templates/template/.template.config/template.json b/templates/template/.template.config/template.json deleted file mode 100644 index 7e36c3d8..00000000 --- a/templates/template/.template.config/template.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/template", - "author": "Nocco Giovanni Emanuele", - "classifications": [ - "Web/ASP.NET" - ], - "name": "Genocs: Service Template for .NET Core", - "identity": "Genocs.ServiceTemplate", - "groupIdentity": "Genocs.ServiceTemplate", - "shortName": "gnx-webapi", - "tags": { - "language": "C#" // Specify that this template is in C#. - }, - "sourceName": "Genocs.Template", // Will replace the string 'Genocs.Template' with the value provided via -n. - "preferNameDirectory": true, - "symbols": { - "use-cases": { - "type": "parameter", - "datatype": "choice", - "defaultValue": "full", - "choices": [ - { - "choice": "full", - "description": "Full Wallet Account Balance Management (Read/Write)" - } - ], - "replaces": "use-cases", - "onlyIf": [ - { - "after": "\"use-cases\": \"" - } - ] - }, - "Full": { - "type": "computed", - "value": "(use-cases == \"full\")" - } - - }, - "sources": [ - { - "source": "./", - "modifiers": [ - { - "condition": "(Full)", - "exclude": [] - } - ] - } - ], - "primaryOutputs": [ - { - "path": "src/Genocs.Template.Application/Genocs.Template.Application.csproj" - }, - { - "path": "src/Genocs.Template.WebApi/Genocs.Template.WebApi.csproj" - } - ] -} \ No newline at end of file diff --git a/templates/template/.travis.yml b/templates/template/.travis.yml deleted file mode 100644 index ce1bee34..00000000 --- a/templates/template/.travis.yml +++ /dev/null @@ -1,20 +0,0 @@ -language: csharp -mono: none -sudo: required -dist: xenial -dotnet: 7.0.101 -branches: - only: - - master - - develop -before_script: - - chmod -R a+x scripts -script: - - ./scripts/build.sh - - ./scripts/test.sh -after_success: - #- ./scripts/dotnet-pack.sh -notifications: - email: - on_success: never - on_failure: always diff --git a/templates/template/Genocs.Template.sln b/templates/template/Genocs.Template.sln deleted file mode 100644 index 365e5cdf..00000000 --- a/templates/template/Genocs.Template.sln +++ /dev/null @@ -1,61 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.32112.339 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Genocs.Template.WebApi", "src\Genocs.Template.WebApi\Genocs.Template.WebApi.csproj", "{DE058D73-748F-4E31-BA5C-48DEF7C6BA41}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Genocs.Template.AcceptanceTests", "src\Genocs.Template.AcceptanceTests\Genocs.Template.AcceptanceTests.csproj", "{FF2A2975-6B15-49D0-982A-4020591FE745}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Genocs.Template.IntegrationTests", "src\Genocs.Template.IntegrationTests\Genocs.Template.IntegrationTests.csproj", "{252000C9-DCC7-4222-ACF0-A7DEA1727155}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Genocs.Template.UnitTests", "src\Genocs.Template.UnitTests\Genocs.Template.UnitTests.csproj", "{D5FDC379-F6F0-427F-9EC1-EC63DBC40183}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Genocs.Template.Application", "src\Genocs.Template.Application\Genocs.Template.Application.csproj", "{B3DAF7F3-D632-4252-829B-010BF702BDC0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{6335BAF6-440D-4B7D-8618-DB1B32F4E1DB}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Solution Items", "_Solution Items", "{1250CDBC-9DDA-447F-97E6-FCBF98F82BAC}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DE058D73-748F-4E31-BA5C-48DEF7C6BA41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DE058D73-748F-4E31-BA5C-48DEF7C6BA41}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DE058D73-748F-4E31-BA5C-48DEF7C6BA41}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DE058D73-748F-4E31-BA5C-48DEF7C6BA41}.Release|Any CPU.Build.0 = Release|Any CPU - {FF2A2975-6B15-49D0-982A-4020591FE745}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FF2A2975-6B15-49D0-982A-4020591FE745}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FF2A2975-6B15-49D0-982A-4020591FE745}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FF2A2975-6B15-49D0-982A-4020591FE745}.Release|Any CPU.Build.0 = Release|Any CPU - {252000C9-DCC7-4222-ACF0-A7DEA1727155}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {252000C9-DCC7-4222-ACF0-A7DEA1727155}.Debug|Any CPU.Build.0 = Debug|Any CPU - {252000C9-DCC7-4222-ACF0-A7DEA1727155}.Release|Any CPU.ActiveCfg = Release|Any CPU - {252000C9-DCC7-4222-ACF0-A7DEA1727155}.Release|Any CPU.Build.0 = Release|Any CPU - {D5FDC379-F6F0-427F-9EC1-EC63DBC40183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D5FDC379-F6F0-427F-9EC1-EC63DBC40183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D5FDC379-F6F0-427F-9EC1-EC63DBC40183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D5FDC379-F6F0-427F-9EC1-EC63DBC40183}.Release|Any CPU.Build.0 = Release|Any CPU - {B3DAF7F3-D632-4252-829B-010BF702BDC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B3DAF7F3-D632-4252-829B-010BF702BDC0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B3DAF7F3-D632-4252-829B-010BF702BDC0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B3DAF7F3-D632-4252-829B-010BF702BDC0}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {FF2A2975-6B15-49D0-982A-4020591FE745} = {6335BAF6-440D-4B7D-8618-DB1B32F4E1DB} - {252000C9-DCC7-4222-ACF0-A7DEA1727155} = {6335BAF6-440D-4B7D-8618-DB1B32F4E1DB} - {D5FDC379-F6F0-427F-9EC1-EC63DBC40183} = {6335BAF6-440D-4B7D-8618-DB1B32F4E1DB} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {7085404A-CC53-42BF-9A9F-C0BC3830CC6C} - EndGlobalSection -EndGlobal diff --git a/templates/template/NuGet.config b/templates/template/NuGet.config deleted file mode 100644 index 06e20895..00000000 --- a/templates/template/NuGet.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/templates/template/README.md b/templates/template/README.md deleted file mode 100644 index 17edff41..00000000 --- a/templates/template/README.md +++ /dev/null @@ -1,1493 +0,0 @@ -# The Genocs Library Service built with Template - - -Genocs.ServiceTemplate implementation. - -![Documentation](https://blog.genocs.com/) - - - -## Usage - -```sh -dotnet new -i Genocs.ServiceTemplate::1.0.0 -dotnet new servicetemplate -n "CompanyName.ProjectName" -``` - -## Usage -Update mongoDB database name before start - - - - -This will be removed - - - of the **Genocs Enterprise Service Principles with .NET Core**. Use cases as central organizing structure, decoupled from frameworks and technology details. Built with small components that are developed and tested in isolation. - ----- - -[![Build Status](https://travis-ci.com/genocs/clean-architecture-template.svg?branch=master)](https://travis-ci.com/genocs/clean-architecture-template) [![Build status](https://ci.appveyor.com/api/projects/status/0i6s33kw3y87tkb2?svg=true)](https://ci.appveyor.com/project/genocs/clean-architecture-template) ![NuGet](https://buildstats.info/nuget/Genocs.CleanArchitectureTemplate) [![All Contributors](https://img.shields.io/badge/all_contributors-12-yellow.svg?style=flat-square)](#contributors) - - - -## Usage - -```sh -dotnet new -i Genocs.ServiceTemplate::1.0.0 -dotnet new servicetemplate -n "CompanyName.ProjectName" -``` - -To get the Clean Architecture updates hit the `WATCH` button. - -Would you like to show Clean Architecture on your GitHub profile? Hit the `FORK` button. - -Really interested in designing modular applications? Support this project with a hit on the `STAR` button. Share with a friend! - -**Manga** is a virtual Wallet application in which a customer can register an account then manage the balance with `Deposits`, `Withdraws` and `Transfers`. - -Run the Docker container in less than 2 minutes using Play With Docker: - -Try in PWD - - -## Motivation - -> Learn how to design modular applications. -> -> Explore the .NET Core features. - -### Learn how to design modular applications - -Learning how to design modular applications will help you become a better engineer. Designing modular applications is the holy grail of software architecture, it is hard to find engineers experienced in designing applications which allows adding new features in a steady speed. - -### Explore the .NET Core features - -.NET Core brings a sweet development environment, an extensible and cross-platform framework. We will explore the benefits of it in the infrastructure layer and we will reduce its relevance in the application layer. The same rule is applied for modern C# language constructions. - -### Learn from the open source community - -This is continually updated, open source project. - -[Contributions](#contributors-) are welcome! - - -## Persistance layer - -This example contains the implementation related to three different storage type: - -- InMemoryDataAccess (usefull only for development) -- Microsoft SQL Server (The popular Relational database developed by Microsoft) -- MongoDB (The popular Document DB) - - -### MongoDB Replicaset - -To set Mongodb as a replicaset - -1. Run Docker compose with 3 mongo container -2. Connect to Mongo Shell -```sh -docker exec -it mongo_db2 /bin/bash -mongo -``` - -3. Initialize the replicaset and check the status - -```ts -rs.initiate( - { - _id : 'rs0', - members: [ - { _id : 0, host : "mongo_db1:27017" }, - { _id : 1, host : "mongo_db2:27017" }, - { _id : 2, host : "mongo_db3:27017" } - ] - } -) - -rs.slaveOk().ok -rs.status() -``` - -## Enterprise service bus - -### RabbitMQ cluster - -The folder docker-infrastructure contains the docker-compose file and everything required to run a two node RabbitMQ cluster. - -This example implement the enterprise service bus through three different library. - -- Rebus -- Bare implementation with Azure Service bus - - -## Containers and orchestrators - -The example is ready to create the docker images for both WebApi and Worker - - -## Contributing - -> Learn from the community. - -Feel free to submit pull requests to help: - -* Fix errors -* Improve sections -* Add new sections -* Submit questions and bugs - -## Index of Clean Architecture Template - -* [Use Cases](#use-cases) -* [Flow of Control](#register-flow-of-control) - * [Register Flow of Control](#register-flow-of-control) - * [Get Customer Details Flow of Control](#get-customer-details-flow-of-control) -* [Architecture Styles](#architecture-styles) - * [Hexagonal Architecture Style](#ports-and-adapters-architecture-style) - * [Ports](#ports) - * [Adapters](#adapters) - * [The Left Side](#the-left-side) - * [The Right Side](#the-right-side) - * [Onion Architecture Style](#onion-architecture-style) - * [Clean Architecture Style](#clean-architecture-style) -* [Design Patterns](#design-patterns) - * [Controller](#controller) - * [ViewModel](#viewmodel) - * [Presenter](#presenter) - * [Standard Output](#standard-output) - * [Error Output](#error-output) - * [Alternative Output](#alternative-output) - * [Unit of Work](#unit-of-work) - * [First-Class Collections](#first-class-collections) - * [Factory](#factory) -* [Domain-Driven Design Patterns](#domain-driven-design-patterns) - * [Value Object](#value-object) - * [Entity](#entity) - * [Aggregate Root](#aggregate-root) - * [Repository](#repository) - * [Use Case](#use-case) -* [Separation of Concerns](#separation-of-concerns) - * [Domain](#domain) - * [Application](#application) - * [Infrastructure](#infrastructure) - * [User Interface](#user-interface) -* [Encapsulation](#encapsulation) -* [Test-Driven Development TDD](#test-driven-development-tdd) - * [Fakes](#fakes) -* [SOLID](#solid) - * [Single Responsibility Principle](#single-responsibility-principle) - * [Open-Closed Principle](#open-closed-principle) - * [Liskov Substitution Principle](#liskov-substitution-principle) - * [Interface Segregation Principle](#interface-segregation-principle) - * [Dependency Inversion Principle](#dependency-inversion-principle) -* [.NET Core Web API](#.net-core-webapi) - * [Swagger and API Versioning](#swagger-and-api-versioning) - * [Microsoft Extensions](#microsoft-extensions) - * [Feature Flags](#feature-flags) - * [Logging](#logging) - * [Data Annotations](#data-annotations) - * [Authentication](#authentication) - * [Authorization](#authorization) -* [Entity Framework Core](#entity-framework-core) - * [Add Migration](#add-migration) - * [Update Database](#update-database) -* [Environment Configurations](#environment-configurations) -* [DevOps](#devops) - * [Running the Application Locally](#running-the-application-locally) - * [Running the Tests Locally](#running-the-tests-locally) - * [Continuous Integration & Continuous Deployment](#continuous-integration-continuous-deployment) -* [Docker](#docker) -* [SQL Server](#sql-server) -* [Related Content and Projects](#related-content-and-projects) - - -## Use Cases - -> Use Cases are delivery independent, they show the intent of a system. -> -> Use Cases are algorithms which interpret the input to generate the output data. - -Application architecture is about usage, a good architecture screams the business use cases to the developer and framework concerns are implementation details. On **Manga** sample the user can `Register` an account then manage the balance by `Deposits`, `Withdrawals` and `Transfers`. - -

- Clean -

- -Following the list of Use Cases: - -| Use Case | Description | -|----------------------|-----------------------------------------------------------------------| -| Register | An customer can register an account using his personal details. | -| Deposit | The customer can deposit an amount. | -| Transfer | The customer can transfer money from one account to another. | -| Withdraw | A customer can withdraw money but not more that the current balance. | -| Get Customer Details | Get customer details including all related accounts and transactions. | -| Get Account Details | Get account details including transactions. | -| Close Account | Closes an account, requires balance to be zero. | - -## Flow of Control - -The flow of control begins in the controller, moves through the use case, and then winds up executing in the presenter. - -### Register Flow of Control - -1. An request in received by the `CustomersController` and an action `Post` is invoked. -2. The action creates an `RegisterInput` message and the `Register` use case is executed. -3. The `Register` use case creates a `Customer` and an `Account`. Repositories are called, the `RegisterOutput` message is built and sent to the `RegisterPresenter`. -4. The `RegisterPresenter` builds the HTTP Response message. -5. The `CustomersController` asks the presenter the current response. - -![Register Flow of Control](https://github.com/genocs/clean-architecture/blob/master/docs/register-flow-of-control.svg) - -### Get Customer Details Flow of Control - -1. An request in received by the `CustomersController` and an action `GetCustomer` is invoked. -2. The action creates an `GetCustomerDetailsInput` message and the `GetCustomerDetails` use case is executed. -3. The `GetCustomerDetails` use case asks the repositories about the `Customer` and the `Account`. It could call the `NotFound` or the `Default` port of the `GetCustomerDetailsPresenter` depending if it exists or not. -4. The `GetCustomerDetailsPresenter` builds the HTTP Response message. -5. The `CustomersController` asks the presenter the current response. - -## Architecture Styles - -Manga uses ideas from popular architectural styles. They Ports and Adapters are the simplest one followed by the others, they complement each other and aim a software made by use cases decoupled from technology implementation details. - -### Hexagonal Architecture Style - -The general idea behind Hexagonal architecture style is that the dependencies (Adapters) required by the software to run are used behind an interface (Port). - -The software is divided into **Application** and **Infrastructure** in which the adapters are interchangeable components developed and tested in isolation. The Application is loosely coupled to the Adapters and their implementation details. - -#### Ports - -Interfaces like `ICustomerRepository`, `IOutputPort` and `IUnitOfWork` are ports required by the application. - -#### Adapters - -The interface implementations, they are specific to a technology and bring external capabilities. For instance the `CustomerRepository` inside the `EntityFrameworkDataAccess` folder provides capabilities to consume an SQL Server database. - -![Ports and Adapters](https://raw.githubusercontent.com/genocs/clean-architecture/master/docs/clean-architecture-ports-and-adapters.png) - -#### The Left Side - -Primary Actors are usually the user interface or the Test Suit. - -#### The Right Side - -The Secondary Actors are usually Databases, Cloud Services or other systems. - -### Onion Architecture Style - -Very similar to Ports and Adapters, I would add that data objects cross boundaries as simple data structures. For instance, when the controller execute an use case it passes and immutable Input message. When the use cases calls an Presenter it gives a Output message (Data Transfer Objects if you like). - -### Clean Architecture Style - -An application architecture implementation guided by tests cases. - -## Design Patterns - -The following Design Patterns will help you continue implementing use cases in a consistent way. - -### Controller - -Controllers receive Requests, build the Input message then call the Use Case, you should notice that the controller do not build the Response, instead this responsibility is delegated to the presenter object. - -```c# -public sealed class CustomersController : Controller -{ - // Code omitted to simplify... - - public async Task Post([FromBody][Required] RegisterRequest request) - { - await _registerUseCase.Execute(new RegisterInput( - new SSN(request.SSN), - new Name(request.Name), - new PositiveAmount(request.InitialAmount))); - - return _presenter.ViewModel; - } -} -``` - -### ViewModel - -ViewModels are Data Transfer Objects, they will be rendered by the MVC framework so we need to follow the framework guidelines. I suggest that you add comments describing each property and the `[Required]` attribute so swagger generators could know the properties that are not nullable. My personal preference is to avoid getters here because you have total control of response object instantiation, so implement the constructor. - -```c# -/// -/// The response for Registration -/// -public sealed class RegisterResponse -{ - /// - /// Customer ID - /// - [Required] - public Guid CustomerId { get; } - - /// - /// SSN - /// - [Required] - public string SSN { get; } - - /// - /// Name - /// - [Required] - public string Name { get; } - - /// - /// Accounts - /// - [Required] - public List Accounts { get; } - - public RegisterResponse( - Guid customerId, - string ssn, - string name, - List accounts) - { - CustomerId = customerId; - SSN = ssn; - Name = name; - Accounts = accounts; - } -} -``` - -### Presenter - -Presenters are called by the application Use Cases and build the Response objects. - -```c# -public sealed class RegisterPresenter : IOutputPort -{ - public IActionResult ViewModel { get; private set; } - - public void Error(string message) - { - var problemDetails = new ProblemDetails() - { - Title = "An error occurred", - Detail = message - }; - - ViewModel = new BadRequestObjectResult(problemDetails); - } - - public void Standard(RegisterOutput output) - { - /// Long object creation omitted... - - ViewModel = new CreatedAtRouteResult("GetCustomer", - new - { - customerId = model.CustomerId - }, - model); - } -} -``` - -It is important to understand that from the Application perspective the use cases see an OutputPort with custom methods to call dependent on the message, and from the Web Api perspective the Controller only see the ViewModel property. - -#### Standard Output - -The output port for the use case regular behavior. - -#### Error Output - -Called when an blocking errors happens. - -#### Alternative Output - -Called when an blocking errors happens. - -### Unit of Work - -```c# -public interface IUnitOfWork -{ - Task Save(); -} -``` - -```c# -public sealed class UnitOfWork : IUnitOfWork, IDisposable -{ - private readonly GenocsContext _context; - - public UnitOfWork(GenocsContext context) - => _context = context; - - public async Task Save() - { - int affectedRows = await context.SaveChangesAsync(); - return affectedRows; - } - - private bool _disposed = false; - - private void Dispose(bool disposing) - { - if (!_disposed) - { - if (disposing) - { - context.Dispose(); - } - } - _disposed = true; - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } -} -``` - -### First-Class Collections - -```c# -public sealed class CreditsCollection -{ - private readonly IList _credits; - - public CreditsCollection() - { - _credits = new List(); - } - - public void Add(IEnumerable credits) - where T : ICredit - { - foreach (var credit in credits) - Add(credit); - } - - public void Add(ICredit credit) - { - _credits.Add(credit); - } - - public IReadOnlyCollection GetTransactions() - { - var transactions = new ReadOnlyCollection(_credits); - return transactions; - } - - public PositiveAmount GetTotal() - { - PositiveAmount total = new PositiveAmount(0); - - foreach (ICredit credit in _credits) - { - total = credit.Sum(total); - } - - return total; - } -} -``` - -### Factory - -```c# -public interface IEntityFactory -{ - ICustomer NewCustomer(SSN ssn, Name name); - IAccount NewAccount(ICustomer customer); - ICredit NewCredit(IAccount account, PositiveAmount amountToDeposit); - IDebit NewDebit(IAccount account, PositiveAmount amountToWithdraw); -} -``` - -```c# -public sealed class EntityFactory : IEntityFactory -{ - public IAccount NewAccount(ICustomer customer) - { - var account = new Account(customer); - return account; - } - - public ICredit NewCredit(IAccount account, PositiveAmount amountToDeposit) - { - var credit = new Credit(account, amountToDeposit); - return credit; - } - - public ICustomer NewCustomer(SSN ssn, Name name) - { - var customer = new Customer(ssn, name); - return customer; - } - - public IDebit NewDebit(IAccount account, PositiveAmount amountToWithdraw) - { - var debit = new Debit(account, amountToWithdraw); - return debit; - } -} -``` - -### Component - -## Domain-Driven Design Patterns - -The following patterns are known to describe business solutions. - -### Value Object - -Describe the tiny domain business rules. Objects that are unique by the has of their properties. Are immutable. - -```c# -public sealed class Name : IEquatable -{ - private readonly string _text; - - private Name() { } - - public Name(string text) - { - if (string.IsNullOrWhiteSpace(text)) - throw new NameShouldNotBeEmptyException("The 'Name' field is required"); - - _text = text; - } - - public override string ToString() - { - return _text; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is string) - { - return obj.ToString() == _text; - } - - return ((Name) obj)._text == _text; - } - - public override int GetHashCode() - { - unchecked - { - int hash = 17; - hash = hash * 23 + _text.GetHashCode(); - return hash; - } - } - - public bool Equals(Name other) - { - return this._text == other._text; - } -} -``` - -### Entity - -Mutable objects unique identified by their IDs. - -```c# -public class Credit : ICredit -{ - public Guid Id { get; protected set; } - - public PositiveAmount Amount { get; protected set; } - - public string Description - { - get { return "Credit"; } - } - - public DateTime TransactionDate { get; protected set; } - - public PositiveAmount Sum(PositiveAmount amount) - { - return Amount.Add(amount); - } -} -``` - -### Aggregate Root - -Similar to Entities with the addition that Aggregate Root are responsible to keep the tree of objects consistent. - -```c# -public class Account : IAccount -{ - public Guid Id { get; protected set; } - - public CreditsCollection Credits { get; protected set; } - - public DebitsCollection Debits { get; protected set; } - - protected Account() - { - Credits = new CreditsCollection(); - Debits = new DebitsCollection(); - } - - public ICredit Deposit(IEntityFactory entityFactory, PositiveAmount amountToDeposit) - { - var credit = entityFactory.NewCredit(this, amountToDeposit); - Credits.Add(credit); - return credit; - } - - public IDebit Withdraw(IEntityFactory entityFactory, PositiveAmount amountToWithdraw) - { - if (GetCurrentBalance().LessThan(amountToWithdraw)) - return null; - - var debit = entityFactory.NewDebit(this, amountToWithdraw); - Debits.Add(debit); - return debit; - } - - public bool IsClosingAllowed() - { - return GetCurrentBalance().IsZero(); - } - - public Amount GetCurrentBalance() - { - var totalCredits = Credits - .GetTotal(); - - var totalDebits = Debits - .GetTotal(); - - var totalAmount = totalCredits - .Subtract(totalDebits); - - return totalAmount; - } -} -``` - -### Repository - -```c# -public sealed class CustomerRepository : ICustomerRepository -{ - private readonly GenocsContext _context; - - public CustomerRepository(GenocsContext context) - => _context = context; - - public async Task Add(ICustomer customer) - { - _context.Customers.Add((InMemoryDataAccess.Customer) customer); - await Task.CompletedTask; - } - - public async Task Get(Guid id) - { - Customer customer = _context.Customers - .Where(e => e.Id == id) - .SingleOrDefault(); - - return await Task.FromResult(customer); - } - - public async Task Update(ICustomer customer) - { - Customer customerOld = _context.Customers - .Where(e => e.Id == customer.Id) - .SingleOrDefault(); - - customerOld = (Customer) customer; - await Task.CompletedTask; - } -} -``` - -### Use Case - -```c# -public sealed class Withdraw : IUseCase -{ - // Properties and constructor omitted - - public async Task Execute(WithdrawInput input) - { - IAccount account = await _accountRepository.Get(input.AccountId); - if (account == null) - { - _outputHandler.Error($"The account {input.AccountId} does not exist or is already closed."); - return; - } - - IDebit debit = account.Withdraw(_entityFactory, input.Amount); - - if (debit == null) - { - _outputHandler.Error($"The account {input.AccountId} does not have enough funds to withdraw {input.Amount}."); - return; - } - - await _accountRepository.Update(account, debit); - await _unitOfWork.Save(); - - WithdrawOutput output = new WithdrawOutput( - debit, - account.GetCurrentBalance() - ); - - _outputHandler.Default(output); - } -} -``` - -## Separation of Concerns - -

- Layers -

- -### Domain - -The package that contains the `High Level Modules` which describe the Domain via Aggregate Roots, Entities and Value Objects. By design this project is `Highly Abstract` and `Stable`, in other terms this package contains a considerable amount of interfaces and should not depend on external libraries and frameworks. Ideally it should be loosely coupled even to the .NET Framework. - -### Application - -A project that contains the Application Use Cases which orchestrate the high level business rules. By design the orchestration will depend on abstractions of external services (eg. Repositories). The package exposes Boundaries Interfaces (in other terms Contracts or `Ports`) which are used by the user interface. - -### Infrastructure - -The infrastructure layer is responsible to implement the `Adapters` to the `Secondary Actors`. For instance an SQL Server Database is a secondary actor which is affected by the application use cases, all the implementation and dependencies required to consume the SQL Server is created on infrastructure. By design the infrastructure depends on application layer. - -### User Interface - -The system entry point responsible to render an interface to interact with the User. Made with Controllers which receive HTTP Requests and Presenters which converts the application outputs into ViewModels that are rendered as HTTP Responses. - -## Encapsulation - -> Given a class, the sum of its members complexity should be less that the sum of its parts in isolation. - -Suppose there is a `Customer` entity like this: - -```c# -public class Customer -{ - public Guid Id { get; set; } - public string Name { get; set; } - public string SSN { get; set; } - public bool Active { get; set; } - public string ActivatedBy { get; set; } -} -``` - -The complexity of the previous class is the same if there were variables like the following: - -```c# - Guid Id; - string Name; - string SSN; - bool Active; - string ActivatedBy; -``` - -Classes that are similar to a bag of data leaks unnecessary complexity. Consider reducing the complexity with something like: - -```c# -public class Customer -{ - public Guid Id { get; protected set; } - public string Name { get; protected set; } - public string SSN { get; protected set; } - public bool Active { get; protected set; } - public string ActivatedBy { get; protected set; } -} -``` - -## Test-Driven Development (TDD) - -> You are not allowed to write any production code unless it is to make a failing unit test pass. -> -> You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures. -> -> You are not allowed to write any more production code than is sufficient to pass the one failing unit test. - -http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd - -### Fakes - -> Fake it till you make it - -## SOLID - -### Single Responsibility Principle - -> A class should have one, and only one, reason to change. - -### Open-Closed Principle - -> You should be able to extend a classes behavior, without modifying it. - -### Liskov Substitution Principle - -> Derived classes must be substitutable for their base classes. - -### Interface Segregation Principle - -> Make fine grained interfaces that are client specific. - -### Dependency Inversion Principle - -> Depend on abstractions, not on concretions. - -## .NET Core Web API - -### Swagger and API Versioning - -```c# -namespace Genocs.WebApi.Extensions -{ - using Filters; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Mvc.ApiExplorer; - using Microsoft.AspNetCore.Mvc; - using Microsoft.Extensions.DependencyInjection; - using Swashbuckle.AspNetCore.Examples; - using Swashbuckle.AspNetCore.Swagger; - using Swashbuckle.AspNetCore.SwaggerGen; - - public static class VersionedSwaggerExtensions - { - public static IServiceCollection AddVersionedSwagger(this IServiceCollection services) - { - services.AddApiVersioning(o => - { - o.AssumeDefaultVersionWhenUnspecified = true; - o.DefaultApiVersion = new ApiVersion(1, 0); - }); - - services.AddVersionedApiExplorer(o => o.GroupNameFormat = "'V'VVV"); - - services.AddSwaggerGen(options => - { - var provider = services.BuildServiceProvider() - .GetRequiredService(); - - foreach (var apiVersion in provider.ApiVersionDescriptions) - { - ConfigureVersionedDescription(options, apiVersion); - } - - var xmlCommentsPath = Assembly.GetExecutingAssembly() - .Location.Replace("dll", "xml"); - options.IncludeXmlComments(xmlCommentsPath); - - options.OperationFilter(); - options.DocumentFilter(); - }); - - return services; - } - - private static void ConfigureVersionedDescription( - SwaggerGenOptions options, - ApiVersionDescription apiVersion) - { - var dictionairy = new Dictionary - { - { "1.0", "This API features several endpoints showing different API features for API version V1" }, - { "2.0", "This API features several endpoints showing different API features for API version V2" } - }; - - var apiVersionName = apiVersion.ApiVersion.ToString(); - options.SwaggerDoc(apiVersion.GroupName, - new Info() - { - Title = "Clean Architecture Genocs", - Contact = new Contact() - { - Name = "@giovanninocco", - Email = "giovanni.nocco@genocs.com", - Url = "https://github.com/genocs" - }, - License = new License() - { - Name = "MIT License" - }, - Version = apiVersionName, - Description = dictionairy[apiVersionName] - }); - } - - public static IApplicationBuilder UseVersionedSwagger( - this IApplicationBuilder app, - IApiVersionDescriptionProvider provider) - { - app.UseSwagger(options => - { - options.PreSerializeFilters.Add((swaggerDoc, httpRequest) => - { - if (httpRequest.Path.Value.Contains("/swagger")) - { - swaggerDoc.BasePath = httpRequest.Path.Value.Split("/").FirstOrDefault() ?? ""; - } - - if (httpRequest.Headers.TryGetValue("X-Forwarded-Prefix", out var xForwardedPrefix)) - { - swaggerDoc.BasePath = xForwardedPrefix[0]; - } - }); - }); - - app.UseSwaggerUI(options => - { - // Build a swagger endpoint for each discovered API version - foreach (var description in provider.ApiVersionDescriptions) - { - options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant()); - } - }); - - return app; - } - } -} -``` - -### Microsoft Extensions - -```c# -public sealed class Startup -{ - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureDevelopmentServices(IServiceCollection services) - { - services.AddMvc() - .SetCompatibilityVersion(CompatibilityVersion.Version_3_0) - .AddControllersAsServices(); - - services.AddBusinessExceptionFilter(); - - services.AddFeatureFlags(Configuration); - services.AddVersionedSwagger(); - - services.AddUseCases(); - - services.AddInMemoryPersistence(); - - services.AddPresentersV1(); - services.AddPresentersV2(); - } - - public void ConfigureProductionServices(IServiceCollection services) - { - services.AddMvc() - .SetCompatibilityVersion(CompatibilityVersion.Version_2_2) - .AddControllersAsServices(); - - services.AddBusinessExceptionFilter(); - - services.AddFeatureFlags(Configuration); - services.AddVersionedSwagger(); - - services.AddUseCases(); - - services.AddSQLServerPersistence(Configuration); - - services.AddPresentersV1(); - services.AddPresentersV2(); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure( - IApplicationBuilder app, - IWebHostEnvironment env, - IApiVersionDescriptionProvider provider) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - app.UseHsts(); - } - - app.UseVersionedSwagger(provider); - app.UseHttpsRedirection(); - app.UseStaticFiles(); - app.UseCookiePolicy(); - app.UseMvc(); - } -} -``` - -### Feature Flags - -```c# -public sealed class CustomControllerFeatureProvider : IApplicationFeatureProvider -{ - private readonly IFeatureManager _featureManager; - - public CustomControllerFeatureProvider(IFeatureManager featureManager) - { - _featureManager = featureManager; - } - - public void PopulateFeature(IEnumerable parts, ControllerFeature feature) - { - for (int i = feature.Controllers.Count - 1; i >= 0; i--) - { - var controller = feature.Controllers[i].AsType(); - foreach (var customAttribute in controller.CustomAttributes) - { - if (customAttribute.AttributeType.FullName == typeof(FeatureGateAttribute).FullName) - { - var constructorArgument = customAttribute.ConstructorArguments.First(); - foreach (var argumentValue in constructorArgument.Value as IEnumerable) - { - var typedArgument = (CustomAttributeTypedArgument) argumentValue; - var typedArgumentValue = (Features) (int) typedArgument.Value; - if (!_featureManager.IsEnabled(typedArgumentValue.ToString())) - feature.Controllers.RemoveAt(i); - } - } - } - } - } -} -``` - -### Logging - -```c# -public static IWebHostBuilder CreateWebHostBuilder(string[] args) -{ - return WebHost.CreateDefaultBuilder(args) - .ConfigureAppConfiguration((hostingContext, config) => - { - var env = hostingContext.HostingEnvironment; - - config.AddJsonFile("appsettings.json", optional : true, reloadOnChange : true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional : true, reloadOnChange : true); - - config.AddEnvironmentVariables(); - - if (args != null) - { - config.AddCommandLine(args); - } - }) - .ConfigureLogging((hostingContext, logging) => - { - // Requires `using Microsoft.Extensions.Logging;` - logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - logging.AddConsole(); - logging.AddDebug(); - logging.AddEventSourceLogger(); - }) - .UseStartup(typeof(Program).Assembly.FullName); -} -``` - -```c# -public static class FeatureFlagsExtensions -{ - public static IServiceCollection AddFeatureFlags(this IServiceCollection services, IConfiguration configuration) - { - services.AddFeatureManagement(configuration); - - var featureManager = services.BuildServiceProvider() - .GetRequiredService(); - - services.AddMvc() - .ConfigureApplicationPartManager(apm => - apm.FeatureProviders.Add( - new CustomControllerFeatureProvider(featureManager) - )); - - return services; - } -} -``` - -```c# -public enum Features -{ - Transfer, - GetAccountDetailsV2 -} -``` - -### Data Annotations - -Data Annotations are powerful tool from .NET, it can be interpreted by ASP.NET Core and other frameworks to generate Validation, User Interface and other things. On Manga project, Data Annotations are used to create a complete Swagger UI and HTTP Request validation. Of course following the Clean Architecture Principles we need to keep frameworks under control. - -I decided to use Data Annotations on the User Interface layer. Take a look on the `RegisterRequest` class: - -```c# -/// -/// Registration Request -/// -public sealed class RegisterRequest -{ - /// - /// SSN - /// - [Required] - public string SSN { get; set; } - - /// - /// Name - /// - [Required] - public string Name { get; set; } - - /// - /// Initial Amount - /// - [Required] - public decimal InitialAmount { get; set; } -} -``` - -The `RegisterResponse` also needs `[Required]` annotation for Swagger Clients. - -```c# -/// -/// The response for Registration -/// -public sealed class RegisterResponse -{ - /// - /// Customer ID - /// - [Required] - public Guid CustomerId { get; } - - /// - /// SSN - /// - [Required] - public string SSN { get; } - - /// - /// Name - /// - [Required] - public string Name { get; } - - /// - /// Accounts - /// - [Required] - public List Accounts { get; } - - public RegisterResponse( - Guid customerId, - string ssn, - string name, - List accounts) - { - CustomerId = customerId; - SSN = ssn; - Name = name; - Accounts = accounts; - } -} -``` - -References: [Designing and Testing Input Validation in .NET Core: The Clean Architecture way](https://paulovich.net/designing-testing-input-validation-in-dotnet-core-the-clean-architecture-way/) - -## Entity Framework Core - -```c# -public sealed class GenocsContext : DbContext -{ - public GenocsContext(DbContextOptions options) : base(options) - { - - } - - public DbSet Accounts { get; set; } - public DbSet Customers { get; set; } - public DbSet Credits { get; set; } - public DbSet Debits { get; set; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity() - .ToTable("Account"); - - modelBuilder.Entity() - .Ignore(p => p.Credits) - .Ignore(p => p.Debits); - - modelBuilder.Entity() - .ToTable("Customer") - .Property(b => b.SSN) - .HasConversion( - v => v.ToString(), - v => new SSN(v)); - - modelBuilder.Entity() - .ToTable("Customer") - .Property(b => b.Name) - .HasConversion( - v => v.ToString(), - v => new Name(v)); - - modelBuilder.Entity() - .Ignore(p => p.Accounts); - - modelBuilder.Entity() - .ToTable("Debit") - .Property(b => b.Amount) - .HasConversion( - v => v.ToAmount().ToDecimal(), - v => new PositiveAmount(v)); - - modelBuilder.Entity() - .ToTable("Credit") - .Property(b => b.Amount) - .HasConversion( - v => v.ToAmount().ToDecimal(), - v => new PositiveAmount(v)); - - modelBuilder.Entity().HasData( - new { Id = new Guid("197d0438-e04b-453d-b5de-eca05960c6ae"), Name = new Name("Test User"), SSN = new SSN("19860817-9999") } - ); - - modelBuilder.Entity().HasData( - new { Id = new Guid("4c510cfe-5d61-4a46-a3d9-c4313426655f"), CustomerId = new Guid("197d0438-e04b-453d-b5de-eca05960c6ae") } - ); - - modelBuilder.Entity().HasData( - new - { - Id = new Guid("f5117315-e789-491a-b662-958c37237f9b"), - AccountId = new Guid("4c510cfe-5d61-4a46-a3d9-c4313426655f"), - Amount = new PositiveAmount(400), - Description = "Credit", - TransactionDate = DateTime.UtcNow - } - ); - - modelBuilder.Entity().HasData( - new - { - Id = new Guid("3d6032df-7a3b-46e6-8706-be971e3d539f"), - AccountId = new Guid("4c510cfe-5d61-4a46-a3d9-c4313426655f"), - Amount = new PositiveAmount(400), - Description = "Debit", - TransactionDate = DateTime.UtcNow - } - ); - } -} -``` - -### Add Migration - -Run the EF Tool to add a migration to the `Genocs.Infrastructure` project. - -```sh -dotnet ef migrations add "InitialCreate" -o "EntityFrameworkDataAccess/Migrations" --project src/{MyCompany.MyProject}.Infrastructure --startup-project src/{MyCompany.MyProject}.WebApi -``` - -### Update Database - -Generate tables and seed the database via Entity Framework Tool: - -```sh -dotnet ef database update --project src/{MyCompany.MyProject}.Infrastructure --startup-project src/{MyCompany.MyProject}.WebApi -``` - -## Environment Configurations - -To run in `Development` mode use: - -```sh -dotnet run --project "src/{MyCompany.MyProject}.WebApi/{MyCompany.MyProject}.WebApi.csproj" --Environment="Development" -``` - -It starts the application and call `ConfigureDevelopmentServices` method which runs the application using in memory persistence. - -The second option is to run in `Production` mode: - -```sh -dotnet run --project "src/{MyCompany.MyProject}.WebApi/{MyCompany.MyProject}.WebApi.csproj" --Environment="Production" -``` - -This command will call `ConfigureProductionServices` then use SQL Server repositories. - -## DevOps - -### Running the Application Locally - -Manga is a cross-platform application, you can run it from Mac, Windows or Unix. To develop new features, you may use Visual Studio or Visual Studio Code :heart:. - -The single requirement is to install the latest .NET Code SDK. - -* [.NET Core SDK 5.0](https://dotnet.microsoft.com/download/dotnet/5.0) - -We made available scripts to create and seed the database quickly via Docker. - -Finally to run it locally use: - -```sh -dotnet run --project "src/{MyCompany.MyProject}.WebApi/{MyCompany.MyProject}.WebApi.csproj" -``` - -### Running the Tests Locally - -Run the following command at the root folder: - -```sh -dotnet test -``` - -### Continuous Integration & Continuous Deployment - -```yml -version: '1.0.{build}' -image: - - Ubuntu -environment: - DOCKER_USER: - secure: YnlezJhfKFUWo+E5/WCikQ== - DOCKER_PASS: - secure: iwibHSi3B80XJ3KjT1sAS1c66AsaOP3UFyUKKWrL1jo= - HEROKU_USERNAME: - secure: CUWu9AI7dgCvD7XMGYEDtb7XQlvkcOSuxpdaKdzOu/M= - HEROKU_API_KEY: - secure: XEo5yF9x7hReDhlb66Aj6xnk2HOFboVzNW6BLR1+shV7MP1DhRl8J+hHg8Do7OKl - HEROKU_APP_NAME: - secure: tKa7ydQJbbA+uovQNa5sBs9OcRWsCj71r4l9wvDG7/I= -services: - - docker -dotnet_csproj: - patch: true - file: '**\*.csproj' - version: '{version}' -build_script: - - docker pull mcr.microsoft.com/mssql/server:2017-latest || true - - docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=' -p 1433:1433 --name sql1 -d mcr.microsoft.com/mssql/server:2017-latest || true - - sleep 10 - - docker exec -i sql1 /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P '' -Q 'ALTER LOGIN SA WITH PASSWORD=""' || true - - dotnet ef database update --project src/{MyCompany.MyProject}.Infrastructure --startup-project src/{MyCompany.MyProject}.WebApi - - dotnet build - - pushd src/{MyCompany.MyProject}.WebApi/ - - dotnet pack --configuration Release - - popd -test_script: - - dotnet test test/{MyCompany.MyProject}.UnitTests/{MyCompany.MyProject}.UnitTests.csproj - - dotnet test test/{MyCompany.MyProject}.IntegrationTests/{MyCompany.MyProject}.IntegrationTests.csproj - - dotnet test test/{MyCompany.MyProject}.AcceptanceTests/{MyCompany.MyProject}.AcceptanceTests.csproj -deploy_script: - - docker build -t {mycompany}/clean-architecture:github . - - docker login -u="$DOCKER_USER" -p="$DOCKER_PASS" - - docker push {mycompany}/clean-architecture:github - - docker login --username=$HEROKU_USERNAME --password=$HEROKU_API_KEY registry.heroku.com - - docker tag {mycompany}/clean-architecture:github registry.heroku.com/$HEROKU_APP_NAME/web - - docker push registry.heroku.com/$HEROKU_APP_NAME/web - - curl https://cli-assets.heroku.com/install.sh | sh - - heroku container:release web -a $HEROKU_APP_NAME -``` - -## Docker - -The project build two different images. One for the the Web API and one for the bus worker. - -```sh -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build -WORKDIR /app - -# Copy everything else and build -COPY . . -RUN dotnet publish src/{MyCompany.MyProject}.WebApi -c release -o out - -# Build runtime image -FROM mcr.microsoft.com/dotnet/aspnet:7.0 -WORKDIR /app -COPY --from=build /app/out . -ENV ASPNETCORE_URLS http://*:80 -ENV ASPNETCORE_ENVIRONMENT Docker -ENTRYPOINT dotnet {Company.Project}.WebApi.dll -``` - -To build the docker images - -```sh -docker build -t company/project.webapi -f .\src\Genocs.MicroserviceLight.Template.WebApi\Dockerfile . -docker build -t company/project.worker -f .\src\Genocs.MicroserviceLight.Template.BusWorker\Dockerfile . -``` - - -## SQL Server - -To spin up a SQL Server in a docker container using the connection string `Server=localhost;User Id=sa;Password=;` run the following command: - -```sh -./src/scripts/sql-docker-up.sh -``` - -## Related Content and Projects - -| Video | Date | -|---------------------------------------|--------------| -| [Hexagonal and Clean Architecture styles. Same or Different?](https://www.youtube.com/watch?v=FNQbyZu-NAo)| Sep 16, 2019 | -| [Clean Architecture Essentials](https://www.youtube.com/watch?v=NjPjCxTIf4M)| Sep 13, 2019 | -| [Shinning Frameworks and DDD?!](https://www.youtube.com/watch?v=OmxBqmmhoHg)| Sep 12, 2019 | -| [Clean Architecture: The User Interface is a detail](https://www.youtube.com/watch?v=lWH_ZDu2zKQ)| Sep 11, 2019 | -| [TDD and Hexagonal Architecture: Clean Tests](https://www.youtube.com/watch?v=j6_XPsqjrhE)| Sep 10, 2019 | -| [Designing and Testing Input Validation with .NET Core: The Clean Architecture way](https://www.youtube.com/watch?v=hyW4d5OcExw)| Sep 9, 2019 | -| [Clean Architecture Manga](https://www.youtube.com/watch?v=ivAkdJmSqLQ) | Aug 6, 2019 | -| [TDD and TDD with .NET Core and VSCode](https://www.youtube.com/watch?v=ORe0r4bpfac&t=360s) | Nov 3, 2018 | -| [Introduction to Clean Architecture](https://www.youtube.com/watch?v=6SeoWIIK1NU&t=50s) | Oct 31, 2018 | - -## Contributors ✨ - -Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): - - - - - - - -
Giovanni Emananuele Nocco
Giovanni Nocco

⚠️
- - - -This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! - - diff --git a/templates/template/api-workbench.rest b/templates/template/api-workbench.rest deleted file mode 100644 index 68b92726..00000000 --- a/templates/template/api-workbench.rest +++ /dev/null @@ -1,19 +0,0 @@ -@url = https://localhost:5001 -@apiVersion = api-version=1.0 - -@customerId = 1f7516de-c0b3-4bc6-a09e-564eac27c263 - -### Create a customer -POST {{url}}/api/v1/Customers?{{apiVersion}} HTTP/1.1 -Content-Type: application/json - -{ - "ssn": "2410196814", - "name": "Giovanni3", - "initialAmount": 100 -} - -### Get an existing customer -GET {{url}}/api/v1/Customers/{{customerId}}?{{apiVersion}} HTTP/1.1 - - diff --git a/templates/template/docker-compose.dcproj b/templates/template/docker-compose.dcproj deleted file mode 100644 index ecb283cb..00000000 --- a/templates/template/docker-compose.dcproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - 2.1 - Linux - 0db314b3-916f-41c8-8f82-9ba947f03541 - LaunchBrowser - {Scheme}://localhost:{ServicePort} - genocs.template.webapi - - - - docker-compose.yml - - - - - \ No newline at end of file diff --git a/templates/template/docker-compose.override.yml b/templates/template/docker-compose.override.yml deleted file mode 100644 index e2c19118..00000000 --- a/templates/template/docker-compose.override.yml +++ /dev/null @@ -1,20 +0,0 @@ -version: '3.9' - -services: - servicetemplate.webapi: - environment: - - ASPNETCORE_ENVIRONMENT=Development - - ASPNETCORE_URLS=https://+:443;http://+:80 - networks: - - genocs - ports: - - 80 - - 443 - volumes: - - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro - - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro - -networks: - genocs: - name: genocs-network - external: true diff --git a/templates/template/docker-compose.yml b/templates/template/docker-compose.yml deleted file mode 100644 index c4af6075..00000000 --- a/templates/template/docker-compose.yml +++ /dev/null @@ -1,8 +0,0 @@ -version: '3.9' - -services: - servicetemplate.webapi: - image: ${DOCKER_REGISTRY-}servicetemplate-webapi - build: - context: . - dockerfile: webapi.dockerfile diff --git a/templates/template/k8s/Genocs.Template-kubernetes-ingress.yaml b/templates/template/k8s/Genocs.Template-kubernetes-ingress.yaml deleted file mode 100644 index 55ba5e16..00000000 --- a/templates/template/k8s/Genocs.Template-kubernetes-ingress.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: agic-ingress - annotations: - kubernetes.io/ingress.class: azure/application-gateway -spec: - rules: - - http: - paths: - - path: /foo - pathType: Prefix - backend: - service: - name: servicetemplate-webapi - port: - number: 80 - - path: /bar - pathType: Prefix - backend: - service: - name: qrcode - port: - number: 80 diff --git a/templates/template/k8s/Genocs.Template-kubernetes-webapi.yaml b/templates/template/k8s/Genocs.Template-kubernetes-webapi.yaml deleted file mode 100644 index eb19eb2a..00000000 --- a/templates/template/k8s/Genocs.Template-kubernetes-webapi.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: servicetemplate - labels: - app: servicetemplate-webapi -spec: - selector: - matchLabels: - app: servicetemplate-webapi - replicas: 1 - template: - metadata: - labels: - app: servicetemplate-webapi - spec: - containers: - - name: servicetemplate-webapi - image: genocs/servicetemplate:latest - env: - - name: ASPNETCORE_ENVIRONMENT - value: "Development" - - name: ApiClients__SecondUrl - value: "http://servicetemplate.default.svc.cluster.local" - resources: - requests: - cpu: 100m - memory: 100Mi - limits: - cpu: 250m - memory: 1024Mi - ports: - - containerPort: 80 ---- -apiVersion: v1 -kind: Service -metadata: - name: servicetemplate -spec: - # type: LoadBalancer - ports: - - port: 80 - selector: - app: servicetemplate-webapi diff --git a/templates/template/k8s/Genocs.Template-kubernetes-worker.yaml b/templates/template/k8s/Genocs.Template-kubernetes-worker.yaml deleted file mode 100644 index 154b15e2..00000000 --- a/templates/template/k8s/Genocs.Template-kubernetes-worker.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: servicetemplate - labels: - app: servicetemplate-worker -spec: - selector: - matchLabels: - app: servicetemplate-worker - replicas: 1 - template: - metadata: - labels: - app: servicetemplate-worker - spec: - containers: - - name: servicetemplate-worker - image: genocsacr.azurecr.io/servicetemplate-worker:latest - env: - - name: ParticularOptions__TransportConnectionString - value: "<>" - - name: ParticularOptions__PersistenceConnectionString - value: "<>" - resources: - requests: - cpu: 100m - memory: 100Mi - limits: - cpu: 250m - memory: 1024Mi - ports: - - containerPort: 80 ---- -apiVersion: v1 -kind: Service -metadata: - name: servicetemplate -spec: - # type: LoadBalancer - ports: - - port: 80 - selector: - app: servicetemplate-worker diff --git a/templates/template/pipelines/azure-pipeline-build.yml b/templates/template/pipelines/azure-pipeline-build.yml deleted file mode 100644 index 1cd065a8..00000000 --- a/templates/template/pipelines/azure-pipeline-build.yml +++ /dev/null @@ -1,40 +0,0 @@ -pool: - name: Azure Pipelines -#Your build pipeline references an undefined variable named ‘Parameters.RestoreBuildProjects’. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab. See https://go.microsoft.com/fwlink/?linkid=865972 -#Your build pipeline references the ‘BuildConfiguration’ variable, which you’ve selected to be settable at queue time. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab, and then select the option to make it settable at queue time. See https://go.microsoft.com/fwlink/?linkid=865971 -#Your build pipeline references an undefined variable named ‘Parameters.TestProjects’. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab. See https://go.microsoft.com/fwlink/?linkid=865972 -#Your build pipeline references the ‘BuildConfiguration’ variable, which you’ve selected to be settable at queue time. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab, and then select the option to make it settable at queue time. See https://go.microsoft.com/fwlink/?linkid=865971 -#Your build pipeline references the ‘BuildConfiguration’ variable, which you’ve selected to be settable at queue time. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab, and then select the option to make it settable at queue time. See https://go.microsoft.com/fwlink/?linkid=865971 - -steps: - - task: NuGetAuthenticate@0 - displayName: "NuGet Authenticate" - - - task: DotNetCoreCLI@2 - displayName: "Build projects" - inputs: - projects: "$(Parameters.RestoreBuildProjects)" - arguments: "--configuration $(BuildConfiguration)" # Update this to match your need - - # Run the Test (after building) - - task: DotNetCoreCLI@2 - displayName: "Run tests" - inputs: - command: "test" - projects: "$(Parameters.TestProjects)" - arguments: "--configuration $(BuildConfiguration)" - - # Publish the artifact to be ready for deploy - - task: DotNetCoreCLI@2 - displayName: Publish - inputs: - command: "publish" - publishWebProjects: true - arguments: "--configuration $(BuildConfiguration) --output $(build.artifactstagingdirectory)" - zipAfterPublish: True - - - task: PublishBuildArtifacts@1 - displayName: "Publish Artifact" - inputs: - pathToPublish: "$(build.artifactstagingdirectory)" - condition: succeededOrFailed() diff --git a/templates/template/pipelines/azure-pipeline-docker-acr.yml b/templates/template/pipelines/azure-pipeline-docker-acr.yml deleted file mode 100644 index 6abb1507..00000000 --- a/templates/template/pipelines/azure-pipeline-docker-acr.yml +++ /dev/null @@ -1,64 +0,0 @@ -# Build and push a Docker image to Azure Container Registry -# The stage contains a couple of build and push -# one for the Version and one for Latest -# https://aka.ms/yaml -# https://docs.microsoft.com/azure/devops/pipelines/languages/docker - -name: Build Docker Image and publish on AzureContainerRegistry - -trigger: - - none - -pool: - vmImage: ubuntu-latest - -variables: - buildConfiguration: "Release" - imageName: "$(DockerRegistry).azurecr.io/$(DockerImageName)" - -stages: - - stage: Build - displayName: Build image - jobs: - - job: Build - displayName: Build and push Docker image - steps: - - task: Docker@1 - displayName: "Build the Docker image" - inputs: - containerRegistryType: "Container Registry" - dockerRegistryEndpoint: "$(DockerRegistry)" - command: "Build image" - dockerFile: "Dockerfile" - arguments: "--build-arg BuildId=$(Build.BuildId)" - includeLatestTag: true - imageName: "$(ImageName)" - - - task: Docker@1 - displayName: "Push the Docker image to AzureContainerRegistry" - inputs: - containerRegistryType: "Container Registry" - dockerRegistryEndpoint: "$(DockerRegistry)" - command: "Push image" - imageName: "$(ImageName)" - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - - - task: Docker@1 - displayName: "Tag-Latest Build the Docker image" - inputs: - containerRegistryType: "Container Registry" - dockerRegistryEndpoint: "$(DockerRegistry)" - command: "Build image" - dockerFile: "Dockerfile" - arguments: "--build-arg BuildId=$(Build.BuildId)" - imageName: "$(ImageName):$(MajorVer).$(MinorVer).$(Build.BuildId)" - - - task: Docker@1 - displayName: "Tag-Latest Push the Docker image to AzureContainerRegistry" - inputs: - containerRegistryType: "Container Registry" - dockerRegistryEndpoint: "$(DockerRegistry)" - command: "Push image" - imageName: "$(ImageName):$(MajorVer).$(MinorVer).$(Build.BuildId)" - - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) diff --git a/templates/template/pipelines/azure-pipeline-docker.yml b/templates/template/pipelines/azure-pipeline-docker.yml deleted file mode 100644 index 7a9acfa3..00000000 --- a/templates/template/pipelines/azure-pipeline-docker.yml +++ /dev/null @@ -1,64 +0,0 @@ -# Build and push a Docker image to Docker hub -# The stage contains a couple of build and push -# one for the Version and one for Latest -# https://aka.ms/yaml -# https://docs.microsoft.com/azure/devops/pipelines/languages/docker - -name: Build Docker Image and publish on Docker Hub - -trigger: - - none - -pool: - vmImage: ubuntu-latest - -variables: - buildConfiguration: "Release" - imageName: "$(DockerRegistry)/$(DockerImageName)" - -stages: - - stage: Build - displayName: Build image - jobs: - - job: Build - displayName: Build and push Docker image - steps: - - task: Docker@1 - displayName: "Build the Docker image" - inputs: - containerRegistryType: "Container Registry" - dockerRegistryEndpoint: "DockerHub" - command: "Build image" - dockerFile: "Dockerfile" - arguments: "--build-arg BuildId=$(Build.BuildId)" - includeLatestTag: true - imageName: "$(ImageName)" - - - task: Docker@1 - displayName: "Push the Docker image to Docker Hub" - inputs: - containerRegistryType: "Container Registry" - dockerRegistryEndpoint: "DockerHub" - command: "Push image" - imageName: "$(ImageName)" - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - - - task: Docker@1 - displayName: "Tag-Latest Build the Docker image" - inputs: - containerRegistryType: "Container Registry" - dockerRegistryEndpoint: "DockerHub" - command: "Build image" - dockerFile: "Dockerfile" - arguments: "--build-arg BuildId=$(Build.BuildId)" - imageName: "$(ImageName):$(MajorVer).$(MinorVer).$(Build.BuildId)" - - - task: Docker@1 - displayName: "Tag-Latest Push the Docker image to Docker Hub" - inputs: - containerRegistryType: "Container Registry" - dockerRegistryEndpoint: "DockerHub" - command: "Push image" - imageName: "$(ImageName):$(MajorVer).$(MinorVer).$(Build.BuildId)" - - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) diff --git a/templates/template/pipelines/azure-pipeline.yml b/templates/template/pipelines/azure-pipeline.yml deleted file mode 100644 index 9bc0f995..00000000 --- a/templates/template/pipelines/azure-pipeline.yml +++ /dev/null @@ -1,126 +0,0 @@ -variables: - repositoryName: workflow - chartPath: charts/workflow - dockerFileName: src/shipping/workflow/Dockerfile - imageName: $(repositoryName):$(Build.SourceBranchName) - azureSubscription: AZURE_PIPELINES_SERVICE_CONN_NAME_VAR_VAL - azureContainerRegistry: ACR_SERVER_VAR_VAL - azureContainerRegistryName: ACR_NAME_VAR_VAL - -name: $(build.sourceBranch)-$(Date:yyyyMMdd)$(Rev:.rr) - -pr: # only valid for GitHub. Using Azure repo it must be configure as a Branch Policy - paths: - include: - - /src/shipping/workflow/ - - branches: - include: - - master - - release/workflow/v* # for bug fixes - -trigger: - batch: true - branches: - include: - # for new release to production: release flow strategy - - release/workflow/v* - - refs/relelase/workflow/v* - - master - - feature/workflow/* - - topic/workflow/* - paths: - include: - - /src/shipping/workflow/ - -resources: - - repo: self - -jobs: - # CI - - job: workflowjobci - displayName: "Workflow CI" - pool: - vmImage: "Ubuntu 20.04" - timeoutInMinutes: 90 - variables: - fullCI: $[ startsWith(variables['build.sourceBranch'], 'refs/heads/release/workflow/v') ] - buildImage: $[ eq(variables['build.sourceBranch'], 'refs/heads/master') ] - steps: - - task: Docker@1 - displayName: "Build testrunner image" - inputs: - azureSubscriptionEndpoint: $(azureSubscription) - azureContainerRegistry: $(azureContainerRegistry) - arguments: "--pull --target testrunner" - dockerFile: $(System.DefaultWorkingDirectory)/$(dockerFileName) - imageName: "$(imageName)-test" - - - task: Docker@1 - displayName: "Run tests" - inputs: - azureSubscriptionEndpoint: $(azureSubscription) - azureContainerRegistry: $(azureContainerRegistry) - command: "run" - containerName: testrunner - volumes: "$(System.DefaultWorkingDirectory)/TestResults:/src/tests/TestResults" - imageName: "$(imageName)-test" - runInBackground: false - - - task: PublishTestResults@2 - displayName: "Publish test results" - inputs: - testResultsFormat: "VSTest" # Options: JUnit, NUnit, VSTest, xUnit - testResultsFiles: "TestResults/*.trx" - searchFolder: "$(System.DefaultWorkingDirectory)" - publishRunAttachments: true - - - task: Docker@1 - condition: or(eq(variables['buildImage'],True),eq(variables['fullCI'],True)) - displayName: "Build runtime image" - inputs: - azureSubscriptionEndpoint: $(azureSubscription) - azureContainerRegistry: $(azureContainerRegistry) - dockerFile: $(System.DefaultWorkingDirectory)/$(dockerFileName) - includeLatestTag: false - imageName: "$(imageName)" - - - task: Docker@1 - condition: eq(variables['fullCI'],True) - displayName: "Push runtime image" - inputs: - azureSubscriptionEndpoint: $(azureSubscription) - azureContainerRegistry: $(azureContainerRegistry) - command: "Push an image" - imageName: "$(imageName)" - includeSourceTags: false - - - task: HelmInstaller@0 - condition: eq(variables['fullCI'],True) - displayName: "Install Helm 3.0.3" - inputs: - helmVersion: 3.0.3 - checkLatestHelmVersion: false - kubectlVersion: 1.12.4 - checkLatestKubectl: false - - - task: HelmDeploy@0 - condition: eq(variables['fullCI'],True) - displayName: "helm package" - inputs: - command: package - chartPath: $(chartPath) - chartVersion: $(Build.SourceBranchName) - updateDependency: true - save: false - arguments: "--app-version $(Build.SourceBranchName)" - - - task: AzureCLI@1 - condition: eq(variables['fullCI'],True) - displayName: "Push a helm package" - inputs: - azureSubscription: $(azureSubscription) - scriptLocation: inlineScript - - inlineScript: | - az acr helm push $(System.ArtifactsDirectory)/$(repositoryName)-$(Build.SourceBranchName).tgz --name $(azureContainerRegistryName) --force; diff --git a/templates/template/scripts/build.sh b/templates/template/scripts/build.sh deleted file mode 100644 index 4fb071e1..00000000 --- a/templates/template/scripts/build.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -MYGET_ENV="" -case "$TRAVIS_BRANCH" in - "develop") - MYGET_ENV="-dev" - ;; -esac - -dotnet build ../src/Genocs.Template.WebApi -c Release \ No newline at end of file diff --git a/templates/template/scripts/test.sh b/templates/template/scripts/test.sh deleted file mode 100644 index 6046c35a..00000000 --- a/templates/template/scripts/test.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -dotnet test \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.AcceptanceTests/Genocs.Template.AcceptanceTests.csproj b/templates/template/src/Genocs.Template.AcceptanceTests/Genocs.Template.AcceptanceTests.csproj deleted file mode 100644 index 32cbfc5a..00000000 --- a/templates/template/src/Genocs.Template.AcceptanceTests/Genocs.Template.AcceptanceTests.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - net7.0 - false - - - - TRACE;Full - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - diff --git a/templates/template/src/Genocs.Template.Application/Commands/CreateUser.cs b/templates/template/src/Genocs.Template.Application/Commands/CreateUser.cs deleted file mode 100644 index 4c221f65..00000000 --- a/templates/template/src/Genocs.Template.Application/Commands/CreateUser.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Genocs.Core.CQRS.Commands; - -namespace Genocs.Template.Application.Commands; - -public class CreateUser : ICommand -{ - public Guid UserId { get; } - public string Email { get; } - public string Name { get; } - public string Password { get; } - public string Role { get; } - public IEnumerable Permissions { get; } - - public CreateUser(Guid userId, string email, string name, string password, string role, - IEnumerable permissions) - { - UserId = userId == Guid.Empty ? Guid.NewGuid() : userId; - Email = email; - Name = name; - Password = password; - Role = role; - Permissions = permissions; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Commands/Handlers/CreateUserHandler.cs b/templates/template/src/Genocs.Template.Application/Commands/Handlers/CreateUserHandler.cs deleted file mode 100644 index d1a78f88..00000000 --- a/templates/template/src/Genocs.Template.Application/Commands/Handlers/CreateUserHandler.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Genocs.Core.CQRS.Commands; -using Genocs.Template.Application.Domain.Entities; -using Genocs.Template.Application.Domain.Exceptions; -using Genocs.Template.Application.Events; -using Genocs.Template.Application.Domain.Repositories; -using Genocs.Template.Application.Services; -using Microsoft.Extensions.Logging; -using System.Text.RegularExpressions; - -namespace Genocs.Template.Application.Commands.Handlers; - -internal sealed class CreateUserHandler : ICommandHandler -{ - private readonly IUserRepository _userRepository; - private readonly IPasswordService _passwordService; - private readonly IMessageBroker _messageBroker; - private readonly ILogger _logger; - - private static readonly Regex EmailRegex = new Regex( - @"^(?("")("".+?(? logger) - { - _userRepository = userRepository; - _passwordService = passwordService; - _messageBroker = messageBroker; - _logger = logger; - } - - public async Task HandleAsync(CreateUser command, CancellationToken cancellationToken = default) - { - if (!EmailRegex.IsMatch(command.Email)) - { - _logger.LogError($"Invalid email: {command.Email}"); - throw new InvalidEmailException(command.Email); - } - - var user = await _userRepository.GetByEmailAsync(command.Email); - if (user is { }) - { - _logger.LogError($"Email already in use: {command.Email}"); - throw new EmailInUseException(command.Email); - } - - user = await _userRepository.GetByNameAsync(command.Name); - if (user is { }) - { - _logger.LogError($"Name already in use: {command.Name}"); - throw new NameInUseException(command.Name); - } - - var role = string.IsNullOrWhiteSpace(command.Role) ? "user" : command.Role.ToLowerInvariant(); - var password = _passwordService.Hash(command.Password); - user = new User(command.UserId, command.Email, command.Name, password, role, DateTime.UtcNow, - command.Permissions); - await _userRepository.AddAsync(user); - _logger.LogInformation($"Created an account for the user with ID: '{user.Id}'."); - await _messageBroker.PublishAsync(new UserCreated(user.Id, user.Name, user.Role)); - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Commands/Handlers/LockUserHandler.cs b/templates/template/src/Genocs.Template.Application/Commands/Handlers/LockUserHandler.cs deleted file mode 100644 index 454d7e6a..00000000 --- a/templates/template/src/Genocs.Template.Application/Commands/Handlers/LockUserHandler.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Genocs.Core.CQRS.Commands; -using Genocs.Template.Application.Events; -using Genocs.Template.Application.Exceptions; -using Genocs.Template.Application.Commands; -using Genocs.Template.Application.Domain.Repositories; -using Genocs.Template.Application.Services; - -namespace Genocs.Template.Application.Commands.Handlers; - -internal sealed class LockUserHandler : ICommandHandler -{ - private readonly IUserRepository _userRepository; - private readonly IMessageBroker _messageBroker; - - public LockUserHandler(IUserRepository userRepository, IMessageBroker messageBroker) - { - _userRepository = userRepository; - _messageBroker = messageBroker; - } - - public async Task HandleAsync(LockUser command, CancellationToken cancellationToken = default) - { - var user = await _userRepository.GetAsync(command.UserId); - if (user is null) - { - throw new UserNotFoundException(command.UserId); - } - - if (user.Lock()) - { - await _userRepository.UpdateAsync(user); - await _messageBroker.PublishAsync(new UserLocked(user.Id)); - } - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Commands/Handlers/RevokeAccessTokenHandler.cs b/templates/template/src/Genocs.Template.Application/Commands/Handlers/RevokeAccessTokenHandler.cs deleted file mode 100644 index f4d4cc86..00000000 --- a/templates/template/src/Genocs.Template.Application/Commands/Handlers/RevokeAccessTokenHandler.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Genocs.Auth; -using Genocs.Core.CQRS.Commands; - -namespace Genocs.Template.Application.Commands.Handlers; - -internal sealed class RevokeAccessTokenHandler : ICommandHandler -{ - private readonly IAccessTokenService _accessTokenService; - - public RevokeAccessTokenHandler(IAccessTokenService accessTokenService) - { - _accessTokenService = accessTokenService ?? throw new ArgumentNullException(nameof(accessTokenService)); - } - - public async Task HandleAsync(RevokeAccessToken command, CancellationToken cancellationToken = default) - => await _accessTokenService.DeactivateAsync(command.AccessToken); -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Commands/Handlers/RevokeRefreshTokenHandler.cs b/templates/template/src/Genocs.Template.Application/Commands/Handlers/RevokeRefreshTokenHandler.cs deleted file mode 100644 index 25ce32ef..00000000 --- a/templates/template/src/Genocs.Template.Application/Commands/Handlers/RevokeRefreshTokenHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Genocs.Core.CQRS.Commands; -using Genocs.Template.Application.Domain.Exceptions; -using Genocs.Template.Application.Domain.Repositories; - -namespace Genocs.Template.Application.Commands.Handlers; - -internal sealed class RevokeRefreshTokenHandler : ICommandHandler -{ - private readonly IRefreshTokenRepository _refreshTokenRepository; - - public RevokeRefreshTokenHandler(IRefreshTokenRepository refreshTokenRepository) - { - _refreshTokenRepository = refreshTokenRepository; - } - - public async Task HandleAsync(RevokeRefreshToken command, CancellationToken cancellationToken = default) - { - var token = await _refreshTokenRepository.GetAsync(command.RefreshToken); - if (token is null) - { - throw new InvalidRefreshTokenException(); - } - - token.Revoke(DateTime.UtcNow); - await _refreshTokenRepository.UpdateAsync(token); - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Commands/Handlers/SignInHandler.cs b/templates/template/src/Genocs.Template.Application/Commands/Handlers/SignInHandler.cs deleted file mode 100644 index 87713f4c..00000000 --- a/templates/template/src/Genocs.Template.Application/Commands/Handlers/SignInHandler.cs +++ /dev/null @@ -1,72 +0,0 @@ -using Genocs.Core.CQRS.Commands; -using Genocs.Template.Application.Domain.Entities; -using Genocs.Template.Application.Domain.Exceptions; -using Genocs.Template.Application.Events; -using Genocs.Template.Application.Commands; -using Genocs.Template.Application.Domain.Repositories; -using Genocs.Template.Application.Services; -using Microsoft.Extensions.Logging; - -namespace Genocs.Template.Application.Commands.Handlers; - -internal sealed class SignInHandler : ICommandHandler -{ - private readonly IUserRepository _userRepository; - private readonly IRefreshTokenRepository _refreshTokenRepository; - private readonly IPasswordService _passwordService; - private readonly IJwtProvider _jwtProvider; - private readonly IRng _rng; - private readonly ITokenStorage _storage; - private readonly IMessageBroker _messageBroker; - private readonly ILogger _logger; - - public SignInHandler(IUserRepository userRepository, IRefreshTokenRepository refreshTokenRepository, - IPasswordService passwordService, IJwtProvider jwtProvider, IRng rng, ITokenStorage storage, - IMessageBroker messageBroker, ILogger logger) - { - _userRepository = userRepository; - _refreshTokenRepository = refreshTokenRepository; - _passwordService = passwordService; - _jwtProvider = jwtProvider; - _rng = rng; - _storage = storage; - _messageBroker = messageBroker; - _logger = logger; - } - - public async Task HandleAsync(SignIn command, CancellationToken cancellationToken = default) - { - var user = await _userRepository.GetByNameAsync(command.Name); - if (user is null || !_passwordService.IsValid(user.Password, command.Password)) - { - _logger.LogError($"User with name: {command.Name} was not found."); - throw new InvalidCredentialsException(command.Name); - } - - if (user.Locked) - { - throw new UserLockedException(user.Id); - } - - var claims = user.Permissions.Any() - ? new Dictionary> - { - ["permissions"] = user.Permissions - } - : null; - var auth = _jwtProvider.Create(user.Id, user.Name, user.Role, claims: claims); - auth.RefreshToken = await CreateRefreshTokenAsync(user.Id); - _storage.Set(command.Id, auth); - _logger.LogInformation($"User with id: {user.Id} has been authenticated."); - await _messageBroker.PublishAsync(new SignedIn(user.Id)); - } - - private async Task CreateRefreshTokenAsync(Guid userId) - { - var token = _rng.Generate(30, true); - var refreshToken = new RefreshToken(new AggregateId(), userId, token, DateTime.UtcNow); - await _refreshTokenRepository.AddAsync(refreshToken); - - return token; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Commands/Handlers/UnlockUserHandler.cs b/templates/template/src/Genocs.Template.Application/Commands/Handlers/UnlockUserHandler.cs deleted file mode 100644 index 34c12f3d..00000000 --- a/templates/template/src/Genocs.Template.Application/Commands/Handlers/UnlockUserHandler.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Genocs.Core.CQRS.Commands; -using Genocs.Template.Application.Events; -using Genocs.Template.Application.Exceptions; -using Genocs.Template.Application.Domain.Repositories; -using Genocs.Template.Application.Services; - -namespace Genocs.Template.Application.Commands.Handlers; - -internal sealed class UnlockUserHandler : ICommandHandler -{ - private readonly IUserRepository _userRepository; - private readonly IMessageBroker _messageBroker; - - public UnlockUserHandler(IUserRepository userRepository, IMessageBroker messageBroker) - { - _userRepository = userRepository; - _messageBroker = messageBroker; - } - - public async Task HandleAsync(UnlockUser command, CancellationToken cancellationToken = default) - { - var user = await _userRepository.GetAsync(command.UserId); - if (user is null) - { - throw new UserNotFoundException(command.UserId); - } - - if (user.Unlock()) - { - await _userRepository.UpdateAsync(user); - await _messageBroker.PublishAsync(new UserUnlocked(user.Id)); - } - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Commands/Handlers/UseRefreshTokenHandler.cs b/templates/template/src/Genocs.Template.Application/Commands/Handlers/UseRefreshTokenHandler.cs deleted file mode 100644 index b0eaec31..00000000 --- a/templates/template/src/Genocs.Template.Application/Commands/Handlers/UseRefreshTokenHandler.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Genocs.Core.CQRS.Commands; -using Genocs.Template.Application.Domain.Exceptions; -using Genocs.Template.Application.Exceptions; -using Genocs.Template.Application.Domain.Repositories; -using Genocs.Template.Application.Services; - -namespace Genocs.Template.Application.Commands.Handlers; - -internal sealed class UseRefreshTokenHandler : ICommandHandler -{ - private readonly IRefreshTokenRepository _refreshTokenRepository; - private readonly IUserRepository _userRepository; - private readonly IJwtProvider _jwtProvider; - private readonly ITokenStorage _storage; - - public UseRefreshTokenHandler(IRefreshTokenRepository refreshTokenRepository, IUserRepository userRepository, - IJwtProvider jwtProvider, ITokenStorage storage) - { - _refreshTokenRepository = refreshTokenRepository; - _userRepository = userRepository; - _jwtProvider = jwtProvider; - _storage = storage; - } - - public async Task HandleAsync(UseRefreshToken command, CancellationToken cancellationToken = default) - { - var token = await _refreshTokenRepository.GetAsync(command.RefreshToken); - if (token is null) - { - throw new InvalidRefreshTokenException(); - } - - if (token.Revoked) - { - throw new RevokedRefreshTokenException(); - } - - var user = await _userRepository.GetAsync(token.UserId); - if (user is null) - { - throw new UserNotFoundException(token.UserId); - } - - var claims = user.Permissions.Any() - ? new Dictionary> - { - ["permissions"] = user.Permissions - } - : null; - var auth = _jwtProvider.Create(token.UserId, user.Name, user.Role, claims: claims); - auth.RefreshToken = command.RefreshToken; - _storage.Set(command.Id, auth); - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Commands/LockUser.cs b/templates/template/src/Genocs.Template.Application/Commands/LockUser.cs deleted file mode 100644 index 7151d636..00000000 --- a/templates/template/src/Genocs.Template.Application/Commands/LockUser.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Genocs.Core.CQRS.Commands; - -namespace Genocs.Template.Application.Commands; - -public class LockUser : ICommand -{ - public Guid UserId { get; } - - public LockUser(Guid userId) - { - UserId = userId; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Commands/RevokeAccessToken.cs b/templates/template/src/Genocs.Template.Application/Commands/RevokeAccessToken.cs deleted file mode 100644 index d23bbded..00000000 --- a/templates/template/src/Genocs.Template.Application/Commands/RevokeAccessToken.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Genocs.Core.CQRS.Commands; - -namespace Genocs.Template.Application.Commands; - -public class RevokeAccessToken : ICommand -{ - public string AccessToken { get; } - - public RevokeAccessToken(string accessToken) - { - AccessToken = accessToken; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Commands/RevokeRefreshToken.cs b/templates/template/src/Genocs.Template.Application/Commands/RevokeRefreshToken.cs deleted file mode 100644 index 73a2469a..00000000 --- a/templates/template/src/Genocs.Template.Application/Commands/RevokeRefreshToken.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Genocs.Core.CQRS.Commands; - -namespace Genocs.Template.Application.Commands; - -public class RevokeRefreshToken : ICommand -{ - public string RefreshToken { get; } - - public RevokeRefreshToken(string refreshToken) - { - RefreshToken = refreshToken; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Commands/SignIn.cs b/templates/template/src/Genocs.Template.Application/Commands/SignIn.cs deleted file mode 100644 index 7728cb68..00000000 --- a/templates/template/src/Genocs.Template.Application/Commands/SignIn.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Genocs.Core.CQRS.Commands; - -namespace Genocs.Template.Application.Commands; - -public class SignIn : ICommand -{ - public Guid Id { get; } = Guid.NewGuid(); - public string Name { get; set; } - public string Password { get; set; } - - public SignIn(string name, string password) - { - Name = name; - Password = password; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Commands/UnlockUser.cs b/templates/template/src/Genocs.Template.Application/Commands/UnlockUser.cs deleted file mode 100644 index 4c936233..00000000 --- a/templates/template/src/Genocs.Template.Application/Commands/UnlockUser.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Genocs.Core.CQRS.Commands; - -namespace Genocs.Template.Application.Commands; - -public class UnlockUser : ICommand -{ - public Guid UserId { get; } - - public UnlockUser(Guid userId) - { - UserId = userId; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Commands/UseRefreshToken.cs b/templates/template/src/Genocs.Template.Application/Commands/UseRefreshToken.cs deleted file mode 100644 index b5b4c6a9..00000000 --- a/templates/template/src/Genocs.Template.Application/Commands/UseRefreshToken.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Genocs.Core.CQRS.Commands; - -namespace Genocs.Template.Application.Commands; - -public class UseRefreshToken : ICommand -{ - public Guid Id { get; } = Guid.NewGuid(); - public string RefreshToken { get; } - - public UseRefreshToken(string refreshToken) - { - RefreshToken = refreshToken; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/ContractAttribute.cs b/templates/template/src/Genocs.Template.Application/ContractAttribute.cs deleted file mode 100644 index c4218fa9..00000000 --- a/templates/template/src/Genocs.Template.Application/ContractAttribute.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Genocs.Template.Application; - -// Marker -public class ContractAttribute : Attribute -{ -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/CorrelationContext.cs b/templates/template/src/Genocs.Template.Application/CorrelationContext.cs deleted file mode 100644 index fbbbf9ff..00000000 --- a/templates/template/src/Genocs.Template.Application/CorrelationContext.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Genocs.Template.Application; - -internal class CorrelationContext -{ - public string CorrelationId { get; set; } - public string SpanContext { get; set; } - public UserContext User { get; set; } - public string ResourceId { get; set; } - public string TraceId { get; set; } - public string ConnectionId { get; set; } - public string Name { get; set; } - public DateTime CreatedAt { get; set; } - - public class UserContext - { - public string Id { get; set; } - public bool IsAuthenticated { get; set; } - public string Role { get; set; } - public IDictionary Claims { get; set; } - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/CorrelationIdFactory.cs b/templates/template/src/Genocs.Template.Application/CorrelationIdFactory.cs deleted file mode 100644 index 4491b022..00000000 --- a/templates/template/src/Genocs.Template.Application/CorrelationIdFactory.cs +++ /dev/null @@ -1,78 +0,0 @@ -using Genocs.HTTP; -using Genocs.HTTP.Options; -using Genocs.MessageBrokers; -using Microsoft.AspNetCore.Http; - -namespace Genocs.Template.Application; - -internal class CorrelationIdFactory : ICorrelationIdFactory -{ - private static readonly AsyncLocal Holder = new AsyncLocal(); - - private readonly IMessagePropertiesAccessor _messagePropertiesAccessor; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly string _header; - - public CorrelationIdFactory(IMessagePropertiesAccessor messagePropertiesAccessor, - IHttpContextAccessor httpContextAccessor, HttpClientSettings httpClientOptions) - { - _messagePropertiesAccessor = messagePropertiesAccessor; - _httpContextAccessor = httpContextAccessor; - _header = httpClientOptions.CorrelationIdHeader; - } - - private static string CorrelationId { - get => Holder.Value?.Id; - set { - var holder = Holder.Value; - if (holder is { }) - { - holder.Id = null; - } - - if (value is { }) - { - Holder.Value = new CorrelationIdHolder { Id = value }; - } - } - } - - private class CorrelationIdHolder - { - public string Id; - } - - public string Create() - { - if (!string.IsNullOrWhiteSpace(CorrelationId)) - { - return CorrelationId; - } - - var correlationId = _messagePropertiesAccessor.MessageProperties?.CorrelationId; - if (!string.IsNullOrWhiteSpace(correlationId)) - { - CorrelationId = correlationId; - return CorrelationId; - } - - if (string.IsNullOrWhiteSpace(_header) || _httpContextAccessor.HttpContext is null) - { - CorrelationId = CreateId(); - return CorrelationId; - } - - if (!_httpContextAccessor.HttpContext.Request.Headers.TryGetValue(_header, out var id)) - { - CorrelationId = CreateId(); - return CorrelationId; - } - - correlationId = id.ToString(); - CorrelationId = string.IsNullOrWhiteSpace(correlationId) ? CreateId() : correlationId; - - return CorrelationId; - } - - private static string CreateId() => Guid.NewGuid().ToString("N"); -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/DTO/AuthDto.cs b/templates/template/src/Genocs.Template.Application/DTO/AuthDto.cs deleted file mode 100644 index 42354849..00000000 --- a/templates/template/src/Genocs.Template.Application/DTO/AuthDto.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Genocs.Template.Application.DTO; - -public class AuthDto -{ - public Guid UserId { get; set; } - public string Username { get; set; } - public string Role { get; set; } - public string AccessToken { get; set; } - public string RefreshToken { get; set; } - public long Expires { get; set; } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/DTO/PagedDto.cs b/templates/template/src/Genocs.Template.Application/DTO/PagedDto.cs deleted file mode 100644 index f4eeb50e..00000000 --- a/templates/template/src/Genocs.Template.Application/DTO/PagedDto.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Genocs.Template.Application.DTO; - - -/// -/// It will be removed with Core implementation -/// -/// -public class PagedDto -{ - public IEnumerable Items { get; set; } - public bool Empty => Items is null || !Items.Any(); - public int CurrentPage { get; set; } - public int ResultsPerPage { get; set; } - public int TotalPages { get; set; } - public long TotalResults { get; set; } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/DTO/UserDetailsDto.cs b/templates/template/src/Genocs.Template.Application/DTO/UserDetailsDto.cs deleted file mode 100644 index eb1554c1..00000000 --- a/templates/template/src/Genocs.Template.Application/DTO/UserDetailsDto.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Genocs.Template.Application.DTO; - -/// -/// The UserDetails Dto -/// -public class UserDetailsDto : UserDto -{ - public string Email { get; set; } - public string Role { get; set; } - public IEnumerable? Permissions { get; set; } - public decimal Funds { get; set; } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/DTO/UserDto.cs b/templates/template/src/Genocs.Template.Application/DTO/UserDto.cs deleted file mode 100644 index 390f9fa5..00000000 --- a/templates/template/src/Genocs.Template.Application/DTO/UserDto.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Genocs.Template.Application.DTO; - -public class UserDto -{ - public Guid Id { get; set; } - public string Name { get; set; } - public DateTime CreatedAt { get; set; } - public bool Locked { get; set; } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Decorators/LoggingCommandHandlerDecorator.cs b/templates/template/src/Genocs.Template.Application/Decorators/LoggingCommandHandlerDecorator.cs deleted file mode 100644 index 8b2e6712..00000000 --- a/templates/template/src/Genocs.Template.Application/Decorators/LoggingCommandHandlerDecorator.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Genocs.Common.Types; -using Genocs.Core.CQRS.Commands; -using Genocs.Core.Extensions; -using Genocs.HTTP; -using Microsoft.Extensions.Logging; -using Serilog.Context; - -namespace Genocs.Template.Application.Decorators; - -[Decorator] -internal sealed class LoggingCommandHandlerDecorator : ICommandHandler - where TCommand : class, ICommand -{ - private readonly ICommandHandler _handler; - private readonly ICorrelationIdFactory _correlationIdFactory; - private readonly ILogger> _logger; - - public LoggingCommandHandlerDecorator(ICommandHandler handler, - ICorrelationIdFactory correlationIdFactory, ILogger> logger) - { - _handler = handler; - _correlationIdFactory = correlationIdFactory; - _logger = logger; - } - - public async Task HandleAsync(TCommand command, CancellationToken cancellationToken = default) - { - var correlationId = _correlationIdFactory.Create(); - using (LogContext.PushProperty("CorrelationId", correlationId)) - { - var name = command.GetType().Name.Underscore(); - _logger.LogInformation($"Handling a command: '{name}'..."); - await _handler.HandleAsync(command); - } - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Decorators/LoggingEventHandlerDecorator.cs b/templates/template/src/Genocs.Template.Application/Decorators/LoggingEventHandlerDecorator.cs deleted file mode 100644 index f3700ffa..00000000 --- a/templates/template/src/Genocs.Template.Application/Decorators/LoggingEventHandlerDecorator.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Genocs.Common.Types; -using Genocs.Core.CQRS.Events; -using Genocs.Core.Extensions; -using Genocs.HTTP; -using Microsoft.Extensions.Logging; -using Serilog.Context; - -namespace Genocs.Template.Application.Decorators; - -[Decorator] -internal sealed class LoggingEventHandlerDecorator : IEventHandler - where TEvent : class, IEvent -{ - private readonly IEventHandler _handler; - private readonly ICorrelationIdFactory _correlationIdFactory; - private readonly ILogger> _logger; - - public LoggingEventHandlerDecorator(IEventHandler handler, ICorrelationIdFactory correlationIdFactory, - ILogger> logger) - { - _handler = handler; - _correlationIdFactory = correlationIdFactory; - _logger = logger; - } - - public async Task HandleAsync(TEvent @event, CancellationToken cancellationToken = default) - { - var correlationId = _correlationIdFactory.Create(); - using (LogContext.PushProperty("CorrelationId", correlationId)) - { - var name = @event.GetType().Name.Underscore(); - _logger.LogInformation($"Handling an event: '{name}'..."); - await _handler.HandleAsync(@event); - } - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Decorators/OutboxCommandHandlerDecorator.cs b/templates/template/src/Genocs.Template.Application/Decorators/OutboxCommandHandlerDecorator.cs deleted file mode 100644 index d033f3af..00000000 --- a/templates/template/src/Genocs.Template.Application/Decorators/OutboxCommandHandlerDecorator.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Genocs.Common.Types; -using Genocs.Core.CQRS.Commands; -using Genocs.MessageBrokers; -using Genocs.MessageBrokers.Outbox; -using Genocs.MessageBrokers.Outbox.Options; - -namespace Genocs.Template.Application.Decorators; - -[Decorator] -internal sealed class OutboxCommandHandlerDecorator : ICommandHandler - where TCommand : class, ICommand -{ - private readonly ICommandHandler _handler; - private readonly IMessageOutbox _outbox; - private readonly string _messageId; - private readonly bool _enabled; - - public OutboxCommandHandlerDecorator(ICommandHandler handler, IMessageOutbox outbox, - OutboxSettings outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) - { - _handler = handler; - _outbox = outbox; - _enabled = outboxOptions.Enabled; - - var messageProperties = messagePropertiesAccessor.MessageProperties; - _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId) - ? Guid.NewGuid().ToString("N") - : messageProperties.MessageId; - } - - public Task HandleAsync(TCommand command, CancellationToken cancellationToken = default) - => _enabled - ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(command)) - : _handler.HandleAsync(command); -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Decorators/OutboxEventHandlerDecorator.cs b/templates/template/src/Genocs.Template.Application/Decorators/OutboxEventHandlerDecorator.cs deleted file mode 100644 index c115dcb0..00000000 --- a/templates/template/src/Genocs.Template.Application/Decorators/OutboxEventHandlerDecorator.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Genocs.Common.Types; -using Genocs.Core.CQRS.Events; -using Genocs.MessageBrokers; -using Genocs.MessageBrokers.Outbox; -using Genocs.MessageBrokers.Outbox.Options; - -namespace Genocs.Template.Application.Decorators; - -[Decorator] -internal sealed class OutboxEventHandlerDecorator : IEventHandler - where TEvent : class, IEvent -{ - private readonly IEventHandler _handler; - private readonly IMessageOutbox _outbox; - private readonly string _messageId; - private readonly bool _enabled; - - public OutboxEventHandlerDecorator(IEventHandler handler, IMessageOutbox outbox, - OutboxSettings outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) - { - _handler = handler; - _outbox = outbox; - _enabled = outboxOptions.Enabled; - - var messageProperties = messagePropertiesAccessor.MessageProperties; - _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId) - ? Guid.NewGuid().ToString("N") - : messageProperties.MessageId; - } - - public Task HandleAsync(TEvent @event, CancellationToken cancellationToken = default) - => _enabled - ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(@event)) - : _handler.HandleAsync(@event); - -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Entities/AggregateId.cs b/templates/template/src/Genocs.Template.Application/Domain/Entities/AggregateId.cs deleted file mode 100644 index 939fc9d2..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Entities/AggregateId.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace Genocs.Template.Application.Domain.Entities; - -public class AggregateId : IEquatable -{ - public Guid Value { get; } - - public AggregateId() - { - Value = Guid.NewGuid(); - } - - public AggregateId(Guid value) - { - Value = value; - } - - /// - /// The Equals operator - /// - /// - /// - public bool Equals(AggregateId? other) - { - if (ReferenceEquals(null, other)) return false; - return ReferenceEquals(this, other) || Value.Equals(other.Value); - } - - public override bool Equals(object? obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - return obj.GetType() == GetType() && Equals((AggregateId)obj); - } - - public override int GetHashCode() - => Value.GetHashCode(); - - public static implicit operator Guid(AggregateId id) - => id.Value; - - public static implicit operator AggregateId(Guid id) - => new AggregateId(id); - - /// - /// Return the string data - /// - /// string description about the object - public override string ToString() - => Value.ToString(); -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Entities/AggregateRoot.cs b/templates/template/src/Genocs.Template.Application/Domain/Entities/AggregateRoot.cs deleted file mode 100644 index 7c2e06c3..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Entities/AggregateRoot.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Genocs.Template.Application.Domain.Entities; - -/// -/// The aggregate root class -/// -public abstract class AggregateRoot -{ - public AggregateId Id { get; protected set; } - public int Version { get; protected set; } - - protected AggregateRoot(AggregateId id, int version = 0) - { - Id = id; - Version = version; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Entities/RefreshToken.cs b/templates/template/src/Genocs.Template.Application/Domain/Entities/RefreshToken.cs deleted file mode 100644 index 8a85cdce..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Entities/RefreshToken.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Genocs.Template.Application.Domain.Exceptions; - -namespace Genocs.Template.Application.Domain.Entities; - -public class RefreshToken : AggregateRoot -{ - public AggregateId UserId { get; private set; } - public string Token { get; private set; } - public DateTime CreatedAt { get; private set; } - public DateTime? RevokedAt { get; private set; } - public bool Revoked - => RevokedAt.HasValue; - - public RefreshToken(AggregateId id, AggregateId userId, string token, DateTime createdAt, - DateTime? revokedAt = null) : base(id) - { - if (string.IsNullOrWhiteSpace(token)) - { - throw new EmptyRefreshTokenException(); - } - - UserId = userId; - Token = token; - CreatedAt = createdAt; - RevokedAt = revokedAt; - } - - public void Revoke(DateTime revokedAt) - { - if (Revoked) - { - throw new RevokedRefreshTokenException(); - } - - RevokedAt = revokedAt; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Entities/Role.cs b/templates/template/src/Genocs.Template.Application/Domain/Entities/Role.cs deleted file mode 100644 index 5c42b8b8..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Entities/Role.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Genocs.Template.Application.Domain.Entities; - -public static class Role -{ - public const string User = "user"; - public const string Admin = "admin"; - - public static bool IsValid(string role) - { - if (string.IsNullOrWhiteSpace(role)) - { - return false; - } - - role = role.ToLowerInvariant(); - - return role == User || role == Admin; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Entities/User.cs b/templates/template/src/Genocs.Template.Application/Domain/Entities/User.cs deleted file mode 100644 index fb08d2ad..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Entities/User.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Genocs.Template.Application.Domain.Exceptions; - -namespace Genocs.Template.Application.Domain.Entities; - -public class User : AggregateRoot -{ - public string Email { get; private set; } - public string Name { get; private set; } - public string Role { get; private set; } - public string Password { get; private set; } - public DateTime CreatedAt { get; private set; } - public IEnumerable? Permissions { get; private set; } - public bool Locked { get; private set; } - - public User(Guid id, string email, string name, string password, string role, DateTime createdAt, - IEnumerable? permissions = null, bool locked = false) : base(id) - { - if (string.IsNullOrWhiteSpace(email)) - { - throw new InvalidEmailException(email); - } - - if (string.IsNullOrWhiteSpace(name)) - { - throw new InvalidNameException(name); - } - - if (string.IsNullOrWhiteSpace(password)) - { - throw new InvalidPasswordException(); - } - - if (!Entities.Role.IsValid(role)) - { - throw new InvalidRoleException(role); - } - - Id = id; - Email = email.ToLowerInvariant(); - Name = name.Trim(); - Password = password; - Role = role.ToLowerInvariant(); - CreatedAt = createdAt; - Permissions = permissions ?? Enumerable.Empty(); - Locked = locked; - } - - - public bool Lock() - { - if (Locked) - { - return false; - } - - Locked = true; - return true; - } - - public bool Unlock() - { - if (!Locked) - { - return false; - } - - Locked = false; - return true; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/DomainException.cs b/templates/template/src/Genocs.Template.Application/Domain/Exceptions/DomainException.cs deleted file mode 100644 index 88a5f925..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/DomainException.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Genocs.Template.Application.Domain.Exceptions; - -public abstract class DomainException : Exception -{ - protected DomainException(string message) : base(message) - { - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/EmailInUseException.cs b/templates/template/src/Genocs.Template.Application/Domain/Exceptions/EmailInUseException.cs deleted file mode 100644 index 2b60564a..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/EmailInUseException.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Genocs.Template.Application.Domain.Exceptions; - -public class EmailInUseException : DomainException -{ - public string Email { get; } - - public EmailInUseException(string email) : base($"Email {email} is already in use.") - { - Email = email; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/EmptyRefreshTokenException.cs b/templates/template/src/Genocs.Template.Application/Domain/Exceptions/EmptyRefreshTokenException.cs deleted file mode 100644 index 507111f8..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/EmptyRefreshTokenException.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Genocs.Template.Application.Domain.Exceptions; - -public class EmptyRefreshTokenException : DomainException -{ - public EmptyRefreshTokenException() : base("Empty refresh token.") - { - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidAggregateIdException.cs b/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidAggregateIdException.cs deleted file mode 100644 index c7408daf..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidAggregateIdException.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Genocs.Template.Application.Domain.Exceptions; - -public class InvalidAggregateIdException : DomainException -{ - public InvalidAggregateIdException() : base($"Invalid aggregate id.") - { - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidCredentialsException.cs b/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidCredentialsException.cs deleted file mode 100644 index 3ae16a0c..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidCredentialsException.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Genocs.Template.Application.Domain.Exceptions; - -public class InvalidCredentialsException : DomainException -{ - public string Email { get; } - - public InvalidCredentialsException(string email) : base("Invalid credentials.") - { - Email = email; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidEmailException.cs b/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidEmailException.cs deleted file mode 100644 index 69961afb..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidEmailException.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Genocs.Template.Application.Domain.Exceptions; - -public class InvalidEmailException : DomainException -{ - public InvalidEmailException(string email) : base($"Invalid email: {email}.") - { - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidNameException.cs b/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidNameException.cs deleted file mode 100644 index 0d17349c..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidNameException.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Genocs.Template.Application.Domain.Exceptions; - -public class InvalidNameException : DomainException -{ - public InvalidNameException(string name) : base($"Invalid name: {name}.") - { - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidPasswordException.cs b/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidPasswordException.cs deleted file mode 100644 index 4e0800b2..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidPasswordException.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Genocs.Template.Application.Domain.Exceptions; - -public class InvalidPasswordException : DomainException -{ - public InvalidPasswordException() : base($"Invalid password.") - { - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidRefreshTokenException.cs b/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidRefreshTokenException.cs deleted file mode 100644 index d6245567..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidRefreshTokenException.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Genocs.Template.Application.Domain.Exceptions; - -public class InvalidRefreshTokenException : DomainException -{ - public InvalidRefreshTokenException() : base("Invalid refresh token.") - { - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidRoleException.cs b/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidRoleException.cs deleted file mode 100644 index 821caae2..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/InvalidRoleException.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Genocs.Template.Application.Domain.Exceptions; - -public class InvalidRoleException : DomainException -{ - public InvalidRoleException(string role) : base($"Invalid role: {role}.") - { - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/NameInUseException.cs b/templates/template/src/Genocs.Template.Application/Domain/Exceptions/NameInUseException.cs deleted file mode 100644 index 07c13d5f..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/NameInUseException.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Genocs.Template.Application.Domain.Exceptions; - -public class NameInUseException : DomainException -{ - public string Name { get; } - - public NameInUseException(string name) : base($"Name {name} is already in use.") - { - Name = name; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/RevokedRefreshTokenException.cs b/templates/template/src/Genocs.Template.Application/Domain/Exceptions/RevokedRefreshTokenException.cs deleted file mode 100644 index 9eaba101..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/RevokedRefreshTokenException.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Genocs.Template.Application.Domain.Exceptions; - -public class RevokedRefreshTokenException : DomainException -{ - public RevokedRefreshTokenException() : base("Revoked refresh token.") - { - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/UserLockedException.cs b/templates/template/src/Genocs.Template.Application/Domain/Exceptions/UserLockedException.cs deleted file mode 100644 index d783d73f..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Exceptions/UserLockedException.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Genocs.Template.Application.Domain.Exceptions; - -public class UserLockedException : DomainException -{ - public Guid UserId { get; } - - public UserLockedException(Guid userId) : base($"User with ID: '{userId}' is locked.") - { - UserId = userId; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Repositories/IRefreshTokenRepository.cs b/templates/template/src/Genocs.Template.Application/Domain/Repositories/IRefreshTokenRepository.cs deleted file mode 100644 index 882f5b4c..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Repositories/IRefreshTokenRepository.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Genocs.Template.Application.Domain.Entities; - -namespace Genocs.Template.Application.Domain.Repositories; - -public interface IRefreshTokenRepository -{ - Task GetAsync(string token); - Task AddAsync(RefreshToken refreshToken); - Task UpdateAsync(RefreshToken refreshToken); -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Domain/Repositories/IUserRepository.cs b/templates/template/src/Genocs.Template.Application/Domain/Repositories/IUserRepository.cs deleted file mode 100644 index 245ddeb0..00000000 --- a/templates/template/src/Genocs.Template.Application/Domain/Repositories/IUserRepository.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Genocs.Template.Application.Domain.Entities; - -namespace Genocs.Template.Application.Domain.Repositories; - -public interface IUserRepository -{ - Task GetAsync(AggregateId id); - Task GetByEmailAsync(string email); - Task GetByNameAsync(string name); - Task AddAsync(User user); - Task UpdateAsync(User user); -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Events/SignedIn.cs b/templates/template/src/Genocs.Template.Application/Events/SignedIn.cs deleted file mode 100644 index 314077a7..00000000 --- a/templates/template/src/Genocs.Template.Application/Events/SignedIn.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Genocs.Core.CQRS.Events; - -namespace Genocs.Template.Application.Events; - -public class SignedIn : IEvent -{ - public Guid UserId { get; } - - public SignedIn(Guid userId) - { - UserId = userId; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Events/UserCreated.cs b/templates/template/src/Genocs.Template.Application/Events/UserCreated.cs deleted file mode 100644 index 7bbd460e..00000000 --- a/templates/template/src/Genocs.Template.Application/Events/UserCreated.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Genocs.Core.CQRS.Events; - -namespace Genocs.Template.Application.Events; - -public class UserCreated : IEvent -{ - public Guid UserId { get; } - public string Name { get; } - public string Role { get; } - - public UserCreated(Guid userId, string name, string role) - { - UserId = userId; - Name = name; - Role = role; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Events/UserLocked.cs b/templates/template/src/Genocs.Template.Application/Events/UserLocked.cs deleted file mode 100644 index 70b2c536..00000000 --- a/templates/template/src/Genocs.Template.Application/Events/UserLocked.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Genocs.Core.CQRS.Events; - -namespace Genocs.Template.Application.Events; - -public class UserLocked : IEvent -{ - public Guid UserId { get; } - - public UserLocked(Guid userId) - { - UserId = userId; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Events/UserUnlocked.cs b/templates/template/src/Genocs.Template.Application/Events/UserUnlocked.cs deleted file mode 100644 index 9b4a8aaa..00000000 --- a/templates/template/src/Genocs.Template.Application/Events/UserUnlocked.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Genocs.Core.CQRS.Events; - -namespace Genocs.Template.Application.Events; - -public class UserUnlocked : IEvent -{ - public Guid UserId { get; } - - public UserUnlocked(Guid userId) - { - UserId = userId; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Exceptions/AppException.cs b/templates/template/src/Genocs.Template.Application/Exceptions/AppException.cs deleted file mode 100644 index f13d5238..00000000 --- a/templates/template/src/Genocs.Template.Application/Exceptions/AppException.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Genocs.Template.Application.Exceptions; - -public abstract class AppException : Exception -{ - protected AppException(string message) : base(message) - { - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Exceptions/ExceptionToMessageMapper.cs b/templates/template/src/Genocs.Template.Application/Exceptions/ExceptionToMessageMapper.cs deleted file mode 100644 index de24f712..00000000 --- a/templates/template/src/Genocs.Template.Application/Exceptions/ExceptionToMessageMapper.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Genocs.MessageBrokers.RabbitMQ; - -namespace Genocs.Template.Application.Exceptions; - -internal sealed class ExceptionToMessageMapper : IExceptionToMessageMapper -{ - public object Map(Exception exception, object message) => null; -} - diff --git a/templates/template/src/Genocs.Template.Application/Exceptions/ExceptionToResponseMapper.cs b/templates/template/src/Genocs.Template.Application/Exceptions/ExceptionToResponseMapper.cs deleted file mode 100644 index 5475e079..00000000 --- a/templates/template/src/Genocs.Template.Application/Exceptions/ExceptionToResponseMapper.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Genocs.Core.Extensions; -using Genocs.Template.Application.Domain.Exceptions; -using Genocs.WebApi.Exceptions; -using System.Collections.Concurrent; -using System.Net; - -namespace Genocs.Template.Application.Exceptions; - -public class ExceptionToResponseMapper : IExceptionToResponseMapper -{ - private static readonly ConcurrentDictionary Codes = new ConcurrentDictionary(); - - public ExceptionResponse Map(Exception exception) - => exception switch - { - DomainException ex => new ExceptionResponse(new { code = GetCode(ex), reason = ex.Message }, - HttpStatusCode.BadRequest), - AppException ex => new ExceptionResponse(new { code = GetCode(ex), reason = ex.Message }, - HttpStatusCode.BadRequest), - _ => new ExceptionResponse(new { code = "error", reason = "There was an error." }, - HttpStatusCode.BadRequest) - }; - - private static string GetCode(Exception exception) - { - var type = exception.GetType(); - if (Codes.TryGetValue(type, out var code)) - { - return code; - } - - var exceptionCode = exception.GetType().Name.Underscore().Replace("_exception", string.Empty); - Codes.TryAdd(type, exceptionCode); - - return exceptionCode; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Exceptions/Extensions.cs b/templates/template/src/Genocs.Template.Application/Exceptions/Extensions.cs deleted file mode 100644 index 0f383875..00000000 --- a/templates/template/src/Genocs.Template.Application/Exceptions/Extensions.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Genocs.Core.Extensions; - -namespace Genocs.Template.Application.Exceptions; - -internal static class Extensions -{ - public static string GetExceptionCode(this Exception exception) - => exception.GetType().Name.Underscore().Replace("_exception", string.Empty); -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Exceptions/UserNotFoundException.cs b/templates/template/src/Genocs.Template.Application/Exceptions/UserNotFoundException.cs deleted file mode 100644 index a5fd0e20..00000000 --- a/templates/template/src/Genocs.Template.Application/Exceptions/UserNotFoundException.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Genocs.Template.Application.Exceptions; - -public class UserNotFoundException : AppException -{ - public Guid UserId { get; } - - public UserNotFoundException(Guid userId) : base($"User with ID: '{userId}' was not found.") - { - UserId = userId; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Extensions.cs b/templates/template/src/Genocs.Template.Application/Extensions.cs deleted file mode 100644 index d20ec8d2..00000000 --- a/templates/template/src/Genocs.Template.Application/Extensions.cs +++ /dev/null @@ -1,128 +0,0 @@ -using Genocs.Auth; -using Genocs.Common.Options; -using Genocs.Core.Builders; -using Genocs.Core.CQRS.Commands; -using Genocs.Core.CQRS.Events; -using Genocs.Core.CQRS.Queries; -using Genocs.HTTP; -using Genocs.Template.Application.Mongo; -using Genocs.MessageBrokers; -using Genocs.MessageBrokers.CQRS; -using Genocs.MessageBrokers.Outbox; -using Genocs.MessageBrokers.Outbox.MongoDB; -using Genocs.MessageBrokers.RabbitMQ; -using Genocs.Metrics.AppMetrics; -using Genocs.Persistence.MongoDb.Legacy; -using Genocs.Persistence.Redis; -using Genocs.Template.Application.Commands; -using Genocs.Template.Application.Decorators; -using Genocs.Template.Application.Domain.Repositories; -using Genocs.Template.Application.Exceptions; -using Genocs.Template.Application.Logging; -using Genocs.Template.Application.Mongo.Documents; -using Genocs.Template.Application.Mongo.Repositories; -using Genocs.Template.Application.Services; -using Genocs.Tracing.Jaeger; -using Genocs.WebApi; -using Genocs.WebApi.CQRS; -using Genocs.WebApi.Swagger; -using Genocs.WebApi.Swagger.Docs; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; -using System.Text; - -namespace Genocs.Template.Application; - -public static class Extensions -{ - public static IGenocsBuilder AddCore(this IGenocsBuilder builder) - { - builder.Services - .AddScoped() - .AddSingleton() - .AddSingleton() - .AddSingleton, PasswordHasher>() - .AddSingleton() - .AddSingleton() - .AddScoped() - .AddScoped() - .AddScoped(); - - builder - .AddErrorHandler() - .AddExceptionToMessageMapper() - .AddCommandHandlers() - .AddEventHandlers() - .AddQueryHandlers() - .AddInMemoryCommandDispatcher() - .AddInMemoryEventDispatcher() - .AddInMemoryQueryDispatcher() - .AddJwt() - .AddHttpClient() - .AddRabbitMq() - .AddMessageOutbox(o => o.AddMongo()) - .AddMongo() - .AddRedis() - .AddJaeger() - .AddMetrics() - .AddMongoRepository("refreshTokens") - .AddMongoRepository("users") - .AddWebApiSwaggerDocs(); - - builder.Services.AddSingleton(); - - builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(LoggingCommandHandlerDecorator<>)); - builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(LoggingEventHandlerDecorator<>)); - builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(OutboxCommandHandlerDecorator<>)); - builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(OutboxEventHandlerDecorator<>)); - - return builder; - } - - public static long ToUnixTimeMilliseconds(this DateTime dateTime) - => new DateTimeOffset(dateTime).ToUnixTimeMilliseconds(); - - public static IApplicationBuilder UseCore(this IApplicationBuilder app) - { - app.UseMiddleware() - .UseErrorHandler() - .UseJaeger() - .UseSwaggerDocs() - .UseGenocs() - .UseAccessTokenValidator() - .UseMongo() - .UsePublicContracts() - .UseAuthentication() - .UseMetrics() - .UseRabbitMq() - .SubscribeCommand(); - - return app; - } - - public static async Task GetAppName(this HttpContext httpContext) - => await httpContext.Response.WriteAsync(httpContext.RequestServices?.GetService()?.Name ?? string.Empty); - - internal static CorrelationContext GetCorrelationContext(this IHttpContextAccessor accessor) - => accessor.HttpContext?.Request.Headers.TryGetValue("Correlation-Context", out var json) is true - ? JsonConvert.DeserializeObject(json.FirstOrDefault()) - : null; - - internal static string GetSpanContext(this IMessageProperties messageProperties, string header) - { - if (messageProperties is null) - { - return string.Empty; - } - - if (messageProperties.Headers.TryGetValue(header, out var span) && span is byte[] spanBytes) - { - return Encoding.UTF8.GetString(spanBytes); - } - - return string.Empty; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Genocs.Template.Application.csproj b/templates/template/src/Genocs.Template.Application/Genocs.Template.Application.csproj deleted file mode 100644 index 392b215d..00000000 --- a/templates/template/src/Genocs.Template.Application/Genocs.Template.Application.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - net7.0 - enable - enable - - - - - - - - - - - - - - - - - - - diff --git a/templates/template/src/Genocs.Template.Application/Logging/LogContextMiddleware.cs b/templates/template/src/Genocs.Template.Application/Logging/LogContextMiddleware.cs deleted file mode 100644 index 2746e0ad..00000000 --- a/templates/template/src/Genocs.Template.Application/Logging/LogContextMiddleware.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Genocs.HTTP; -using Microsoft.AspNetCore.Http; -using Serilog.Context; - -namespace Genocs.Template.Application.Logging; - -internal class LogContextMiddleware : IMiddleware -{ - private readonly ICorrelationIdFactory _correlationIdFactory; - - public LogContextMiddleware(ICorrelationIdFactory correlationIdFactory) - { - _correlationIdFactory = correlationIdFactory; - } - - public async Task InvokeAsync(HttpContext context, RequestDelegate next) - { - var correlationId = _correlationIdFactory.Create(); - using (LogContext.PushProperty("CorrelationId", correlationId)) - { - await next(context); - } - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Mongo/Documents/RefreshTokenDocument.cs b/templates/template/src/Genocs.Template.Application/Mongo/Documents/RefreshTokenDocument.cs deleted file mode 100644 index be1c1a40..00000000 --- a/templates/template/src/Genocs.Template.Application/Mongo/Documents/RefreshTokenDocument.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Genocs.Common.Types; -using Genocs.Template.Application.Domain.Entities; - -namespace Genocs.Template.Application.Mongo.Documents; - -public class RefreshTokenDocument : IIdentifiable -{ - public Guid Id { get; set; } - public Guid UserId { get; set; } - public string Token { get; set; } - public DateTime CreatedAt { get; set; } - public DateTime? RevokedAt { get; set; } - - public RefreshTokenDocument() - { - } - - public RefreshTokenDocument(RefreshToken refreshToken) - { - Id = refreshToken.Id; - UserId = refreshToken.UserId; - Token = refreshToken.Token; - CreatedAt = refreshToken.CreatedAt; - RevokedAt = refreshToken.RevokedAt; - } - - public RefreshToken ToEntity() => new RefreshToken(Id, UserId, Token, CreatedAt, RevokedAt); -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Mongo/Documents/UserDocument.cs b/templates/template/src/Genocs.Template.Application/Mongo/Documents/UserDocument.cs deleted file mode 100644 index b0d9c7d4..00000000 --- a/templates/template/src/Genocs.Template.Application/Mongo/Documents/UserDocument.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Genocs.Common.Types; -using Genocs.Template.Application.Domain.Entities; - -namespace Genocs.Template.Application.Mongo.Documents; - -public class UserDocument : IIdentifiable -{ - public Guid Id { get; set; } - public string Email { get; set; } - public string Name { get; set; } - public string Role { get; set; } - public string Password { get; set; } - public DateTime CreatedAt { get; set; } - public IEnumerable? Permissions { get; set; } - public bool Locked { get; set; } - - public UserDocument() - { - } - - public UserDocument(User user) - { - Id = user.Id; - Email = user.Email; - Name = user.Name; - Role = user.Role; - Password = user.Password; - CreatedAt = user.CreatedAt; - Permissions = user.Permissions; - Locked = user.Locked; - } - - public User ToEntity() => new User(Id, Email, Name, Password, Role, CreatedAt, Permissions, Locked); -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Mongo/Extensions.cs b/templates/template/src/Genocs.Template.Application/Mongo/Extensions.cs deleted file mode 100644 index d7131890..00000000 --- a/templates/template/src/Genocs.Template.Application/Mongo/Extensions.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Genocs.Persistence.MongoDb.Legacy; -using Genocs.Template.Application.Mongo.Documents; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using MongoDB.Driver; - -namespace Genocs.Template.Application.Mongo; - -public static class Extensions -{ - public static IApplicationBuilder UseMongo(this IApplicationBuilder builder) - { - using var scope = builder.ApplicationServices.CreateScope(); - var users = scope.ServiceProvider.GetService>().Collection; - var userBuilder = Builders.IndexKeys; - Task.Run(async () => await users.Indexes.CreateManyAsync( - new[] - { - new CreateIndexModel(userBuilder.Ascending(i => i.Email), - new CreateIndexOptions - { - Unique = true - }), - new CreateIndexModel(userBuilder.Ascending(i => i.Name), - new CreateIndexOptions - { - Unique = true - }) - })); - - return builder; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Mongo/Queries/Handlers/BrowseUsersHandler.cs b/templates/template/src/Genocs.Template.Application/Mongo/Queries/Handlers/BrowseUsersHandler.cs deleted file mode 100644 index 56560041..00000000 --- a/templates/template/src/Genocs.Template.Application/Mongo/Queries/Handlers/BrowseUsersHandler.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Genocs.Core.CQRS.Queries; -using Genocs.Persistence.MongoDb.Legacy; -using Genocs.Template.Application.DTO; -using Genocs.Template.Application.Mongo.Documents; -using Genocs.Template.Application.Queries; -using MongoDB.Driver; -using MongoDB.Driver.Linq; - -namespace Genocs.Template.Application.Mongo.Queries.Handlers; - -public class BrowseUsersHandler : IQueryHandler> -{ - private readonly IMongoDatabase _database; - - public BrowseUsersHandler(IMongoDatabase database) - { - _database = database; - } - - public async Task> HandleAsync(BrowseUsers query, CancellationToken cancellationToken = default) - { - var result = await _database.GetCollection("users") - .AsQueryable() - .OrderByDescending(x => x.CreatedAt) - .PaginateAsync(query); - - var pagedResult = PagedResult.From(result, result.Items.Select(x => Map(x))); - return new PagedDto - { - CurrentPage = pagedResult.CurrentPage, - TotalPages = pagedResult.TotalPages, - ResultsPerPage = pagedResult.ResultsPerPage, - TotalResults = pagedResult.TotalResults, - Items = pagedResult.Items - }; - } - - private static UserDto Map(UserDocument user) - => new UserDto - { - Id = user.Id, - Name = user.Name, - CreatedAt = user.CreatedAt, - Locked = user.Locked - }; -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Mongo/Queries/Handlers/GetUserHandler.cs b/templates/template/src/Genocs.Template.Application/Mongo/Queries/Handlers/GetUserHandler.cs deleted file mode 100644 index 4369ec7d..00000000 --- a/templates/template/src/Genocs.Template.Application/Mongo/Queries/Handlers/GetUserHandler.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Genocs.Core.CQRS.Queries; -using Genocs.Persistence.MongoDb.Legacy; -using Genocs.Template.Application.DTO; -using Genocs.Template.Application.Mongo.Documents; -using Genocs.Template.Application.Queries; - -namespace Genocs.Template.Application.Mongo.Queries.Handlers; - -public class GetUserHandler : IQueryHandler -{ - private readonly IMongoRepository _userRepository; - - public GetUserHandler(IMongoRepository userRepository) - { - _userRepository = userRepository; - } - - public async Task HandleAsync(GetUser query, CancellationToken cancellationToken = default) - { - var user = await _userRepository.GetAsync(query.UserId); - - return user is null - ? null - : new UserDetailsDto - { - Id = user.Id, - Email = user.Email, - Name = user.Name, - Role = user.Role, - CreatedAt = user.CreatedAt, - Locked = user.Locked, - Permissions = user.Permissions, - }; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Mongo/Repositories/RefreshTokenRepository.cs b/templates/template/src/Genocs.Template.Application/Mongo/Repositories/RefreshTokenRepository.cs deleted file mode 100644 index a22d49b0..00000000 --- a/templates/template/src/Genocs.Template.Application/Mongo/Repositories/RefreshTokenRepository.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Genocs.Persistence.MongoDb.Legacy; -using Genocs.Template.Application.Domain.Entities; -using Genocs.Template.Application.Domain.Repositories; -using Genocs.Template.Application.Mongo.Documents; - -namespace Genocs.Template.Application.Mongo.Repositories; - -public class RefreshTokenRepository : IRefreshTokenRepository -{ - private readonly IMongoRepository _repository; - - public RefreshTokenRepository(IMongoRepository repository) - { - _repository = repository; - } - - public async Task GetAsync(string token) - { - var refreshToken = await _repository.GetAsync(x => x.Token == token); - return refreshToken?.ToEntity(); - } - - public Task AddAsync(RefreshToken refreshToken) - => _repository.AddAsync(new RefreshTokenDocument(refreshToken)); - - public Task UpdateAsync(RefreshToken refreshToken) - => _repository.UpdateAsync(new RefreshTokenDocument(refreshToken)); -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Mongo/Repositories/UserRepository.cs b/templates/template/src/Genocs.Template.Application/Mongo/Repositories/UserRepository.cs deleted file mode 100644 index 51312b73..00000000 --- a/templates/template/src/Genocs.Template.Application/Mongo/Repositories/UserRepository.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Genocs.Persistence.MongoDb.Legacy; -using Genocs.Template.Application.Domain.Entities; -using Genocs.Template.Application.Domain.Repositories; -using Genocs.Template.Application.Mongo.Documents; - -namespace Genocs.Template.Application.Mongo.Repositories; - -public class UserRepository : IUserRepository -{ - private readonly IMongoRepository _repository; - - public UserRepository(IMongoRepository repository) - { - _repository = repository; - } - - public async Task GetAsync(AggregateId id) - { - var document = await _repository.GetAsync(id); - return document?.ToEntity(); - } - - public async Task GetByEmailAsync(string email) - { - if (string.IsNullOrWhiteSpace(email)) - { - return null; - } - - var document = await _repository.GetAsync(x => x.Email == email.ToLowerInvariant()); - return document?.ToEntity(); - } - - public async Task GetByNameAsync(string name) - { - if (string.IsNullOrWhiteSpace(name)) - { - return null; - } - - var document = await _repository.GetAsync(x => x.Name == name); - return document?.ToEntity(); - } - - public Task AddAsync(User user) - => _repository.AddAsync(new UserDocument(user)); - - public Task UpdateAsync(User user) - => _repository.UpdateAsync(new UserDocument(user)); -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Queries/BrowseUsers.cs b/templates/template/src/Genocs.Template.Application/Queries/BrowseUsers.cs deleted file mode 100644 index 5438c1e5..00000000 --- a/templates/template/src/Genocs.Template.Application/Queries/BrowseUsers.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Genocs.Core.CQRS.Queries; -using Genocs.Template.Application.DTO; - -namespace Genocs.Template.Application.Queries; - -public class BrowseUsers : PagedQueryBase, IQuery> -{ - public Guid? UserId { get; set; } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Queries/GetUser.cs b/templates/template/src/Genocs.Template.Application/Queries/GetUser.cs deleted file mode 100644 index ccbeaaba..00000000 --- a/templates/template/src/Genocs.Template.Application/Queries/GetUser.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Genocs.Core.CQRS.Queries; -using Genocs.Template.Application.DTO; - -namespace Genocs.Template.Application.Queries; - -public class GetUser : IQuery -{ - public Guid UserId { get; set; } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Services/IJwtProvider.cs b/templates/template/src/Genocs.Template.Application/Services/IJwtProvider.cs deleted file mode 100644 index 36112b9a..00000000 --- a/templates/template/src/Genocs.Template.Application/Services/IJwtProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Genocs.Template.Application.DTO; - -namespace Genocs.Template.Application.Services; - -/// -/// JwtProvider interface definition -/// -public interface IJwtProvider -{ - AuthDto Create(Guid userId, string username, string role, string? audience = null, - IDictionary>? claims = null); -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Services/IMessageBroker.cs b/templates/template/src/Genocs.Template.Application/Services/IMessageBroker.cs deleted file mode 100644 index 94c3d96b..00000000 --- a/templates/template/src/Genocs.Template.Application/Services/IMessageBroker.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Genocs.Core.CQRS.Events; - -namespace Genocs.Template.Application.Services; - -public interface IMessageBroker -{ - Task PublishAsync(params IEvent[] events); -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Services/IPasswordService.cs b/templates/template/src/Genocs.Template.Application/Services/IPasswordService.cs deleted file mode 100644 index 68bd2ea6..00000000 --- a/templates/template/src/Genocs.Template.Application/Services/IPasswordService.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Genocs.Template.Application.Services; - -public interface IPasswordService -{ - bool IsValid(string hash, string password); - string Hash(string password); -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Services/IRng.cs b/templates/template/src/Genocs.Template.Application/Services/IRng.cs deleted file mode 100644 index bb649493..00000000 --- a/templates/template/src/Genocs.Template.Application/Services/IRng.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Genocs.Template.Application.Services; - -public interface IRng -{ - string Generate(int length = 50, bool removeSpecialChars = false); -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Services/ITokenStorage.cs b/templates/template/src/Genocs.Template.Application/Services/ITokenStorage.cs deleted file mode 100644 index 57996213..00000000 --- a/templates/template/src/Genocs.Template.Application/Services/ITokenStorage.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Genocs.Template.Application.DTO; - -namespace Genocs.Template.Application.Services; - -public interface ITokenStorage -{ - void Set(Guid commandId, AuthDto token); - AuthDto Get(Guid commandId); -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Services/JwtProvider.cs b/templates/template/src/Genocs.Template.Application/Services/JwtProvider.cs deleted file mode 100644 index fd666cd9..00000000 --- a/templates/template/src/Genocs.Template.Application/Services/JwtProvider.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Genocs.Auth; -using Genocs.Template.Application.DTO; - -namespace Genocs.Template.Application.Services; - -public class JwtProvider : IJwtProvider -{ - private readonly IJwtHandler _jwtHandler; - - public JwtProvider(IJwtHandler jwtHandler) - { - _jwtHandler = jwtHandler; - } - - public AuthDto Create(Guid userId, string username, string role, string audience = null, - IDictionary> claims = null) - { - var jwt = _jwtHandler.CreateToken(userId.ToString("N"), role, audience, claims); - - return new AuthDto - { - UserId = userId, - Username = username, - AccessToken = jwt.AccessToken, - Role = jwt.Role, - Expires = jwt.Expires - }; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Services/MessageBroker.cs b/templates/template/src/Genocs.Template.Application/Services/MessageBroker.cs deleted file mode 100644 index 818c30b1..00000000 --- a/templates/template/src/Genocs.Template.Application/Services/MessageBroker.cs +++ /dev/null @@ -1,84 +0,0 @@ -using Genocs.Core.CQRS.Events; -using Genocs.Core.Extensions; -using Genocs.HTTP; -using Genocs.MessageBrokers; -using Genocs.MessageBrokers.Outbox; -using Genocs.MessageBrokers.RabbitMQ; -using Genocs.Template.Application; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; - -namespace Genocs.Template.Application.Services; - -internal class MessageBroker : IMessageBroker -{ - private const string DefaultSpanContextHeader = "span_context"; - private readonly IBusPublisher _busPublisher; - private readonly IMessageOutbox _outbox; - private readonly ICorrelationContextAccessor _contextAccessor; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IMessagePropertiesAccessor _messagePropertiesAccessor; - private readonly ICorrelationIdFactory _correlationIdFactory; - private readonly ILogger _logger; - private readonly string _spanContextHeader; - - public MessageBroker(IBusPublisher busPublisher, IMessageOutbox outbox, - ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor, - IMessagePropertiesAccessor messagePropertiesAccessor, ICorrelationIdFactory correlationIdFactory, - RabbitMQOptions options, ILogger logger) - { - if (options is null) - { - throw new ArgumentNullException(nameof(options)); - } - - _busPublisher = busPublisher ?? throw new ArgumentNullException(nameof(busPublisher)); - _outbox = outbox ?? throw new ArgumentNullException(nameof(outbox)); - _contextAccessor = contextAccessor ?? throw new ArgumentNullException(nameof(contextAccessor)); - _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); - _messagePropertiesAccessor = messagePropertiesAccessor ?? throw new ArgumentNullException(nameof(messagePropertiesAccessor)); - _correlationIdFactory = correlationIdFactory ?? throw new ArgumentNullException(nameof(correlationIdFactory)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _spanContextHeader = string.IsNullOrWhiteSpace(options.SpanContextHeader) - ? DefaultSpanContextHeader - : options.SpanContextHeader; - } - - public Task PublishAsync(params IEvent[] events) => PublishAsync(events?.AsEnumerable()); - - private async Task PublishAsync(IEnumerable? events) - { - if (events is null) - { - return; - } - - var messageProperties = _messagePropertiesAccessor.MessageProperties; - var originatedMessageId = messageProperties?.MessageId; - var correlationId = _correlationIdFactory.Create(); - var spanContext = messageProperties?.GetSpanContext(_spanContextHeader); - var correlationContext = _contextAccessor.CorrelationContext ?? - _httpContextAccessor.GetCorrelationContext(); - var headers = new Dictionary(); - - foreach (var @event in events) - { - if (@event is null) - { - continue; - } - - var messageId = Guid.NewGuid().ToString("N"); - _logger.LogTrace($"Publishing integration event: {@event.GetType().Name.Underscore()} [ID: '{messageId}']."); - if (_outbox.Enabled) - { - await _outbox.SendAsync(@event, originatedMessageId, messageId, correlationId, spanContext, - correlationContext, headers); - continue; - } - - await _busPublisher.PublishAsync(@event, messageId, correlationId, spanContext, correlationContext, - headers); - } - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Services/PasswordService.cs b/templates/template/src/Genocs.Template.Application/Services/PasswordService.cs deleted file mode 100644 index 3f02065e..00000000 --- a/templates/template/src/Genocs.Template.Application/Services/PasswordService.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.AspNetCore.Identity; - -namespace Genocs.Template.Application.Services; - -public class PasswordService : IPasswordService -{ - private readonly IPasswordHasher _passwordHasher; - - public PasswordService(IPasswordHasher passwordHasher) - { - _passwordHasher = passwordHasher; - } - - public bool IsValid(string hash, string password) - => _passwordHasher.VerifyHashedPassword(this, hash, password) != PasswordVerificationResult.Failed; - - public string Hash(string password) - => _passwordHasher.HashPassword(this, password); -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Services/Rng.cs b/templates/template/src/Genocs.Template.Application/Services/Rng.cs deleted file mode 100644 index 669bd2c5..00000000 --- a/templates/template/src/Genocs.Template.Application/Services/Rng.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Security.Cryptography; - -namespace Genocs.Template.Application.Services; - -public class Rng : IRng -{ - private static readonly string[] SpecialChars = new[] { "/", "\\", "=", "+", "?", ":", "&" }; - - public string Generate(int length = 50, bool removeSpecialChars = true) - { - using var rng = RandomNumberGenerator.Create(); - var bytes = new byte[length]; - rng.GetBytes(bytes); - var result = Convert.ToBase64String(bytes); - - return removeSpecialChars - ? SpecialChars.Aggregate(result, (current, chars) => current.Replace(chars, string.Empty)) - : result; - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.Application/Services/TokenStorage.cs b/templates/template/src/Genocs.Template.Application/Services/TokenStorage.cs deleted file mode 100644 index 7736e8f8..00000000 --- a/templates/template/src/Genocs.Template.Application/Services/TokenStorage.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Genocs.Template.Application.DTO; -using Microsoft.Extensions.Caching.Memory; - -namespace Genocs.Template.Application.Services; - -internal class TokenStorage : ITokenStorage -{ - private readonly IMemoryCache _cache; - - public TokenStorage(IMemoryCache cache) - { - _cache = cache ?? throw new ArgumentNullException(nameof(cache)); - } - - public void Set(Guid commandId, AuthDto token) - => _cache.Set(GetKey(commandId), token); - - public AuthDto Get(Guid commandId) - => _cache.Get(GetKey(commandId)); - - private static string GetKey(Guid commandId) - => $"users:tokens:{commandId}"; -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.IntegrationTests/Genocs.Template.IntegrationTests.csproj b/templates/template/src/Genocs.Template.IntegrationTests/Genocs.Template.IntegrationTests.csproj deleted file mode 100644 index eebf48e5..00000000 --- a/templates/template/src/Genocs.Template.IntegrationTests/Genocs.Template.IntegrationTests.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - net7.0 - false - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.UnitTests/Genocs.Template.UnitTests.csproj b/templates/template/src/Genocs.Template.UnitTests/Genocs.Template.UnitTests.csproj deleted file mode 100644 index 55972e5c..00000000 --- a/templates/template/src/Genocs.Template.UnitTests/Genocs.Template.UnitTests.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net7.0 - false - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - diff --git a/templates/template/src/Genocs.Template.WebApi/Genocs.Template.WebApi.csproj b/templates/template/src/Genocs.Template.WebApi/Genocs.Template.WebApi.csproj deleted file mode 100644 index 4fe6f236..00000000 --- a/templates/template/src/Genocs.Template.WebApi/Genocs.Template.WebApi.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - net7.0 - enable - enable - __genocs - Linux - ..\.. - - - - - - - - - - - - - - - - - - - - - diff --git a/templates/template/src/Genocs.Template.WebApi/Program.cs b/templates/template/src/Genocs.Template.WebApi/Program.cs deleted file mode 100644 index 1b370866..00000000 --- a/templates/template/src/Genocs.Template.WebApi/Program.cs +++ /dev/null @@ -1,65 +0,0 @@ -using Genocs.Core.Builders; -using Genocs.Logging; -using Genocs.Template.Application; -using Genocs.Template.Application.Commands; -using Genocs.Template.Application.DTO; -using Genocs.Template.Application.Queries; -using Genocs.Template.Application.Services; -using Genocs.WebApi; -using Genocs.WebApi.CQRS; -using Serilog; -using Serilog.Events; - -Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .MinimumLevel.Override("Microsoft", LogEventLevel.Information) - .MinimumLevel.Override("MassTransit", LogEventLevel.Information) - .Enrich.FromLogContext() - .WriteTo.Console() - .CreateLogger(); - - -var builder = WebApplication.CreateBuilder(args); - -builder.Host - .UseLogging(); -// .UseVault(); - - -var services = builder.Services; - -services.AddGenocs(builder.Configuration) - .AddWebApi() - .AddCore() - .Build(); - -var app = builder.Build(); - -app.UseCore(); -app.UseDispatcherEndpoints(endpoints => endpoints - .Get("", ctx => ctx.GetAppName()) - .Post("sign-in", afterDispatch: (cmd, ctx) => - { - var auth = ctx.RequestServices.GetRequiredService().Get(cmd.Id); - return ctx.Response.WriteJsonAsync(auth); - }) - .Post("sign-up", afterDispatch: (cmd, ctx) => - { - ctx.Response.Headers.Add("user-id", cmd.UserId.ToString()); - return Task.CompletedTask; - }) - .Post("access-tokens/revoke") - .Post("refresh-tokens/use", afterDispatch: (cmd, ctx) => - { - var auth = ctx.RequestServices.GetRequiredService().Get(cmd.Id); - return ctx.Response.WriteJsonAsync(auth); - }) - .Post("refresh-tokens/revoke") - .Get("users/{userId:guid}") - .Get>("users") - .Put("users/{userId:guid}/lock") - .Put("users/{userId:guid}/unlock")); - -app.Run(); - -Log.CloseAndFlush(); diff --git a/templates/template/src/Genocs.Template.WebApi/Properties/launchSettings.json b/templates/template/src/Genocs.Template.WebApi/Properties/launchSettings.json deleted file mode 100644 index 29653af1..00000000 --- a/templates/template/src/Genocs.Template.WebApi/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:61699/", - "sslPort": 44323 - } - }, - "profiles": { - "Genocs.Template.WebApi": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "applicationUrl": "https://localhost:5271;http://localhost:5270" - } - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.WebApi/appsettings.Docker.json b/templates/template/src/Genocs.Template.WebApi/appsettings.Docker.json deleted file mode 100644 index b12f449a..00000000 --- a/templates/template/src/Genocs.Template.WebApi/appsettings.Docker.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "MongoDb": { - "ConnectionString": "mongodb://mongodb" - }, - "Monitoring": { - "Jaeger": "jaeger" - }, - "jaeger": { - "udpHost": "jaeger" - }, - "metrics": { - "influxUrl": "http://influxdb:8086" - }, - "mongo": { - "connectionString": "mongodb://mongodb:27017" - }, - "redis": { - "connectionString": "redis" - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.WebApi/appsettings.json b/templates/template/src/Genocs.Template.WebApi/appsettings.json deleted file mode 100644 index 57681800..00000000 --- a/templates/template/src/Genocs.Template.WebApi/appsettings.json +++ /dev/null @@ -1,241 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Information", - "Microsoft": "Information", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "AllowedHosts": "*", - "MongoDb": { - "ConnectionString": "mongodb://localhost", - "Database": "placeholder" - }, - "Monitoring": { - "Jaeger": "localhost" - }, - "app": { - "name": "genocs.template Service", - "service": "genocs.template-service", - "instance": "000001", - "version": "v1.0", - "displayBanner": true, - "displayVersion": true - }, - "consul": { - "enabled": false, - "url": "http://localhost:8500", - "service": "genocs.template-service", - "address": "docker.for.win.localhost", - "port": "5070", - "pingEnabled": true, - "pingEndpoint": "ping", - "pingInterval": 3, - "removeAfterInterval": 3 - }, - "fabio": { - "enabled": false, - "url": "http://localhost:9999", - "service": "genocs.template-service" - }, - "httpClient": { - "type": "fabio", - "retries": 3, - "services": {}, - "requestMasking": { - "enabled": true, - "maskTemplate": "*****" - }, - "correlationIdHeader": "x-correlation-id" - }, - "logger": { - "level": "information", - "excludePaths": [ - "/", - "/ping", - "/metrics" - ], - "excludeProperties": [ - "api_key", - "access_key", - "ApiKey", - "ApiSecret", - "ClientId", - "ClientSecret", - "ConnectionString", - "Password", - "Email", - "Login", - "Secret", - "Token" - ], - "console": { - "enabled": true - }, - "elk": { - "enabled": false, - "url": "http://localhost:9200" - }, - "file": { - "enabled": true, - "path": "logs/logs.txt", - "interval": "day" - }, - "seq": { - "enabled": false, - "url": "http://localhost:5341", - "apiKey": "secret" - }, - "azure": { - "enabled": false, - "connectionString": "" - }, - "tags": {} - }, - "jaeger": { - "enabled": true, - "serviceName": "genocs.template", - "udpHost": "localhost", - "udpPort": 6831, - "maxPacketSize": 65000, - "sampler": "const", - "excludePaths": [ - "/", - "/ping", - "/metrics" - ] - }, - "jwt": { - "certificate": { - "location": "certs/localhost.pfx", - "password": "test", - "rawData": "" - }, - "issuer": "genocs-identity-service", - "validIssuer": "genocs-identity-service", - "validateAudience": false, - "validateIssuer": true, - "validateLifetime": true, - "expiry": "01:00:00" - }, - "metrics": { - "enabled": true, - "influxEnabled": false, - "prometheusEnabled": false, - "influxUrl": "http://localhost:8086", - "database": "test", - "env": "local", - "interval": 5 - }, - "prometheus": { - "enabled": false, - "endpoint": "/metrics" - }, - "mongo": { - "connectionString": "mongodb://localhost:27017", - "database": "placeholder-service", - "seed": false - }, - "outbox": { - "enabled": true, - "type": "sequential", - "expiry": 3600, - "intervalMilliseconds": 2000, - "inboxCollection": "inbox", - "outboxCollection": "outbox", - "disableTransactions": true - }, - "rabbitMq": { - "connectionName": "genocs.template-service", - "retries": 3, - "retryInterval": 2, - "conventionsCasing": "snakeCase", - "logger": { - "enabled": true - }, - "username": "guest", - "password": "guest", - "virtualHost": "/", - "port": 5672, - "hostnames": [ - "localhost", - "rabbitmq" - ], - "requestedConnectionTimeout": "00:00:30", - "requestedHeartbeat": "00:01:00", - "socketReadTimeout": "00:00:30", - "socketWriteTimeout": "00:00:30", - "continuationTimeout": "00:00:20", - "handshakeContinuationTimeout": "00:00:10", - "networkRecoveryInterval": "00:00:05", - "exchange": { - "declare": true, - "durable": true, - "autoDelete": false, - "type": "topic", - "name": "genocs.template" - }, - "queue": { - "declare": true, - "durable": true, - "exclusive": false, - "autoDelete": false, - "template": "genocs.template-service/{{exchange}}.{{message}}" - }, - "context": { - "enabled": true, - "header": "message_context" - }, - "spanContextHeader": "span_context" - }, - "redis": { - "connectionString": "localhost", - "instance": "genocs.template-service:", - "database": 0 - }, - "swagger": { - "enabled": true, - "reDocEnabled": false, - "name": "v1", - "title": "Genocs.Template API", - "version": "v1", - "routePrefix": "swagger", - "includeSecurity": true - }, - "security": { - "certificate": { - "header": "Certificate" - } - }, - "vault": { - "enabled": false, - "url": "http://localhost:8200", - "authType": "token", - "token": "secret", - "username": "user", - "password": "secret", - "kv": { - "enabled": true, - "engineVersion": 2, - "mountPoint": "kv", - "path": "genocs.template-service/settings" - }, - "pki": { - "enabled": true, - "roleName": "genocs.template-service", - "commonName": "genocs.template-service.demo.io" - }, - "lease": { - "mongo": { - "type": "database", - "roleName": "genocs.template-service", - "enabled": true, - "autoRenewal": true, - "templates": { - "connectionString": "mongodb://{{username}}:{{password}}@localhost:27017" - } - } - } - } -} \ No newline at end of file diff --git a/templates/template/src/Genocs.Template.WebApi/certs/localhost.cer b/templates/template/src/Genocs.Template.WebApi/certs/localhost.cer deleted file mode 100644 index 31fc2df7..00000000 --- a/templates/template/src/Genocs.Template.WebApi/certs/localhost.cer +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDCTCCAfGgAwIBAgIUFLglzcz4ySPop6UDOY4VfocxoSowDQYJKoZIhvcNAQEL -BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIwMDMwMTExMjU1MloXDTIxMDMw -MTExMjU1MlowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEAs7oyRinTJp0bER/BMpO/zNiNlW/KXQsP+RKxHCTd4Sjq -X9bdp1IkqMQtDJnxCMHjfmKkPEXLa7BlrmkMXZ3r54ociq0tbUa/S7S6s3oN17Ov -IwM3ohT+eXccTPx1UbrX8hrFfKabIp1e8ARJNsr+UJ1TeQc4h8rZLoCa0JbMXMyQ -bqZ1X8C6hOpKzgB+SwmsIm2w5eRqnbvxwXNS5hyxfL8r6XRObbJNb36xGE0WBjy9 -DIIiChlq8VOldqNBqGjRpN7bIxnVpSLeKq4Y2+jMPeoC9gr2Ncsjbrddac6ePUXj -UebC2Yb/5rL2PyebEWBmVa+OMN1QopQYhyI5hE+oswIDAQABo1MwUTAdBgNVHQ4E -FgQUpKJM7NhUzpmzGare25X3rNoml40wHwYDVR0jBBgwFoAUpKJM7NhUzpmzGare -25X3rNoml40wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAfAFt -SUYlg1zSFmpt5338EJRaTCu0wmXeg+Hn73Bdi3Pa6qh3v+sgaRGzdJXdBLHZhhWB -h+gMRrJQ+ALNzh2vcPH6gS/du4tfFHfRufI2GalN+o95UUiiDmen9yGkyYsuEQp/ -mKYpLHvzsj6Qsx6++N7eI5nQ2d88OE753YPxZbWTUOr1voGodKCgJB36e5Rh2Lvs -Uqn8ePtKPewqjfcKE3wCcN5HNpiC+GyK9SkzPGDuh4VGzZCMA83vuWrYNOE5rMOs -G8NGFdqZ4Pk8wT4RQhJkPEAT0yravTaFE0rOc0pxF5l4PA1FCVmtjOyYZgj7JUeA -zPKUtgIPz28SUzsFdw== ------END CERTIFICATE----- diff --git a/templates/template/src/Genocs.Template.WebApi/certs/localhost.key b/templates/template/src/Genocs.Template.WebApi/certs/localhost.key deleted file mode 100644 index 270c4a91..00000000 --- a/templates/template/src/Genocs.Template.WebApi/certs/localhost.key +++ /dev/null @@ -1,30 +0,0 @@ ------BEGIN ENCRYPTED PRIVATE KEY----- -MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIm5Ia64KZKmwCAggA -MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECO2HnxA2BhMEBIIEyE7V/k2Dt0oB -h4b3WlT6VR2sXWn8n83oWS8GP+XcwrFIS0LWGPtUImh2rxdytXC9pPIoDvZbcKyx -t+UQaEYPZWIn2HM83V5yaNc2WtBXzTpfTH6sMEDkWFtqTuOSrjLaI63kldjLJ0dv -aEa8zHmPO7KEtMawKY1vucJFL5aU1IX5b7YXfgeI9iV7V0qktjQtOlQxJARwh1JV -Vo6VVzArNIiC+Ul39xgtMYe9UcSMJsSFOCQYPsiGX0c2XSz26i+G3W19+QXCZUtm -0eqRxF/BRf95siUu4Yd1Rv4+oUCF4Lio16NHEZ/yGIzfya4QeW0YBtE8KHfM6g9e -HcmaJBhF5NLukfCoGbrZpBg//7ZrMypo8D6U9p5pV7oXyHpm0imCjs/liFDHve6v -szTXFlMgqpMyKliBL/EalDhDXBftvOnKItpO8o42Y/XvmDQ2E/B6tRg04hEWoHH5 -XSE5240BgZCmF277/ulY7d7UCXjV//NiVjSwEurMx5km91M1qXgxhiPOeACg4cho -SaVwG+Oii0M8qBvHIk8F3MHMt3fJJ7RGkI0GZzUrAO2NZgMyChuhq3xj9nn/PdNl -Qv3xTwowciiH50nw6L3pXGAqG/4oqei47M6GoUNf5AH8oy0C4p5i1mWkt36T90GO -b7+sTn2CV1mgzdwO8FwdAhZhkANOlB54ocx23At7FHQOMRQ4yQ9ULv4P9zQeolvN -BWjZtD3qhOL8UeSc8syzBfmcOAURpA0WAw44xC5d7sUJuNQ/anRyukoX3Gl1Z2On -GA7q7qzjs2UPFV82dYyoSTcLHTvdRPFtNkfmFPtN/LH6Q30IOZNB85cyDH5d9X+G -x+6rxD2PCKx/8+5XvHZ7d8I4+0+3Jk/TvOsudKHZsXt2mlvIa65jat11OY+uu+59 -sNiT/gc5EhhErwmjj8swKCV0IdhMHzv4PySbsYzky+kv4sQBlRAUhOcHBQ/53BHm -5iee27rMy+SyzGC2sEgQguGKrdvFSe590wE6aoMFXmf/3QwiiVbzRGh19RJQZElL -hzGLdjEdw1if7o4jvSVuxbtpSPycxphmwDSBh/4HszRr2CAD29xGtI/PnATGlZaP -W8BjC3dAMfN1/YHgD5Y8N1VbFCcxNMRvwzoVLHA1ZtxKOoAmlv3FNIb7o5O6DV82 -QMoWXrlApaBHeHlQCo5ieW/3Pua6Xw3gZConlONqFY1snhfPS3x8Qjh1ETqHokTx -WFCJ/JeE6ZOJVPzjEthnDWuUZ6r8iclLmWN/LV5UfROMUErJSwIPTqORLA6gbLUa -nf0kXTPlTONRsyaqubc8Jbdzf/vwlbT5TG6Sbbul7WmF4Y8mT+hfWMfpuwzXepyR -AMbe+E06zMEtV++hEoArhvB9p02e+Ejb9lRGKiAd0GAdk2xEP5cdd4GUIuE1HFp5 -tQrxriM6lF1tdS6eosb7q870zBpyq22gFKi/JWVrWuArwhBFiNOppc4psm0t9mQC -EQnY2t2lvq34bEMVnbT4MPeiynvacHxJAqcfIVZaHsylLWRpPZFLN8zUBB5FqO0t -y0qda8AUqVKoUYRyaqjwg2gmoELRjqvf8caaQi6cmX7nJRoCM0yTV7KzFJ01J0e/ -eNf8bCYpVnYCrPjwexMCwA== ------END ENCRYPTED PRIVATE KEY----- diff --git a/templates/template/src/Genocs.Template.WebApi/certs/localhost.pem b/templates/template/src/Genocs.Template.WebApi/certs/localhost.pem deleted file mode 100644 index 00d8bbb6..00000000 --- a/templates/template/src/Genocs.Template.WebApi/certs/localhost.pem +++ /dev/null @@ -1,56 +0,0 @@ -Bag Attributes - localKeyID: 7A 10 75 44 7E 71 45 11 36 D7 89 60 43 3D BA 90 F8 61 62 27 -subject=CN = localhost - -issuer=CN = localhost - ------BEGIN CERTIFICATE----- -MIIDCTCCAfGgAwIBAgIUFLglzcz4ySPop6UDOY4VfocxoSowDQYJKoZIhvcNAQEL -BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIwMDMwMTExMjU1MloXDTIxMDMw -MTExMjU1MlowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEAs7oyRinTJp0bER/BMpO/zNiNlW/KXQsP+RKxHCTd4Sjq -X9bdp1IkqMQtDJnxCMHjfmKkPEXLa7BlrmkMXZ3r54ociq0tbUa/S7S6s3oN17Ov -IwM3ohT+eXccTPx1UbrX8hrFfKabIp1e8ARJNsr+UJ1TeQc4h8rZLoCa0JbMXMyQ -bqZ1X8C6hOpKzgB+SwmsIm2w5eRqnbvxwXNS5hyxfL8r6XRObbJNb36xGE0WBjy9 -DIIiChlq8VOldqNBqGjRpN7bIxnVpSLeKq4Y2+jMPeoC9gr2Ncsjbrddac6ePUXj -UebC2Yb/5rL2PyebEWBmVa+OMN1QopQYhyI5hE+oswIDAQABo1MwUTAdBgNVHQ4E -FgQUpKJM7NhUzpmzGare25X3rNoml40wHwYDVR0jBBgwFoAUpKJM7NhUzpmzGare -25X3rNoml40wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAfAFt -SUYlg1zSFmpt5338EJRaTCu0wmXeg+Hn73Bdi3Pa6qh3v+sgaRGzdJXdBLHZhhWB -h+gMRrJQ+ALNzh2vcPH6gS/du4tfFHfRufI2GalN+o95UUiiDmen9yGkyYsuEQp/ -mKYpLHvzsj6Qsx6++N7eI5nQ2d88OE753YPxZbWTUOr1voGodKCgJB36e5Rh2Lvs -Uqn8ePtKPewqjfcKE3wCcN5HNpiC+GyK9SkzPGDuh4VGzZCMA83vuWrYNOE5rMOs -G8NGFdqZ4Pk8wT4RQhJkPEAT0yravTaFE0rOc0pxF5l4PA1FCVmtjOyYZgj7JUeA -zPKUtgIPz28SUzsFdw== ------END CERTIFICATE----- -Bag Attributes - localKeyID: 7A 10 75 44 7E 71 45 11 36 D7 89 60 43 3D BA 90 F8 61 62 27 -Key Attributes: ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCzujJGKdMmnRsR -H8Eyk7/M2I2Vb8pdCw/5ErEcJN3hKOpf1t2nUiSoxC0MmfEIweN+YqQ8RctrsGWu -aQxdnevnihyKrS1tRr9LtLqzeg3Xs68jAzeiFP55dxxM/HVRutfyGsV8ppsinV7w -BEk2yv5QnVN5BziHytkugJrQlsxczJBupnVfwLqE6krOAH5LCawibbDl5Gqdu/HB -c1LmHLF8vyvpdE5tsk1vfrEYTRYGPL0MgiIKGWrxU6V2o0GoaNGk3tsjGdWlIt4q -rhjb6Mw96gL2CvY1yyNut11pzp49ReNR5sLZhv/msvY/J5sRYGZVr44w3VCilBiH -IjmET6izAgMBAAECggEAFSTMkVCOVLaa3pLbybV0FQdM8M2sAvIwON8oEuWLbNfB -avD+NpcFVQ1vN1JM9vjIaK/2Hlkfq1WfQLt/BuxzOlQXM4FYHMj77E4gwMiVJFEx -Q283dkv9/BA1NiFlM+5FRWg834NCMPpOlvIKIvYSoS1BCrX8wozsq/nZPLzg4fHE -U+vYXYtydYqZ4iurlQ0Bmpwi7qJgDYHNOxQ64g2u1Memy48vOb29vP00dNeCceaL -blzR/x8hd0VvkvzsDgxFaPKDRb62Pu/P3YY8uKzK50WZ1EqW/SRUG5vzYDu14vLZ -Vu/Wlqs4CJ1MxwweKOQoQFeGghzr3+HFvFOYjbWw8QKBgQDoCqK4L0hDfOn6q3wl -G8SpiE6maSnBpUhYqxK9PE3dOYRQfx4of1RfSGVcSHROvrCGCwtQzoWcJeJE0zcS -m4ykddUTomSZvsPi2LcdNtjr3yi4irmVU5UoqhxYx1eghF0sIzxKNsUe6i2fB7sE -2r70ZRwrJvlUWo2dTa5IMr5mTwKBgQDGSMgphRRjm4Xgi4WNSlgdHHzBvuTOS5ko -9nQLD9tqyuJIrniWZPAtJiNPkG3R7Jpyvfxqs6ux5OnP7EFABK5fnODdeZpW33Z2 -mBZirbYKml88rS4S3S/DHurfksGx/wK81YIcgxZkWHNdUH8TBoqQI3N7sA97jrHC -JOOLRhIiXQKBgCD6Mfn/LkP5Ir+vU2tqsN5vMs5Pr9GHjeqcFYOzaFWCcR2Hk1+q -CPGptk4RNgZi7299blRDpZV1hNzHL9KDLM/Kt0edErMO+4PnzM5oFxgelQj1Hj7s -rAwlcPXlDr0PUraNI2pgsk8cdPNfA+NCu1S8ce5oEZM9c1bhzV0RaSHdAoGBAJIf -xuraOPli/plJGOXT+PWTLdl2PZZOHhQS89fHGtMrqvEXKqTixCDxryoObPA8ZmHS -4vcba5eQ9EIXgMqV6I9rxEdTO0t0KRAi6rRUAgj0VOUZVw/t0yn/WeXjeD0dij3x -No6eiFzw1ivZi5xQwMKSbaOkTvSmZxeFKe9Udz69AoGBAM6T1tO0oh7LoqNuXNZb -fA3BiY9Rosr4glEXpJ16LdYXoQOX6pRgk7/itqr9hrLmJxNgFtoXFiKVLRQsxaYr -ffCaiy89SzO/6tSxMRNM8cdMQ2TF7U5O5JR/MICbAaS6/Gw7n5kx90DR3/tZWTeD -AY3jOFQRNNbeE+hTZB1kL61i ------END PRIVATE KEY----- diff --git a/templates/template/src/Genocs.Template.WebApi/certs/localhost.pfx b/templates/template/src/Genocs.Template.WebApi/certs/localhost.pfx deleted file mode 100644 index 8e1885b6..00000000 Binary files a/templates/template/src/Genocs.Template.WebApi/certs/localhost.pfx and /dev/null differ diff --git a/templates/template/webapi.dockerfile b/templates/template/webapi.dockerfile deleted file mode 100644 index 3dadeb2e..00000000 --- a/templates/template/webapi.dockerfile +++ /dev/null @@ -1,25 +0,0 @@ -#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. - -FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base -WORKDIR /app -EXPOSE 80 -EXPOSE 443 - -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build -WORKDIR /src -COPY ["NuGet.config", "."] -COPY ["src/Genocs.Template.WebApi/Genocs.Template.WebApi.csproj", "Genocs.Template.WebApi/"] -COPY ["src/Genocs.Template.Application/Genocs.Template.Application.csproj", "Genocs.Template.Application/"] -RUN dotnet restore "src/Genocs.Template.WebApi/Genocs.Template.WebApi.csproj" - -COPY . . -WORKDIR "/src/Genocs.Template.WebApi" -RUN dotnet build "Genocs.Template.WebApi.csproj" -c Release -o /app/build - -FROM build AS publish -RUN dotnet publish "Genocs.Template.WebApi.csproj" -c Release -o /app/publish - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "Genocs.Template.WebApi.dll"] \ No newline at end of file diff --git a/testEnvironments.json b/testEnvironments.json new file mode 100644 index 00000000..a110b57d --- /dev/null +++ b/testEnvironments.json @@ -0,0 +1,17 @@ +{ + "version": "1", + "environments": [ + // See https://aka.ms/remotetesting for more details + // about how to configure remote environments. + //{ + // "name": "WSL Ubuntu", + // "type": "wsl", + // "wslDistribution": "Ubuntu" + //}, + //{ + // "name": "Docker dotnet/sdk", + // "type": "docker", + // "dockerImage": "mcr.microsoft.com/dotnet/sdk" + //} + ] +} \ No newline at end of file