├── .gitattributes ├── .github └── workflows │ └── cd.yaml ├── .gitignore ├── Enbiso.NLib.Cqrs.Idempotent ├── Enbiso.NLib.Cqrs.Idempotent.csproj ├── IdempotentCommandBus.cs └── ServiceExtensions.cs ├── Enbiso.NLib.Cqrs ├── Command.cs ├── CommandBus.cs ├── CommandLogger.cs ├── CommandProcessor.cs ├── CommandValidator.cs ├── Enbiso.NLib.Cqrs.csproj ├── IQuery.cs └── ServiceExtensions.cs ├── Enbiso.NLib.DependencyInjection ├── Enbiso.NLib.DependencyInjection.csproj ├── ServiceAttributes.cs └── ServiceExtensions.cs ├── Enbiso.NLib.Domain.Events ├── DomainEvent.cs ├── DomainEventBus.cs ├── Enbiso.NLib.Domain.Events.csproj └── ServiceExtensions.cs ├── Enbiso.NLib.Domain ├── Enbiso.NLib.Domain.csproj ├── Entity.cs ├── Exception.cs └── Repository.cs ├── Enbiso.NLib.EventBus.Abstractions ├── Enbiso.NLib.EventBus.Abstractions.csproj ├── IEvent.cs ├── IEventHandler.cs ├── IEventPublisher.cs ├── IEventService.cs ├── IEventSubscriber.cs └── IEventSubscriptionService.cs ├── Enbiso.NLib.EventBus.AwsSns ├── AwsSnsConnection.cs ├── AwsSnsEventPublisher.cs ├── AwsSnsEventSubscriber.cs ├── AwsSnsOptions.cs ├── Enbiso.NLib.EventBus.AwsSns.csproj └── ServiceExtensions.cs ├── Enbiso.NLib.EventBus.Nats ├── Enbiso.NLib.EventBus.Nats.csproj ├── JetStream │ ├── JetStreamConnection.cs │ ├── JetStreamEventPublisher.cs │ └── JetStreamEventSubscriber.cs ├── NatsConnection.cs ├── NatsEventPublisher.cs ├── NatsEventSubscriber.cs ├── NatsOptions.cs ├── NatsPolicyBuilder.cs └── ServiceExtensions.cs ├── Enbiso.NLib.EventBus.RabbitMq ├── Enbiso.NLib.EventBus.RabbitMq.csproj ├── RabbitMqBusPublisher.cs ├── RabbitMqBusSubscriber.cs ├── RabbitMqConnection.cs ├── RabbitMqOption.cs └── ServiceExtensions.cs ├── Enbiso.NLib.EventBus ├── Enbiso.NLib.EventBus.csproj ├── Event.cs ├── EventProcessor.cs ├── EventService.cs ├── EventSubscriptionService.cs ├── EventTypeManager.cs └── ServiceExtensions.cs ├── Enbiso.NLib.EventInfo ├── Enbiso.NLib.EventInfo.csproj ├── EventInfoController.cs ├── EventInfoService.cs ├── Models │ ├── EventInfoListResponse.cs │ ├── EventRecord.cs │ ├── EventRecordMapper.cs │ └── EventRecordProp.cs └── ServiceExtensions.cs ├── Enbiso.NLib.EventLogger.EntityFramework ├── Enbiso.NLib.EventLogger.EntityFramework.csproj ├── EntityEventLogRepo.cs ├── EventLogEntityConfig.cs └── ServiceExtensions.cs ├── Enbiso.NLib.EventLogger.Mongo ├── Enbiso.NLib.EventLogger.Mongo.csproj ├── MongoEventLogRepo.cs └── ServiceExtensions.cs ├── Enbiso.NLib.EventLogger ├── Enbiso.NLib.EventLogger.csproj ├── EventLog.cs ├── EventLoggerEventService.cs ├── EventLoggerService.cs ├── IEventLogRepo.cs └── ServiceExtensions.cs ├── Enbiso.NLib.GlobalExceptions ├── Enbiso.NLib.GlobalExceptions.csproj ├── GlobalExceptionFilter.cs ├── GlobalExceptionHandler.cs ├── GlobalExceptionHandlerExtensions.cs └── GlobalExceptionResponse.cs ├── Enbiso.NLib.HttpContextService ├── Enbiso.NLib.HttpContextService.csproj ├── HttpContextFetcher.cs ├── HttpContextService.cs └── ServiceExtensions.cs ├── Enbiso.NLib.Idempotency.EntityFramework ├── Enbiso.NLib.Idempotency.EntityFramework.csproj ├── RequestLogEntityConfig.cs ├── RequestLogRepo.cs └── ServiceExtensions.cs ├── Enbiso.NLib.Idempotency ├── Enbiso.NLib.Idempotency.csproj ├── IRequestLogRepo.cs ├── IRequestManager.cs ├── RequestLog.cs ├── RequestManager.cs └── ServiceExtensions.cs ├── Enbiso.NLib.IdentityServer.Mongo ├── Enbiso.NLib.IdentityServer.Mongo.csproj ├── Events │ ├── IdentityServerMongoEvents.cs │ └── IdentityServerMongoUserEvents.cs ├── Models │ ├── ApiResourceData.cs │ ├── ClientData.cs │ ├── IMongoIdentityRole.cs │ ├── IMongoIdentityUser.cs │ ├── IdentityResourceData.cs │ ├── MongoClaim.cs │ ├── MongoUserLoginInfo.cs │ ├── PersistedGrantData.cs │ └── RecoveryCode.cs ├── ServiceExtensions.cs └── Stores │ ├── MongoClientStore.cs │ ├── MongoPersistedGrantStore.cs │ ├── MongoResourceStore.cs │ ├── MongoRoleStore.cs │ └── MongoUserStore.cs ├── Enbiso.NLib.OpenApi ├── Attributes │ ├── Actions │ │ ├── ExtActionAttribute.cs │ │ ├── ExtActionCreateAttribute.cs │ │ ├── ExtActionDeleteAttribute.cs │ │ ├── ExtActionDetailsAttribute.cs │ │ └── ExtActionUpdateAttribute.cs │ ├── ExtAttribute.cs │ ├── ExtHiddenAttribute.cs │ ├── ExtKeyAttribute.cs │ └── Inputs │ │ ├── ExtInputAttribute.cs │ │ ├── ExtInputColorAttribute.cs │ │ ├── ExtInputDateAttribute.cs │ │ ├── ExtInputDateTimeLocalAttribute.cs │ │ ├── ExtInputEmailAttribute.cs │ │ ├── ExtInputMonthAttribute.cs │ │ ├── ExtInputNumberAttribute.cs │ │ ├── ExtInputPasswordAttribute.cs │ │ ├── ExtInputSearchAttribute.cs │ │ ├── ExtInputTelAttribute.cs │ │ ├── ExtInputTextAttribute.cs │ │ ├── ExtInputTimeAttribute.cs │ │ ├── ExtInputUrlAttribute.cs │ │ └── ExtInputWeekAttribute.cs ├── Enbiso.NLib.OpenApi.csproj ├── SchemaExtensionFilter.cs └── ServiceExtensions.cs ├── Enbiso.NLib.RestClient ├── Enbiso.NLib.RestClient.csproj ├── Exceptions.cs ├── RestClient.cs └── ServiceExtensions.cs ├── Enbiso.NLib.Tests ├── Enbiso.NLib.Domain.Tests │ ├── Enbiso.NLib.Domain.Tests.csproj │ └── EntityTests.cs └── Enbiso.NLib.OpenApi.Tests │ ├── Enbiso.NLib.OpenApi.Tests.csproj │ └── ServiceExtensionsTests.cs ├── Enbiso.NLib.sln ├── LICENSE ├── README.md └── logo.png /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/cd.yaml: -------------------------------------------------------------------------------- 1 | name: CD - Nuget 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | env: 9 | PACKAGE_VERSION: 7.0.5 10 | PACKAGE_PROJECT: https://nlib.enbiso.com 11 | PACKAGE_REPO: https://github.com/enbiso/Enbiso.NLib 12 | PACKAGE_COPYRIGHT: Copyright 2021 (c) enbiso. All rights reserved. 13 | 14 | jobs: 15 | test: 16 | name: Unit Tests 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v2 21 | 22 | - name: Setup .NET 23 | uses: actions/setup-dotnet@v1 24 | with: 25 | dotnet-version: 7.0.x 26 | 27 | - name: Test 28 | run: dotnet test --verbosity normal 29 | 30 | build: 31 | name: Build & Publish 32 | needs: [test] 33 | runs-on: ubuntu-latest 34 | 35 | steps: 36 | - name: Checkout 37 | uses: actions/checkout@v2 38 | 39 | - name: Setup .NET 40 | uses: actions/setup-dotnet@v1 41 | with: 42 | dotnet-version: 7.0.x 43 | 44 | - name: Build Release 45 | run: dotnet build -c release -o ./bin 46 | 47 | - name: Publish 48 | run: for nupkg in ./bin/*.nupkg; do 49 | dotnet nuget push $nupkg -s $NUGET_REGISTRY -k $NUGET_API_KEY; 50 | done 51 | env: 52 | NUGET_REGISTRY: https://packages.nuget.org 53 | NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} 54 | 55 | release: 56 | name: Tags and Release 57 | needs: [build] 58 | runs-on: ubuntu-latest 59 | 60 | steps: 61 | - name: Create Release 62 | uses: actions/create-release@v1 63 | env: 64 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 65 | TAG_NAME: v${{ env.PACKAGE_VERSION }} 66 | with: 67 | tag_name: ${{ env.TAG_NAME }} 68 | release_name: Release ${{ env.TAG_NAME }} 69 | body: | 70 | Release ${{ env.TAG_NAME }} 71 | https://www.nuget.org/profiles/enbiso 72 | draft: false 73 | prerelease: false 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /Enbiso.NLib.Cqrs.Idempotent/Enbiso.NLib.Cqrs.Idempotent.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | Enbiso.NLib.Cqrs.Idempotent 6 | 7 | 0.0.1-local 8 | $(PACKAGE_VERSION) 9 | 10 | enbiso 11 | Faraj Farook 12 | Simple Idempotent CQRS 13 | $(PACKAGE_COPYRIGHT) 14 | Enbiso Enbiso.NLib CQRS Idempotent 15 | true 16 | en-AU 17 | enbiso 18 | Initial Release 19 | Enbiso - Simple Idempotent CQRS .NET Standard 20 | Simple Idempotent CQRS 21 | logo.png 22 | $(PACKAGE_PROJECT) 23 | $(PACKAGE_REPO) 24 | default 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Enbiso.NLib.Cqrs.Idempotent/IdempotentCommandBus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Enbiso.NLib.Idempotency; 6 | 7 | namespace Enbiso.NLib.Cqrs.Idempotent 8 | { 9 | /// 10 | /// Idempotent Command bus 11 | /// 12 | public interface IIdempotentCommandBus 13 | { 14 | Task Send(ICommand command, string requestId, 15 | CancellationToken cancellationToken = default(CancellationToken)) 16 | where TResponse : ICommandResponse; 17 | } 18 | 19 | /// 20 | /// 21 | /// Idempotent Command bus implementation 22 | /// 23 | public class IdempotentCommandBus: IIdempotentCommandBus 24 | { 25 | private readonly ICommandBus _bus; 26 | private readonly IRequestManager _requestManager; 27 | 28 | public IdempotentCommandBus(ICommandBus bus, IRequestManager requestManager) 29 | { 30 | _bus = bus; 31 | _requestManager = requestManager; 32 | } 33 | 34 | public async Task Send(ICommand command, string requestId, CancellationToken cancellationToken = default(CancellationToken)) 35 | where TResponse : ICommandResponse 36 | { 37 | if (!Guid.TryParse(requestId, out var reqGuid)) 38 | throw new InvalidRequestIdException(requestId); 39 | 40 | var reqLog = await _requestManager.FindAsync(reqGuid); 41 | if (reqLog != null) 42 | { 43 | return JsonSerializer.Deserialize(reqLog.Response); 44 | } 45 | var response = await _bus.Send(command, cancellationToken); 46 | await _requestManager.CreateRequestAsync(reqGuid, command.GetType().Name, JsonSerializer.Serialize(response)); 47 | return response; 48 | } 49 | } 50 | 51 | /// 52 | /// Invalid request ID exception 53 | /// 54 | public class InvalidRequestIdException : Exception 55 | { 56 | public string RequestId { get; private set; } 57 | public InvalidRequestIdException(string requestId) : base($"Error parsing request ID") 58 | { 59 | RequestId = requestId; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Enbiso.NLib.Cqrs.Idempotent/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Enbiso.NLib.Idempotency; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace Enbiso.NLib.Cqrs.Idempotent 6 | { 7 | public static class ServiceExtensions 8 | { 9 | public static IServiceCollection AddCqrsIdempotent(this IServiceCollection services, bool autoLoadHandlers = true) 10 | { 11 | if (autoLoadHandlers) return services.AddCqrsIdempotent(Assembly.GetCallingAssembly()); 12 | return services.AddCqrsIdempotent(new Assembly[0]); 13 | } 14 | 15 | public static IServiceCollection AddCqrsIdempotent(this IServiceCollection services, 16 | params Assembly[] assemblies) 17 | { 18 | services.AddIdempotency(); 19 | services.AddCqrs(assemblies); 20 | services.AddScoped(); 21 | return services; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Enbiso.NLib.Cqrs/Command.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using MediatR; 4 | using MediatR.Pipeline; 5 | 6 | namespace Enbiso.NLib.Cqrs 7 | { 8 | public interface IBaseCommand 9 | { 10 | } 11 | 12 | public interface ICommand: IRequest, IBaseCommand where TResponse: ICommandResponse 13 | { 14 | } 15 | 16 | public interface ICommandResponse 17 | { 18 | 19 | } 20 | 21 | public interface ICommandHandler : IRequestHandler 22 | where TCommand : ICommand 23 | where TResponse: ICommandResponse 24 | { 25 | 26 | } 27 | 28 | public interface ICommandPreProcessor 29 | where TCommand : ICommand 30 | where TResponse: ICommandResponse 31 | { 32 | Task Process(TCommand request, CancellationToken cancellationToken); 33 | } 34 | 35 | public interface ICommandPostProcessor 36 | where TCommand : ICommand 37 | where TResponse : ICommandResponse 38 | { 39 | Task Process(TCommand request, TResponse response, CancellationToken cancellationToken); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Enbiso.NLib.Cqrs/CommandBus.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using MediatR; 4 | 5 | namespace Enbiso.NLib.Cqrs 6 | { 7 | public interface ICommandBus 8 | { 9 | Task Send(ICommand command, CancellationToken cancellationToken = default) 10 | where TResponse : ICommandResponse; 11 | } 12 | 13 | public class CommandBus : ICommandBus 14 | { 15 | private readonly IMediator _mediator; 16 | 17 | public CommandBus(IMediator mediator) 18 | { 19 | _mediator = mediator; 20 | } 21 | 22 | public Task Send(ICommand command, CancellationToken cancellationToken = default) 23 | where TResponse : ICommandResponse 24 | { 25 | command = command ?? throw new CommandValidationException("Request command is empty"); 26 | return _mediator.Send(command, cancellationToken); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Enbiso.NLib.Cqrs/CommandLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using MediatR; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace Enbiso.NLib.Cqrs 8 | { 9 | public class LoggingBehavior : IPipelineBehavior 10 | where TCommand : ICommand 11 | where TResponse : ICommandResponse 12 | { 13 | private readonly ILogger> _logger; 14 | 15 | public LoggingBehavior(ILogger> logger) 16 | { 17 | _logger = logger; 18 | } 19 | 20 | public async Task Handle(TCommand request, RequestHandlerDelegate next, 21 | CancellationToken cancellationToken) 22 | { 23 | try 24 | { 25 | _logger.LogTrace("Handling {Name}", typeof(TCommand).Name); 26 | var response = await next(); 27 | _logger.LogTrace("Handled {Name}", typeof(TCommand).Name); 28 | return response; 29 | } 30 | catch (Exception e) 31 | { 32 | _logger.LogError(e, "Failed {Name}", typeof(TResponse).Name); 33 | throw; 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Enbiso.NLib.Cqrs/CommandProcessor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using MediatR; 6 | 7 | namespace Enbiso.NLib.Cqrs 8 | { 9 | public class ProcessorBehaviour: IPipelineBehavior 10 | where TCommand : ICommand 11 | where TResponse : ICommandResponse 12 | { 13 | private readonly IEnumerable> _preProcessors; 14 | private readonly IEnumerable> _postProcessors; 15 | 16 | public ProcessorBehaviour(IEnumerable> preProcessors, 17 | IEnumerable> postProcessors) 18 | { 19 | _preProcessors = preProcessors; 20 | _postProcessors = postProcessors; 21 | } 22 | 23 | public async Task Handle(TCommand request, RequestHandlerDelegate next, CancellationToken cancellationToken) 24 | { 25 | await Task.WhenAll( 26 | _preProcessors.Select(p => p.Process(request, cancellationToken))); 27 | 28 | var response = await next(); 29 | 30 | await Task.WhenAll( 31 | _postProcessors.Select(p => p.Process(request, response, cancellationToken))); 32 | 33 | return response; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Enbiso.NLib.Cqrs/CommandValidator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using MediatR; 7 | 8 | namespace Enbiso.NLib.Cqrs 9 | { 10 | public class ValidatorBehavior : IPipelineBehavior 11 | where TCommand : ICommand 12 | where TResponse : ICommandResponse 13 | { 14 | private readonly IEnumerable> _validators; 15 | 16 | public ValidatorBehavior(IEnumerable> validators) 17 | { 18 | _validators = validators; 19 | } 20 | 21 | public async Task Handle(TCommand request, 22 | RequestHandlerDelegate next, CancellationToken cancellationToken) 23 | { 24 | var validatorTasks = _validators.Select(v => v.Validate(request)).ToArray(); 25 | await Task.WhenAll(validatorTasks); 26 | var failures = validatorTasks.SelectMany(t => t.Result).Where(error => error != null).ToArray(); 27 | 28 | if (failures.Any()) 29 | throw new CommandValidationException(typeof(TCommand), failures); 30 | 31 | var response = await next(); 32 | return response; 33 | } 34 | } 35 | 36 | /// 37 | /// Command validator 38 | /// 39 | /// 40 | public interface ICommandValidator 41 | where TCommand: IBaseCommand 42 | { 43 | Task> Validate(TCommand command); 44 | } 45 | 46 | /// 47 | /// Validation ValidationError 48 | /// 49 | public class ValidationError 50 | { 51 | public string Message { get; } 52 | public string Property { get; } 53 | 54 | public ValidationError(string message, string property) 55 | { 56 | Message = message; 57 | Property = property; 58 | } 59 | } 60 | 61 | /// 62 | /// Command Validation exception 63 | /// 64 | public class CommandValidationException : Exception 65 | { 66 | public IEnumerable Errors { get; } 67 | public CommandValidationException(Type command, IEnumerable failures = null) 68 | : this($"{command.Name} validation failed") 69 | { 70 | Errors = failures; 71 | } 72 | public CommandValidationException(string message): base(message) { 73 | 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /Enbiso.NLib.Cqrs/Enbiso.NLib.Cqrs.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | Enbiso.NLib.Cqrs 6 | 7 | 0.0.1-local 8 | $(PACKAGE_VERSION) 9 | 10 | enbiso 11 | Faraj Farook 12 | Simple CQRS 13 | $(PACKAGE_COPYRIGHT) 14 | Enbiso Enbiso.NLib CQRS 15 | true 16 | en-AU 17 | enbiso 18 | Initial Release 19 | Enbiso - Simple CQRS .NET Standard 20 | Simple CQRS 21 | logo.png 22 | $(PACKAGE_PROJECT) 23 | $(PACKAGE_REPO) 24 | default 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Enbiso.NLib.Cqrs/IQuery.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace Enbiso.NLib.Cqrs 5 | { 6 | public interface IQuery 7 | where TQueryModel : IQueryModel 8 | where TSummeryQueryModel : ISummaryQueryModel 9 | { 10 | Task GetByIdAsync(TKey key); 11 | Task> ListAsync(TSearch search); 12 | } 13 | 14 | public interface IQuery : IQuery 15 | { 16 | } 17 | 18 | public interface IQueryModel 19 | { 20 | } 21 | 22 | public interface ISummaryQueryModel 23 | { 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Enbiso.NLib.Cqrs/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using MediatR; 5 | using MediatR.Pipeline; 6 | using Microsoft.Extensions.DependencyInjection; 7 | 8 | namespace Enbiso.NLib.Cqrs 9 | { 10 | public static class ServiceExtensions 11 | { 12 | public static IServiceCollection AddCqrs(this IServiceCollection services, bool autoLoadHandlers = true) 13 | { 14 | if (!autoLoadHandlers) return services.AddCqrs(new Assembly[0]); 15 | return services.AddCqrs(Assembly.GetCallingAssembly()); 16 | } 17 | 18 | public static IServiceCollection AddCqrs(this IServiceCollection services, params Assembly[] assemblies) 19 | { 20 | if (services.All(s => s.ServiceType != typeof(IMediator))) 21 | services.AddMediatR(assemblies); 22 | 23 | services.AddScoped(); 24 | services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>)); 25 | services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidatorBehavior<,>)); 26 | services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ProcessorBehaviour<,>)); 27 | return services; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Enbiso.NLib.DependencyInjection/Enbiso.NLib.DependencyInjection.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | Enbiso.NLib.DependencyInjection 6 | 7 | 0.0.1-local 8 | $(PACKAGE_VERSION) 9 | 10 | enbiso 11 | Faraj Farook 12 | Simple Annotated Dependency Injection library 13 | $(PACKAGE_COPYRIGHT) 14 | Enbiso Enbiso.NLib Annotated Dependency Injection 15 | true 16 | en-AU 17 | enbiso 18 | Initial Release 19 | Enbiso - Simple Annotated Dependency Injection in .NET Standard 20 | Annotated Dependency Injection 21 | 22 | logo.png 23 | $(PACKAGE_PROJECT) 24 | $(PACKAGE_REPO) 25 | default 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Enbiso.NLib.DependencyInjection/ServiceAttributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Enbiso.NLib.DependencyInjection 5 | { 6 | public abstract class ServiceAttribute : Attribute 7 | { 8 | public ServiceLifetime Lifetime { get; } 9 | public Type[] ServiceTypes { get; } 10 | 11 | protected ServiceAttribute(ServiceLifetime lifetime, params Type[] serviceTypes) 12 | { 13 | Lifetime = lifetime; 14 | ServiceTypes = serviceTypes; 15 | } 16 | } 17 | 18 | /// 19 | /// Add as Transient ServiceTypes 20 | /// 21 | [AttributeUsage(AttributeTargets.Class)] 22 | public class TransientServiceAttribute : ServiceAttribute 23 | { 24 | public TransientServiceAttribute(params Type[] serviceTypes) : base(ServiceLifetime.Transient, serviceTypes) 25 | { 26 | } 27 | } 28 | 29 | /// 30 | /// Add as Scoped ServiceTypes 31 | /// 32 | 33 | [AttributeUsage(AttributeTargets.Class)] 34 | public class ScopedServiceAttribute : ServiceAttribute 35 | { 36 | public ScopedServiceAttribute(params Type[] serviceTypes) : base(ServiceLifetime.Scoped, serviceTypes) 37 | { 38 | } 39 | } 40 | 41 | /// 42 | /// Add as Singleton ServiceTypes 43 | /// 44 | [AttributeUsage(AttributeTargets.Class)] 45 | public class SingletonServiceAttribute : ServiceAttribute 46 | { 47 | public SingletonServiceAttribute(params Type[] serviceTypes) : base(ServiceLifetime.Singleton, serviceTypes) 48 | { 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Enbiso.NLib.DependencyInjection/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Enbiso.NLib.DependencyInjection 8 | { 9 | public static class ServiceExtensions 10 | { 11 | public static IServiceCollection AddServices(this IServiceCollection services, params Type[] typeReferences) 12 | { 13 | return services.AddServices(typeReferences.Select(t => t.Assembly).ToArray()); 14 | } 15 | 16 | public static IServiceCollection AddServices(this IServiceCollection services, params Assembly[] assemblies) 17 | { 18 | assemblies.ToList().ForEach(services.AddServicesForAssembly); 19 | return services; 20 | } 21 | 22 | public static IServiceCollection AddServices(this IServiceCollection services) 23 | { 24 | var entry = Assembly.GetEntryAssembly(); 25 | var assemblies = entry.GetReferencedAssemblies().Select(Assembly.Load).ToList(); 26 | assemblies.Add(entry); 27 | return services.AddServices(assemblies.ToArray()); 28 | } 29 | 30 | private static void AddServicesForAssembly(this IServiceCollection services, Assembly assembly) 31 | { 32 | foreach (var type in assembly.GetTypes()) 33 | { 34 | var service = type.GetCustomAttribute(); 35 | if (service == null) continue; 36 | // Get defined interfaces 37 | var interfaces = service.ServiceTypes.ToList(); 38 | // If not specified add implemented interfaces 39 | if (!interfaces.Any()) interfaces.AddRange(type.GetInterfaces()); 40 | // If not then self bind 41 | if (!interfaces.Any()) interfaces.Add(type); 42 | 43 | switch (service.Lifetime) 44 | { 45 | case ServiceLifetime.Transient: 46 | interfaces.ForEach(@interface => services.AddTransient(@interface, type)); 47 | break; 48 | case ServiceLifetime.Singleton: 49 | interfaces.ForEach(@interface => services.AddSingleton(@interface, type)); 50 | break; 51 | case ServiceLifetime.Scoped: 52 | interfaces.ForEach(@interface => services.AddScoped(@interface, type)); 53 | break; 54 | default: 55 | throw new Exception("Unknown scope specified"); 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Enbiso.NLib.Domain.Events/DomainEvent.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace Enbiso.NLib.Domain.Events 4 | { 5 | /// 6 | /// Domain event 7 | /// 8 | public interface IDomainEvent: INotification, IEntityEvent 9 | { 10 | } 11 | 12 | /// 13 | /// Domain event handlers 14 | /// 15 | /// 16 | public interface IDomainEventHandler: INotificationHandler 17 | where TDomainEvent: IDomainEvent 18 | { 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Enbiso.NLib.Domain.Events/DomainEventBus.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using MediatR; 6 | 7 | namespace Enbiso.NLib.Domain.Events 8 | { 9 | /// 10 | /// Domain event bus 11 | /// 12 | public interface IDomainEventBus 13 | { 14 | Task Publish(TDomainEvent @event, CancellationToken cancellationToken = default(CancellationToken)) where TDomainEvent : IDomainEvent; 15 | Task[] PublishFromEntities(IEnumerable entities, CancellationToken cancellationToken = default(CancellationToken)); 16 | } 17 | 18 | /// 19 | /// Domain event bus implementation 20 | /// 21 | public class DomainEventBus: IDomainEventBus 22 | { 23 | private readonly IMediator _mediator; 24 | 25 | public DomainEventBus(IMediator mediator) 26 | { 27 | _mediator = mediator; 28 | } 29 | 30 | public Task Publish(TDomainEvent @event, CancellationToken cancellationToken = default(CancellationToken)) where TDomainEvent : IDomainEvent 31 | { 32 | return _mediator.Publish(@event, cancellationToken); 33 | } 34 | 35 | public Task[] PublishFromEntities(IEnumerable entities, 36 | CancellationToken cancellationToken = default(CancellationToken)) 37 | { 38 | return entities.GetEvents().Select(async e => await Publish(e, cancellationToken)).ToArray(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Enbiso.NLib.Domain.Events/Enbiso.NLib.Domain.Events.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | Enbiso.NLib.Domain.Events 6 | 7 | 0.0.1-local 8 | $(PACKAGE_VERSION) 9 | 10 | enbiso 11 | Faraj Farook 12 | Simple domain event library 13 | $(PACKAGE_COPYRIGHT) 14 | Enbiso Enbiso.NLib Domain Event 15 | true 16 | en-AU 17 | enbiso 18 | Initial Release 19 | Enbiso - Simple Domain event in .NET Standard 20 | Simple Domain Event 21 | logo.png 22 | $(PACKAGE_PROJECT) 23 | $(PACKAGE_REPO) 24 | default 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Enbiso.NLib.Domain.Events/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using MediatR; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Enbiso.NLib.Domain.Events 8 | { 9 | public static class ServiceExtensions 10 | { 11 | public static IServiceCollection AddDomainEvents(this IServiceCollection services) 12 | { 13 | var assembly = Assembly.GetCallingAssembly(); 14 | return services.AddDomainEvents(assembly); 15 | } 16 | 17 | public static IServiceCollection AddDomainEvents(this IServiceCollection services, params Assembly[] assemblies) 18 | { 19 | if (services.All(s => s.ServiceType != typeof(IMediator))) 20 | services.AddMediatR(assemblies); 21 | 22 | services.AddScoped(); 23 | return services; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Enbiso.NLib.Domain/Enbiso.NLib.Domain.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | Enbiso.NLib.Domain 6 | 7 | 0.0.1-local 8 | $(PACKAGE_VERSION) 9 | 10 | enbiso 11 | Faraj Farook 12 | Simple domain SeedWork library 13 | $(PACKAGE_COPYRIGHT) 14 | Enbiso Enbiso.NLib Domain SeedWork 15 | true 16 | en-AU 17 | enbiso 18 | Initial Release 19 | Enbiso - Simple Domain SeedWorks in .NET Standard 20 | Simple Domain Seedwork 21 | logo.png 22 | $(PACKAGE_PROJECT) 23 | $(PACKAGE_REPO) 24 | default 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Enbiso.NLib.Domain/Entity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Enbiso.NLib.Domain 6 | { 7 | /// 8 | /// Aggregated root entity 9 | /// 10 | public interface IRootEntity 11 | { 12 | } 13 | 14 | /// 15 | /// Base Event 16 | /// 17 | public interface IEntityEvent 18 | { 19 | } 20 | 21 | /// 22 | /// Domain Entity interface 23 | /// 24 | public interface IEntity 25 | { 26 | List GetEvents(); 27 | } 28 | 29 | public abstract class Entity: IEntity 30 | { 31 | private readonly List _events = new List(); 32 | 33 | public void AddEvent(IEntityEvent @event) 34 | { 35 | _events.Add(@event?? throw new ArgumentNullException()); 36 | } 37 | 38 | public void RemoveEvent(IEntityEvent @event) 39 | { 40 | _events.Remove(@event ?? throw new ArgumentNullException()); 41 | } 42 | 43 | public List GetEvents() => _events; 44 | } 45 | 46 | /// 47 | /// 48 | /// Domain Entity abstraction 49 | /// 50 | /// 51 | public abstract class Entity: Entity 52 | { 53 | private int? _requestedHashCode; 54 | public virtual TKey Id { get; set; } 55 | 56 | public bool IsTransient() 57 | { 58 | return default(TKey)?.Equals(Id) ?? true; 59 | } 60 | 61 | public override bool Equals(object obj) 62 | { 63 | if (!(obj is Entity)) 64 | return false; 65 | 66 | if (ReferenceEquals(this, obj)) 67 | return true; 68 | 69 | if (GetType() != obj.GetType()) 70 | return false; 71 | 72 | var item = (Entity)obj; 73 | 74 | if (item.IsTransient() || IsTransient()) 75 | return false; 76 | 77 | return item.Id?.Equals(Id) ?? false; 78 | } 79 | 80 | public override int GetHashCode() 81 | { 82 | if (IsTransient()) return base.GetHashCode(); 83 | 84 | if (!_requestedHashCode.HasValue) 85 | _requestedHashCode = Id.GetHashCode() ^ 31; 86 | 87 | return _requestedHashCode.Value; 88 | } 89 | 90 | public static bool operator ==(Entity left, Entity right) 91 | { 92 | return left?.Equals(right) ?? Equals(right, null); 93 | } 94 | 95 | public static bool operator !=(Entity left, Entity right) 96 | { 97 | return !(left == right); 98 | } 99 | } 100 | 101 | /// 102 | /// Entity helper extensions 103 | /// 104 | public static class EntityExtensions 105 | { 106 | public static void ClearEvents(this IEnumerable entities) 107 | { 108 | foreach (var entity in entities) 109 | { 110 | entity.GetEvents().Clear(); 111 | } 112 | } 113 | 114 | public static IEnumerable GetEvents(this IEnumerable entities) where TEvent: IEntityEvent 115 | { 116 | return entities.ToList().SelectMany(e => e.GetEvents()).OfType(); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Enbiso.NLib.Domain/Exception.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.Domain 4 | { 5 | /// 6 | /// 7 | /// Domain Exception 8 | /// 9 | public class DomainException: Exception 10 | { 11 | public DomainException() 12 | { 13 | } 14 | 15 | public DomainException(string message) : base(message) 16 | { 17 | } 18 | 19 | public DomainException(string message, Exception innerException) : base(message, innerException) 20 | { 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Enbiso.NLib.Domain/Repository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Enbiso.NLib.Domain 6 | { 7 | /// 8 | /// 9 | /// Repository 10 | /// 11 | /// 12 | public interface IRepository : IRepository where T : IRootEntity 13 | { 14 | } 15 | 16 | /// 17 | /// Repository 18 | /// 19 | public interface IRepository 20 | { 21 | IUnitOfWork UnitOfWork { get; } 22 | } 23 | 24 | /// 25 | /// 26 | /// Unit of work 27 | /// 28 | public interface IUnitOfWork : IDisposable 29 | { 30 | Task SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)); 31 | Task SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken)); 32 | } 33 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.Abstractions/Enbiso.NLib.EventBus.Abstractions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | Enbiso.NLib.EventBus.Abstractions 6 | Enbiso.NLib.EventBus 7 | 0.0.1-local 8 | $(PACKAGE_VERSION) 9 | Faraj Farook 10 | Simple Abstract event bus library 11 | false 12 | $(PACKAGE_COPYRIGHT) 13 | Enbiso Enbiso.NLib EventBus 14 | true 15 | en-AU 16 | enbiso 17 | Initial Release 18 | Enbiso - Simple Abstract EventBus Library in .NET Standard 19 | Simple Abstract EventBus 20 | logo.png 21 | $(PACKAGE_PROJECT) 22 | $(PACKAGE_REPO) 23 | default 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.Abstractions/IEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.EventBus 4 | { 5 | public interface IEvent 6 | { 7 | Guid EventId { get; } 8 | DateTime EventCreationDate { get; } 9 | } 10 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.Abstractions/IEventHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Enbiso.NLib.EventBus 5 | { 6 | /// 7 | /// Integration event handler 8 | /// 9 | public interface IEventHandler 10 | { 11 | bool IsValidFor(string eventName); 12 | Type EventType { get; } 13 | Task Handle(object @event); 14 | } 15 | 16 | /// 17 | /// Abstract event handler 18 | /// 19 | /// 20 | public abstract class EventHandler: IEventHandler where TEvent : class, IEvent 21 | { 22 | protected abstract Task Handle(TEvent @event); 23 | public Type EventType => typeof(TEvent); 24 | public Task Handle(object @event) => Handle(@event as TEvent); 25 | public bool IsValidFor(string eventName) => eventName == "*" || eventName == EventType.Name; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.Abstractions/IEventPublisher.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace Enbiso.NLib.EventBus 5 | { 6 | public interface IEventPublisher 7 | { 8 | /// 9 | /// Publish @event 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | Task Publish(TEvent @event, string exchange, string eventType, CancellationToken cancellationToken) where TEvent : IEvent; 16 | } 17 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.Abstractions/IEventService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace Enbiso.NLib.EventBus 5 | { 6 | public interface IEventService 7 | { 8 | /// 9 | /// Publish to bus 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | Task PublishToBus(T @event, string exchange = null, string eventType = null, 18 | CancellationToken token = default) where T : IEvent; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.Abstractions/IEventSubscriber.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Enbiso.NLib.EventBus 6 | { 7 | /// 8 | /// Event bus interface 9 | /// 10 | public interface IEventSubscriber: IDisposable 11 | { 12 | /// 13 | /// Subscribe to all events 14 | /// 15 | /// Cancellation Token 16 | /// 17 | Task Subscribe(CancellationToken token = default); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.Abstractions/IEventSubscriptionService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace Enbiso.NLib.EventBus 5 | { 6 | public interface IEventSubscriptionService 7 | { 8 | /// 9 | /// Subscribe all event processors 10 | /// 11 | /// 12 | Task SubscribeAll(CancellationToken token = default); 13 | } 14 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.AwsSns/AwsSnsConnection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Runtime.InteropServices.ComTypes; 3 | using System.Threading.Tasks; 4 | using Amazon.SimpleNotificationService; 5 | using Amazon.SimpleNotificationService.Model; 6 | 7 | namespace Enbiso.NLib.EventBus.AwsSns 8 | { 9 | public interface IAwsSnsConnection 10 | { 11 | IAmazonSimpleNotificationService GetConnection(); 12 | Task GetTopic(string exchangeName); 13 | } 14 | 15 | public class AwsSnsConnection: IAwsSnsConnection 16 | { 17 | private readonly IAmazonSimpleNotificationService _connection; 18 | private readonly Dictionary _topics = new(); 19 | 20 | public AwsSnsConnection(IAmazonSimpleNotificationService connection) 21 | { 22 | _connection = connection; 23 | } 24 | 25 | public IAmazonSimpleNotificationService GetConnection() => _connection; 26 | 27 | public async Task GetTopic(string exchangeName) 28 | { 29 | if (_topics.TryGetValue(exchangeName, out var existingTopic)) 30 | return existingTopic; 31 | var topic = await _connection.FindTopicAsync(exchangeName); 32 | if (topic == null) 33 | { 34 | await _connection.CreateTopicAsync(exchangeName); 35 | topic = await _connection.FindTopicAsync(exchangeName); 36 | } 37 | 38 | lock (_topics) 39 | { 40 | _topics[exchangeName] = topic; 41 | } 42 | return topic; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.AwsSns/AwsSnsEventPublisher.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Amazon.SimpleNotificationService.Model; 5 | using Microsoft.Extensions.Options; 6 | 7 | namespace Enbiso.NLib.EventBus.AwsSns 8 | { 9 | public class AwsSnsEventPublisher: IEventPublisher 10 | { 11 | private readonly IAwsSnsConnection _connection; 12 | private readonly AwsSnsOptions _options; 13 | 14 | public AwsSnsEventPublisher(IAwsSnsConnection connection, IOptions options) 15 | { 16 | _connection = connection; 17 | _options = options.Value; 18 | } 19 | 20 | public async Task Publish(TEvent @event, string exchange, string eventType, CancellationToken cancellationToken) where TEvent : IEvent 21 | { 22 | exchange ??= _options.PublishExchange; 23 | exchange = exchange?.Replace(".", "-"); 24 | var topic = await _connection.GetTopic(exchange); 25 | 26 | eventType ??= @event.GetType().Name; 27 | var message = JsonSerializer.Serialize(@event); 28 | 29 | var request = new PublishRequest(topic.TopicArn, message, eventType) 30 | { 31 | MessageAttributes = 32 | { 33 | ["EventType"] = new MessageAttributeValue {DataType = "String", StringValue = eventType} 34 | } 35 | }; 36 | 37 | await _connection.GetConnection().PublishAsync(request, cancellationToken); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.AwsSns/AwsSnsEventSubscriber.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace Enbiso.NLib.EventBus.AwsSns 5 | { 6 | public class AwsSnsEventSubscriber: IEventSubscriber 7 | { 8 | public void Dispose() 9 | { 10 | } 11 | 12 | public Task Subscribe(CancellationToken token = default) 13 | { 14 | return Task.CompletedTask; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.AwsSns/AwsSnsOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Enbiso.NLib.EventBus.AwsSns 2 | { 3 | public class AwsSnsOptions 4 | { 5 | public string PublishExchange { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.AwsSns/Enbiso.NLib.EventBus.AwsSns.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | Enbiso.NLib.EventBus.AwsSns 6 | 7 | 0.0.1-local 8 | $(PACKAGE_VERSION) 9 | 10 | Faraj Farook 11 | Simple Event bus implementation for AWS SNS 12 | false 13 | $(PACKAGE_COPYRIGHT) 14 | Enbiso Enbiso.NLib EventBus NATS 15 | true 16 | en-AU 17 | enbiso 18 | Initial Release 19 | Enbiso - SSimple Event bus implementation for AWS SNS in .NET Standard 20 | Simple AWS SNS EventBus 21 | 22 | logo.png 23 | $(PACKAGE_PROJECT) 24 | $(PACKAGE_REPO) 25 | default 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.AwsSns/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Amazon.SimpleNotificationService; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace Enbiso.NLib.EventBus.AwsSns 6 | { 7 | public static class ServiceExtensions 8 | { 9 | /// 10 | /// Add Event bus with default connection and manager 11 | /// 12 | /// 13 | /// 14 | public static void AddEventBusAwsSns(this IServiceCollection services, Action option) 15 | { 16 | services.Configure(option); 17 | 18 | services.AddEventBus(); 19 | services.AddSingleton(); 20 | services.AddSingleton(); 21 | services.AddSingleton(); 22 | services.AddSingleton(); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.Nats/Enbiso.NLib.EventBus.Nats.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | Enbiso.NLib.EventBus.Nats 6 | 7 | 0.0.1-local 8 | $(PACKAGE_VERSION) 9 | 10 | Faraj Farook 11 | Simple Event bus implementation for NATs 12 | false 13 | $(PACKAGE_COPYRIGHT) 14 | Enbiso Enbiso.NLib EventBus NATS 15 | true 16 | en-AU 17 | enbiso 18 | Initial Release 19 | Enbiso - Simple Event bus implementation for NATs in .NET Standard 20 | Simple NATs EventBus 21 | 22 | logo.png 23 | $(PACKAGE_PROJECT) 24 | $(PACKAGE_REPO) 25 | default 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.Nats/JetStream/JetStreamConnection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Logging; 3 | using NATS.Client.JetStream; 4 | 5 | namespace Enbiso.NLib.EventBus.Nats.JetStream; 6 | 7 | public interface IJetStreamConnection: IDisposable 8 | { 9 | void VerifyJetStream(string streamName); 10 | public IJetStream GetJetStream(); 11 | 12 | event ConnectedEventHandler Connected; 13 | } 14 | 15 | public class JetStreamConnection: IJetStreamConnection 16 | { 17 | private IJetStreamManagement _jetStreamManagement; 18 | private IJetStream _jetStream; 19 | private readonly INatsConnection _connection; 20 | 21 | public event ConnectedEventHandler Connected; 22 | 23 | public JetStreamConnection(INatsConnection connection, ILogger logger) 24 | { 25 | _connection = connection; 26 | connection.Connected += conn => 27 | { 28 | _jetStreamManagement = conn.CreateJetStreamManagementContext(); 29 | _jetStream = conn.CreateJetStreamContext(); 30 | logger.LogInformation("JetStream Initialised"); 31 | 32 | Connected?.Invoke(conn); 33 | }; 34 | } 35 | 36 | public void VerifyJetStream(string streamName) 37 | { 38 | _connection.VerifyConnection(); 39 | 40 | var streamNames = _jetStreamManagement.GetStreamNames(); 41 | if (streamNames.Contains(streamName)) return; 42 | 43 | var sc = StreamConfiguration.Builder() 44 | .WithName(streamName) 45 | .WithStorageType(StorageType.Memory) 46 | .WithRetentionPolicy(RetentionPolicy.Interest) //wait for all consumers 47 | .WithSubjects($"{streamName}.>") 48 | .Build(); 49 | _jetStreamManagement.AddStream(sc); 50 | } 51 | 52 | public IJetStream GetJetStream() => _jetStream; 53 | 54 | 55 | public void Dispose() 56 | { 57 | _connection?.Dispose(); 58 | } 59 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.Nats/JetStream/JetStreamEventPublisher.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.Json; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Microsoft.Extensions.Logging; 6 | using Microsoft.Extensions.Options; 7 | 8 | namespace Enbiso.NLib.EventBus.Nats.JetStream 9 | { 10 | public class JetStreamEventPublisher: IEventPublisher 11 | { 12 | private readonly IJetStreamConnection _jetStreamConnection; 13 | private readonly NatsOptions _options; 14 | private readonly ILogger _logger; 15 | 16 | public JetStreamEventPublisher(IOptions options, ILogger logger, IJetStreamConnection jetStreamConnection) 17 | { 18 | _options = options.Value; 19 | _logger = logger; 20 | _jetStreamConnection = jetStreamConnection; 21 | } 22 | 23 | public Task Publish(TEvent @event, string exchange, string eventType, CancellationToken cancellationToken) where TEvent : IEvent 24 | { 25 | exchange ??= _options.PublishExchange ?? _options.Exchanges.FirstOrDefault(); 26 | 27 | var eventPath = $"{exchange}.{eventType ?? @event.GetType().Name}"; 28 | var body = JsonSerializer.SerializeToUtf8Bytes(@event); 29 | 30 | var policy = NatsPolicyBuilder.BuildPublishPolicy(_options.PublishRetryCount, _logger); 31 | 32 | _jetStreamConnection.VerifyJetStream(exchange); 33 | var js = _jetStreamConnection.GetJetStream(); 34 | 35 | policy.Execute(() => 36 | { 37 | js.PublishAsync(eventPath, body); 38 | }); 39 | 40 | return Task.CompletedTask; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.Nats/JetStream/JetStreamEventSubscriber.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Microsoft.Extensions.Options; 5 | using NATS.Client.JetStream; 6 | 7 | namespace Enbiso.NLib.EventBus.Nats.JetStream 8 | { 9 | public class JetStreamEventSubscriber: IEventSubscriber 10 | { 11 | private readonly IJetStreamConnection _jsConnection; 12 | private readonly INatsConnection _natsConnection; 13 | private readonly IEventProcessor _eventProcessor; 14 | private readonly NatsOptions _options; 15 | 16 | public JetStreamEventSubscriber(IJetStreamConnection jsConnection, IOptions options, 17 | IEventProcessor eventProcessor, INatsConnection natsConnection) 18 | { 19 | _jsConnection = jsConnection; 20 | _eventProcessor = eventProcessor; 21 | _natsConnection = natsConnection; 22 | _options = options.Value; 23 | } 24 | 25 | public void Dispose() 26 | { 27 | _natsConnection?.Dispose(); 28 | _jsConnection?.Dispose(); 29 | } 30 | 31 | public Task Subscribe(CancellationToken token = default) 32 | { 33 | _jsConnection.Connected += _ => 34 | { 35 | foreach (var exchange in _options.Exchanges ?? Array.Empty()) 36 | { 37 | _jsConnection.VerifyJetStream(exchange); 38 | 39 | var js = _jsConnection.GetJetStream(); 40 | var pullOptions = PullSubscribeOptions.Builder() 41 | .WithDurable($"{_options.Client}_{exchange}") 42 | .Build(); 43 | var sub = js.PullSubscribe($"{exchange}.>", pullOptions); 44 | 45 | Task.Run(async () => 46 | { 47 | while (!token.IsCancellationRequested) 48 | { 49 | foreach (var message in sub.Fetch(_options.PollBatchSize, _options.PollWaitMills)) 50 | { 51 | var eventName = GetEventName(message.Subject, exchange); 52 | await _eventProcessor.ProcessEvent(eventName, message.Data); 53 | message.Ack(); 54 | } 55 | } 56 | }, token); 57 | } 58 | }; 59 | _natsConnection.VerifyConnection(); 60 | return Task.CompletedTask; 61 | } 62 | 63 | private static string GetEventName(string subject, string exchange) => 64 | subject.StartsWith(exchange) 65 | ? subject[(exchange.Length + 1)..] 66 | : subject; 67 | } 68 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.Nats/NatsConnection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Options; 6 | using NATS.Client; 7 | using Options = NATS.Client.Options; 8 | 9 | namespace Enbiso.NLib.EventBus.Nats 10 | { 11 | public interface INatsConnection : IDisposable 12 | { 13 | IConnection GetConnection(); 14 | void VerifyConnection(); 15 | event ConnectedEventHandler Connected; 16 | } 17 | 18 | public delegate void ConnectedEventHandler(IConnection connection); 19 | 20 | public class NatsConnection: INatsConnection 21 | { 22 | private IConnection _connection; 23 | private readonly ConnectionFactory _factory; 24 | private readonly Options _opts; 25 | private readonly ILogger _logger; 26 | private bool _disposed; 27 | public event ConnectedEventHandler Connected; 28 | 29 | public NatsConnection(ConnectionFactory factory, IOptions options, ILogger logger) 30 | { 31 | _logger = logger; 32 | _factory = factory; 33 | 34 | var settings = options.Value; 35 | _opts = ConnectionFactory.GetDefaultOptions(); 36 | _opts.Servers = settings.Servers; 37 | 38 | _opts.MaxReconnect = settings.RetryCount; 39 | if (!string.IsNullOrEmpty(settings.Username)) 40 | _opts.User = settings.Username; 41 | if (!string.IsNullOrEmpty(settings.Password)) 42 | _opts.Password = settings.Password; 43 | if (!string.IsNullOrEmpty(settings.Token)) 44 | _opts.Token = settings.Token; 45 | 46 | _opts.DisconnectedEventHandler += (sender, args) => 47 | { 48 | _logger.LogWarning("Connection to NATS disconnected. Trying to reconnect..."); 49 | VerifyConnection(); 50 | }; 51 | } 52 | 53 | public IConnection GetConnection() => _connection; 54 | 55 | private bool IsConnected => _connection?.State == ConnState.CONNECTED && !_disposed; 56 | public void VerifyConnection() 57 | { 58 | if (IsConnected) return; 59 | 60 | var policy = NatsPolicyBuilder.BuildConnectPolicy(_logger); 61 | 62 | policy.Execute(() => 63 | { 64 | var servers = _opts.Servers ?? Array.Empty(); 65 | _logger.LogInformation("Connecting to {Servers}", string.Join(", ", servers)); 66 | _connection = _factory.CreateConnection(_opts); 67 | if (!IsConnected) return; 68 | _logger.LogInformation("Connected to NATS"); 69 | Connected?.Invoke(_connection); 70 | }); 71 | } 72 | 73 | 74 | 75 | public void Dispose() 76 | { 77 | if (_disposed) return; 78 | _disposed = true; 79 | try 80 | { 81 | _connection.Drain(); 82 | _connection.Close(); 83 | _connection.Dispose(); 84 | } 85 | catch (Exception ex) 86 | { 87 | _logger.LogCritical("{Error}",ex.ToString()); 88 | } 89 | } 90 | 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.Nats/NatsEventPublisher.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.Json; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Microsoft.Extensions.Logging; 6 | using Microsoft.Extensions.Options; 7 | 8 | namespace Enbiso.NLib.EventBus.Nats; 9 | 10 | public class NatsEventPublisher: IEventPublisher 11 | { 12 | private readonly INatsConnection _connection; 13 | private readonly NatsOptions _options; 14 | private readonly ILogger _logger; 15 | 16 | public NatsEventPublisher(INatsConnection connection, IOptions options, ILogger logger) 17 | { 18 | _connection = connection; 19 | _options = options.Value; 20 | _logger = logger; 21 | } 22 | 23 | public Task Publish(TEvent @event, string exchange, string eventType, CancellationToken cancellationToken) where TEvent : IEvent 24 | { 25 | exchange ??= _options.PublishExchange ?? _options.Exchanges.FirstOrDefault(); 26 | 27 | var eventPath = $"{exchange}.{eventType ?? @event.GetType().Name}"; 28 | var body = JsonSerializer.SerializeToUtf8Bytes(@event); 29 | 30 | var policy = NatsPolicyBuilder.BuildPublishPolicy(_options.PublishRetryCount, _logger); 31 | 32 | _connection.VerifyConnection(); 33 | var conn = _connection.GetConnection(); 34 | 35 | policy.Execute(() => { 36 | conn.Publish(eventPath, body); 37 | conn.Flush(); 38 | }); 39 | 40 | return Task.CompletedTask; 41 | } 42 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.Nats/NatsEventSubscriber.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Microsoft.Extensions.Options; 5 | 6 | namespace Enbiso.NLib.EventBus.Nats; 7 | 8 | public class NatsEventSubscriber: IEventSubscriber 9 | { 10 | private readonly INatsConnection _connection; 11 | private readonly IEventProcessor _eventProcessor; 12 | private readonly NatsOptions _options; 13 | 14 | public NatsEventSubscriber(INatsConnection connection, IOptions options, IEventProcessor eventProcessor) 15 | { 16 | _connection = connection; 17 | _eventProcessor = eventProcessor; 18 | _options = options.Value; 19 | } 20 | 21 | public void Dispose() 22 | { 23 | _connection?.Dispose(); 24 | } 25 | 26 | public Task Subscribe(CancellationToken token = default) 27 | { 28 | _connection.Connected += conn => 29 | { 30 | foreach (var exchange in _options.Exchanges ?? Array.Empty()) 31 | { 32 | conn.SubscribeAsync($"{exchange}.>", _options.Client, async (sender, args) => 33 | { 34 | var eventName = GetEventName(args.Message.Subject, exchange); 35 | await _eventProcessor.ProcessEvent(eventName, args.Message.Data); 36 | }); 37 | } 38 | }; 39 | _connection.VerifyConnection(); 40 | return Task.CompletedTask; 41 | } 42 | 43 | private static string GetEventName(string subject, string exchange) => 44 | subject.StartsWith(exchange) 45 | ? subject[(exchange.Length + 1)..] 46 | : subject; 47 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.Nats/NatsOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Enbiso.NLib.EventBus.Nats 2 | { 3 | public class NatsOptions 4 | { 5 | public string[] Servers { get; set; } 6 | /// 7 | /// Rabbit server connection retries 8 | /// 9 | public int RetryCount { get; set; } = 5; 10 | /// 11 | /// Message publish retries 12 | /// 13 | public int PublishRetryCount { get; set; } = 5; 14 | /// 15 | /// Username 16 | /// 17 | public string Username { get; set; } 18 | /// 19 | /// Password 20 | /// 21 | public string Password { get; set; } 22 | /// 23 | /// Client (Queue) name 24 | /// 25 | public string Client { get; set; } 26 | /// 27 | /// Default brokers to publish messages 28 | /// 29 | public string PublishExchange { get; set; } 30 | /// 31 | /// Client brokers to get subscribed 32 | /// 33 | public string[] Exchanges { get; set; } 34 | /// 35 | /// Connection token 36 | /// 37 | public string Token { get; set; } 38 | /// 39 | /// Use NATS JetStream 40 | /// 41 | public bool EnableJetStream { get; set; } = false; 42 | /// 43 | /// Jet Stream poll batch size 44 | /// 45 | public int PollBatchSize { get; set; } = 1; 46 | /// 47 | /// Jet Stream poll wait ms 48 | /// 49 | public int PollWaitMills { get; set; } = 1000; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.Nats/NatsPolicyBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Sockets; 3 | using Microsoft.Extensions.Logging; 4 | using NATS.Client; 5 | using Polly; 6 | using Polly.Retry; 7 | 8 | namespace Enbiso.NLib.EventBus.Nats; 9 | 10 | public class NatsPolicyBuilder 11 | { 12 | public static RetryPolicy BuildPublishPolicy(int retryCount, ILogger logger) 13 | { 14 | var policy = Policy.Handle() 15 | .Or() 16 | .WaitAndRetry(retryCount, 17 | retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), 18 | (ex, _) => { logger.LogWarning("{Error}",ex.ToString()); }); 19 | return policy; 20 | } 21 | 22 | public static RetryPolicy BuildConnectPolicy(ILogger logger) 23 | { 24 | return Policy.Handle() 25 | .WaitAndRetryForever( 26 | _ => TimeSpan.FromSeconds(2), 27 | (ex, _) => logger.LogWarning("{Error}", ex.Message) ); 28 | } 29 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.Nats/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Enbiso.NLib.EventBus.Nats.JetStream; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using NATS.Client; 5 | 6 | namespace Enbiso.NLib.EventBus.Nats 7 | { 8 | public static class ServiceExtensions 9 | { 10 | /// 11 | /// Add Event bus with default connection and manager 12 | /// 13 | /// 14 | /// 15 | public static void AddEventBusNats(this IServiceCollection services, Action option) 16 | { 17 | services.Configure(option); 18 | 19 | services.AddEventBus(); 20 | services.AddSingleton(); 21 | services.AddSingleton(); 22 | 23 | var opts = new NatsOptions(); 24 | option.Invoke(opts); 25 | 26 | if (opts.EnableJetStream) 27 | { 28 | services.AddSingleton(); 29 | services.AddSingleton(); 30 | services.AddSingleton(); 31 | } 32 | else 33 | { 34 | services.AddSingleton(); 35 | services.AddSingleton(); 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.RabbitMq/Enbiso.NLib.EventBus.RabbitMq.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.1 4 | Enbiso.NLib.EventBus.RabbitMq 5 | 6 | 0.0.1-local 7 | $(PACKAGE_VERSION) 8 | 9 | Faraj Farook 10 | RabbitMq Implementation of EventBus 11 | false 12 | $(PACKAGE_COPYRIGHT) 13 | Enbiso Enbiso.NLib EventBus RabbitMq 14 | true 15 | en-AU 16 | enbiso 17 | Initial Release 18 | Enbiso - RabbitMq Implementation of EventBus in .NET Standard 19 | RabbitMq Impl EventBus 20 | 21 | logo.png 22 | $(PACKAGE_PROJECT) 23 | $(PACKAGE_REPO) 24 | default 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.RabbitMq/RabbitMqBusPublisher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Sockets; 5 | using System.Text; 6 | using System.Text.Json; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using Microsoft.Extensions.Logging; 10 | using Microsoft.Extensions.Options; 11 | using Polly; 12 | using RabbitMQ.Client; 13 | using RabbitMQ.Client.Exceptions; 14 | 15 | namespace Enbiso.NLib.EventBus.RabbitMq 16 | { 17 | public class RabbitMqBusPublisher: IEventPublisher 18 | { 19 | private readonly IRabbitMqConnection _connection; 20 | private readonly ILogger _logger; 21 | private readonly RabbitMqOption _options; 22 | 23 | public RabbitMqBusPublisher(IRabbitMqConnection connection, IOptions options, ILogger logger) 24 | { 25 | _connection = connection; 26 | _logger = logger; 27 | _options = options.Value; 28 | } 29 | 30 | public Task Publish(TEvent @event, string exchange, string eventType, CancellationToken cancellationToken) where TEvent : IEvent 31 | { 32 | var policy = Policy.Handle() 33 | .Or() 34 | .WaitAndRetry(_options.PublishRetryCount, 35 | retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, _) => 36 | { 37 | _logger.LogWarning(ex.ToString()); 38 | }); 39 | 40 | _connection.VerifyConnection(); 41 | 42 | exchange ??= _options.PublishExchange ?? _options.Exchanges.FirstOrDefault(); 43 | var channel = _connection.CreateModel(); 44 | channel.ExchangeDeclare(exchange: exchange, type: "direct"); 45 | 46 | var message = JsonSerializer.Serialize(@event); 47 | var body = Encoding.UTF8.GetBytes(message); 48 | 49 | eventType ??= @event.GetType().Name; 50 | 51 | policy.Execute(() => 52 | { 53 | channel.BasicPublish(exchange, eventType, null, body); 54 | }); 55 | 56 | return Task.CompletedTask; 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.RabbitMq/RabbitMqBusSubscriber.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Microsoft.Extensions.Options; 6 | using RabbitMQ.Client; 7 | using RabbitMQ.Client.Events; 8 | 9 | namespace Enbiso.NLib.EventBus.RabbitMq 10 | { 11 | public class RabbitMqBusSubscriber: IEventSubscriber 12 | { 13 | private IModel _consumerChannel; 14 | private readonly IRabbitMqConnection _connection; 15 | private readonly IEventProcessor _eventProcessor; 16 | private readonly string _queueName; 17 | private readonly IEnumerable _exchanges; 18 | 19 | public RabbitMqBusSubscriber(IRabbitMqConnection connection, IEventProcessor eventProcessor, 20 | IOptions optionWrap, IEventTypeManager eventTypeManager) 21 | { 22 | var option = optionWrap.Value; 23 | _queueName = option.Client; 24 | _exchanges = option.Exchanges ?? Array.Empty(); 25 | 26 | _connection = connection; 27 | _eventProcessor = eventProcessor; 28 | 29 | eventTypeManager.OnEventTypeAdded((_, args) => 30 | { 31 | using var channel = _connection.CreateModel(); 32 | foreach (var exchange in _exchanges) 33 | channel.QueueBind(queue: _queueName, exchange: exchange, routingKey: args.EventType.Name); 34 | }); 35 | } 36 | 37 | private IModel CreateConsumerChannel() 38 | { 39 | _connection.VerifyConnection(); 40 | var channel = _connection.CreateModel(); 41 | foreach (var exchange in _exchanges) 42 | { 43 | channel.ExchangeDeclare(exchange: exchange, type: "direct"); 44 | } 45 | 46 | channel.QueueDeclare(queue: _queueName, durable: true, exclusive: false, autoDelete: false, arguments: null); 47 | channel.CallbackException += (sender, ea) => 48 | { 49 | _consumerChannel.Dispose(); 50 | _consumerChannel = CreateConsumerChannel(); 51 | }; 52 | return channel; 53 | } 54 | 55 | public void Dispose() 56 | { 57 | _consumerChannel?.Dispose(); 58 | } 59 | 60 | public Task Subscribe(CancellationToken token = default) 61 | { 62 | _consumerChannel = CreateConsumerChannel(); 63 | var consumer = new EventingBasicConsumer(_consumerChannel); 64 | consumer.Received += async (model, ea) => 65 | { 66 | var eventName = ea.RoutingKey; 67 | await _eventProcessor.ProcessEvent(eventName, ea.Body.ToArray()); 68 | // ACK 69 | _consumerChannel.BasicAck(ea.DeliveryTag, multiple:false); 70 | }; 71 | _consumerChannel.BasicConsume(queue: _queueName, autoAck: false, consumer: consumer); 72 | return Task.CompletedTask; 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.RabbitMq/RabbitMqConnection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Sockets; 3 | using System.Threading.Tasks; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Options; 6 | using Polly; 7 | using RabbitMQ.Client; 8 | using RabbitMQ.Client.Events; 9 | using RabbitMQ.Client.Exceptions; 10 | 11 | namespace Enbiso.NLib.EventBus.RabbitMq 12 | { 13 | public interface IRabbitMqConnection : IDisposable 14 | { 15 | /// 16 | /// Verify Connection 17 | /// 18 | void VerifyConnection(); 19 | 20 | /// 21 | /// Create Model 22 | /// 23 | /// 24 | IModel CreateModel(); 25 | } 26 | 27 | public class RabbitMqConnection : IRabbitMqConnection 28 | { 29 | private IConnection _connection; 30 | private bool _disposed; 31 | private readonly IConnectionFactory _connectionFactory; 32 | private readonly ILogger _logger; 33 | private readonly int _retryCount; 34 | private readonly object _syncRoot = new object(); 35 | 36 | /// 37 | /// Create default Rabbit connection 38 | /// 39 | /// 40 | /// 41 | public RabbitMqConnection( 42 | ILogger logger, 43 | IOptions option) 44 | { 45 | var optVal = option.Value; 46 | _connectionFactory = new ConnectionFactory 47 | { 48 | HostName = optVal.Server ?? throw new ArgumentNullException(nameof(optVal.Server)), 49 | Port = optVal.Port 50 | }; 51 | if (!string.IsNullOrEmpty(optVal.Username)) 52 | _connectionFactory.UserName = optVal.Username; 53 | if (!string.IsNullOrEmpty(optVal.Password)) 54 | _connectionFactory.Password = optVal.Password; 55 | if (!string.IsNullOrEmpty(optVal.VirtualHost)) 56 | _connectionFactory.VirtualHost = optVal.VirtualHost; 57 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 58 | _retryCount = optVal.RetryCount; 59 | } 60 | 61 | private bool IsConnected 62 | => _connection != null && _connection.IsOpen && !_disposed; 63 | 64 | /// 65 | public void Dispose() 66 | { 67 | if (_disposed) return; 68 | _disposed = true; 69 | try 70 | { 71 | _connection.Dispose(); 72 | } 73 | catch (Exception ex) 74 | { 75 | _logger.LogCritical(ex.ToString()); 76 | } 77 | } 78 | 79 | private bool TryConnect() 80 | { 81 | _logger.LogInformation("RabbitMQ Client is trying to connect"); 82 | lock (_syncRoot) 83 | { 84 | var policy = Policy.Handle() 85 | .Or() 86 | .WaitAndRetry(_retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), 87 | (ex, time) => 88 | { 89 | _logger.LogWarning(ex.ToString()); 90 | }); 91 | 92 | policy.Execute(() => 93 | { 94 | _connection = _connectionFactory.CreateConnection(); 95 | }); 96 | 97 | if (!IsConnected) 98 | { 99 | _logger.LogCritical("FATAL ERROR: RabbitMQ connections could not be created and opened"); 100 | return false; 101 | } 102 | 103 | _connection.ConnectionShutdown += OnConnectionShutdown; 104 | _connection.CallbackException += OnCallbackException; 105 | _connection.ConnectionBlocked += OnConnectionBlocked; 106 | 107 | _logger.LogInformation( 108 | $"RabbitMQ persistent connection acquired a connection {_connection.Endpoint.HostName} and is subscribed to failure events"); 109 | return true; 110 | } 111 | } 112 | 113 | public void VerifyConnection() 114 | { 115 | if(!IsConnected && !TryConnect()) 116 | throw new Exception("Unable to connect to Rabbit MQ"); 117 | } 118 | 119 | public IModel CreateModel() 120 | { 121 | VerifyConnection(); 122 | return _connection.CreateModel(); 123 | } 124 | 125 | #region private methods 126 | 127 | private void OnConnectionBlocked(object sender, ConnectionBlockedEventArgs e) 128 | { 129 | if (_disposed) return; 130 | _logger.LogWarning("A RabbitMQ connection is shutdown. Trying to re-connect..."); 131 | TryConnect(); 132 | } 133 | 134 | private void OnCallbackException(object sender, CallbackExceptionEventArgs e) 135 | { 136 | if (_disposed) return; 137 | _logger.LogWarning("A RabbitMQ connection throw exception. Trying to re-connect..."); 138 | TryConnect(); 139 | } 140 | 141 | private void OnConnectionShutdown(object sender, ShutdownEventArgs reason) 142 | { 143 | if (_disposed) return; 144 | _logger.LogWarning("A RabbitMQ connection is on shutdown. Trying to re-connect..."); 145 | TryConnect(); 146 | } 147 | 148 | #endregion 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.RabbitMq/RabbitMqOption.cs: -------------------------------------------------------------------------------- 1 | namespace Enbiso.NLib.EventBus.RabbitMq 2 | { 3 | public class RabbitMqOption 4 | { 5 | /// 6 | /// Server host 7 | /// 8 | public string Server { get; set; } = "localhost"; 9 | /// 10 | /// Server Port 11 | /// 12 | public int Port { get; set; } = 5672; 13 | /// 14 | /// Username 15 | /// 16 | public string Username { get; set; } 17 | /// 18 | /// Password 19 | /// 20 | public string Password { get; set; } 21 | /// 22 | /// Virtual hosts 23 | /// 24 | public string VirtualHost { get; set; } 25 | /// 26 | /// Rabbit server connection retries 27 | /// 28 | public int RetryCount { get; set; } = 5; 29 | /// 30 | /// Message publish retries 31 | /// 32 | public int PublishRetryCount { get; set; } = 5; 33 | /// 34 | /// Client (Queue) name 35 | /// 36 | public string Client { get; set; } 37 | /// 38 | /// Default publish broker 39 | /// 40 | public string PublishExchange { get; set; } 41 | /// 42 | /// Client brokers name 43 | /// 44 | public string[] Exchanges { get; set; } 45 | } 46 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus.RabbitMq/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace Enbiso.NLib.EventBus.RabbitMq 6 | { 7 | public static class ServiceExtensions 8 | { 9 | /// 10 | /// Add Event bus wth custom connection and subscription manager 11 | /// 12 | /// 13 | /// 14 | public static void AddEventBusRabbitMq(this IServiceCollection services, Action option) 15 | { 16 | services.Configure(option); 17 | 18 | services.AddSingleton(); 19 | services.AddSingleton(); 20 | services.AddSingleton(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus/Enbiso.NLib.EventBus.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | Enbiso.NLib.EventBus 6 | 7 | 0.0.1-local 8 | $(PACKAGE_VERSION) 9 | 10 | Faraj Farook 11 | Simple Abstract event bus library 12 | false 13 | $(PACKAGE_COPYRIGHT) 14 | Enbiso Enbiso.NLib EventBus 15 | true 16 | en-AU 17 | enbiso 18 | Initial Release 19 | Enbiso - Simple Abstract EventBus Library in .NET Standard 20 | Simple Abstract EventBus 21 | 22 | logo.png 23 | $(PACKAGE_PROJECT) 24 | $(PACKAGE_REPO) 25 | default 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus/Event.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.EventBus 4 | { 5 | /// 6 | /// Integration event abstraction 7 | /// 8 | public abstract class Event : IEvent 9 | { 10 | protected Event() 11 | { 12 | EventId = Guid.NewGuid(); 13 | EventCreationDate = DateTime.UtcNow; 14 | } 15 | 16 | public Guid EventId { get; } 17 | public DateTime EventCreationDate { get; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus/EventProcessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.Json; 6 | using System.Threading.Tasks; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace Enbiso.NLib.EventBus 10 | { 11 | public interface IEventProcessor 12 | { 13 | Task ProcessEvent(string eventName, byte[] data); 14 | void AddEventHandler(IEventHandler handler); 15 | } 16 | 17 | public class EventProcessor: IEventProcessor 18 | { 19 | private readonly List _eventHandlers = new(); 20 | private readonly ILogger _logger; 21 | private readonly IEventTypeManager _eventTypeManager; 22 | 23 | public EventProcessor(IEnumerable eventHandlers, ILogger logger, 24 | IEventTypeManager eventTypeManager) 25 | { 26 | _logger = logger; 27 | _eventTypeManager = eventTypeManager; 28 | foreach (var eventHandler in eventHandlers) 29 | AddEventHandler(eventHandler); 30 | } 31 | 32 | public async Task ProcessEvent(string eventName, byte[] data) 33 | { 34 | var message = Encoding.UTF8.GetString(data); 35 | 36 | foreach (var eventHandler in _eventHandlers.Where(h => h.IsValidFor(eventName))) 37 | { 38 | var eventType = eventHandler.EventType; 39 | var @event = JsonSerializer.Deserialize(message, eventType); 40 | 41 | var eventId = @event is IEvent iEvent ? iEvent.EventId : Guid.Empty; 42 | try 43 | { 44 | _logger.LogTrace("Processing {EventType} {EventId}", eventType, eventId); 45 | await eventHandler.Handle(@event); 46 | } 47 | catch (Exception e) 48 | { 49 | _logger.LogError(e, "Error Processing {EventType} {EventId}", eventType, eventId); 50 | } 51 | } 52 | } 53 | 54 | public void AddEventHandler(IEventHandler handler) 55 | { 56 | _eventHandlers.Add(handler); 57 | _eventTypeManager.Add(handler.EventType); 58 | } 59 | } 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus/EventService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Enbiso.NLib.EventBus 7 | { 8 | public class EventService : IEventService 9 | { 10 | private readonly IEnumerable _publishers; 11 | 12 | public EventService(IEnumerable publishers) 13 | { 14 | _publishers = publishers; 15 | } 16 | 17 | public Task PublishToBus(T @event, string exchange, string eventType, CancellationToken token) where T : IEvent => 18 | Task.WhenAll(_publishers.Select(p => p.Publish(@event, exchange, eventType, token))); 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus/EventSubscriptionService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Enbiso.NLib.EventBus 7 | { 8 | public class EventSubscriptionService : IEventSubscriptionService 9 | { 10 | private readonly IEnumerable _subscribers; 11 | 12 | public EventSubscriptionService(IEnumerable subscribers) 13 | { 14 | _subscribers = subscribers; 15 | } 16 | 17 | public Task SubscribeAll(CancellationToken token = default) => 18 | Task.WhenAll(_subscribers.Select(s => s.Subscribe(token))); 19 | } 20 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus/EventTypeManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Enbiso.NLib.EventBus; 5 | 6 | public interface IEventTypeManager 7 | { 8 | public void Add(Type eventType); 9 | public void OnEventTypeAdded(EventTypeAddedEventHandler handler); 10 | } 11 | 12 | public class EventTypeManager: IEventTypeManager 13 | { 14 | private readonly List _eventTypes = new(); 15 | private event EventTypeAddedEventHandler EventTypeAdded; 16 | 17 | public void Add(Type eventType) 18 | { 19 | if (_eventTypes.Contains(eventType)) return; 20 | _eventTypes.Add(eventType); 21 | EventTypeAdded?.Invoke(this, new EventTypeAddedEventArgs(eventType)); 22 | } 23 | 24 | public void OnEventTypeAdded(EventTypeAddedEventHandler handler) 25 | { 26 | ReplayEventsForHandler(handler); 27 | EventTypeAdded += handler; 28 | } 29 | 30 | private void ReplayEventsForHandler(EventTypeAddedEventHandler handler) 31 | { 32 | foreach (var eventType in _eventTypes) 33 | handler(this, new EventTypeAddedEventArgs(eventType)); 34 | } 35 | } 36 | 37 | 38 | public class EventTypeAddedEventArgs: EventArgs 39 | { 40 | public EventTypeAddedEventArgs(Type eventType) 41 | { 42 | EventType = eventType; 43 | } 44 | 45 | public Type EventType { get; } 46 | } 47 | 48 | public delegate void EventTypeAddedEventHandler(object sender, EventTypeAddedEventArgs e); -------------------------------------------------------------------------------- /Enbiso.NLib.EventBus/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace Enbiso.NLib.EventBus 4 | { 5 | public static class ServiceExtensions 6 | { 7 | /// 8 | /// Add Event bus with default connection and manager 9 | /// 10 | /// 11 | public static void AddEventBus(this IServiceCollection services) 12 | { 13 | services.AddSingleton(); 14 | services.AddSingleton(); 15 | services.AddSingleton(); 16 | services.AddTransient(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventInfo/Enbiso.NLib.EventInfo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net7.0 4 | Enbiso.NLib.EventInfo 5 | 6 | 0.0.1-local 7 | $(PACKAGE_VERSION) 8 | 9 | Faraj Farook 10 | EventInfo for integration events 11 | false 12 | $(PACKAGE_COPYRIGHT) 13 | Enbiso Enbiso.NLib EventInfo 14 | true 15 | en-AU 16 | enbiso 17 | Initial Release 18 | Enbiso - EventInfo for integration events in .NET 19 | EventInfo for integration events 20 | 21 | logo.png 22 | $(PACKAGE_PROJECT) 23 | $(PACKAGE_REPO) 24 | default 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventInfo/EventInfoController.cs: -------------------------------------------------------------------------------- 1 | using Enbiso.NLib.EventInfo.Models; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace Enbiso.NLib.EventInfo; 5 | 6 | [Route("_events")] 7 | [ApiExplorerSettings(IgnoreApi = true)] 8 | public class EventInfoController: ControllerBase 9 | { 10 | private readonly IEventInfoService _eventInfoService; 11 | 12 | public EventInfoController(IEventInfoService eventInfoService) 13 | { 14 | _eventInfoService = eventInfoService; 15 | } 16 | 17 | [HttpGet] 18 | public EventInfoListResponse ListEvents() => _eventInfoService.List(); 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventInfo/EventInfoService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Enbiso.NLib.EventBus; 5 | using Enbiso.NLib.EventInfo.Models; 6 | using Microsoft.Extensions.Options; 7 | 8 | namespace Enbiso.NLib.EventInfo; 9 | 10 | public interface IEventInfoService 11 | { 12 | public EventInfoListResponse List(); 13 | } 14 | 15 | public class EventInfoService: IEventInfoService 16 | { 17 | private EventInfoListResponse _response; 18 | private readonly EventInfoOption _option; 19 | 20 | public EventInfoService(IOptions option) 21 | { 22 | _option = option.Value; 23 | } 24 | 25 | public EventInfoListResponse List() 26 | { 27 | if (_response != null) return _response; 28 | 29 | var parentTypes = _option.SearchTypes ?? new List(); 30 | parentTypes.Add(typeof(IEvent)); 31 | 32 | var assemblies = AppDomain.CurrentDomain.GetAssemblies() 33 | .Where(p => !(p.FullName?.Contains("NLib") ?? false)); 34 | 35 | var types = assemblies 36 | .SelectMany(s => s.GetTypes()) 37 | .Where(t => t.IsClass) 38 | .Where(t => parentTypes.Any(pt => pt.IsAssignableFrom(t))); 39 | 40 | var records = types.Select(t => new EventRecord(t)).ToList(); 41 | _response = new EventInfoListResponse 42 | { 43 | Records = records, 44 | Count = records.Count, 45 | }; 46 | return _response; 47 | } 48 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventInfo/Models/EventInfoListResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Enbiso.NLib.EventInfo.Models; 4 | 5 | public class EventInfoListResponse 6 | { 7 | public List Records { get; set; } = new(); 8 | public int Count { get; set; } 9 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventInfo/Models/EventRecord.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Enbiso.NLib.EventInfo.Models; 6 | 7 | public class EventRecord 8 | { 9 | public string Name { get; } 10 | public List Props { get; } 11 | 12 | public EventRecord(Type type) 13 | { 14 | Name = type.Name; 15 | Props = type.GetProperties().Select(EventRecordMapper.Map).ToList(); 16 | } 17 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventInfo/Models/EventRecordMapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace Enbiso.NLib.EventInfo.Models; 7 | 8 | public static class EventRecordMapper 9 | { 10 | public static EventRecordProp Map(PropertyInfo property) 11 | { 12 | var prop = new EventRecordProp(property); 13 | if (!IsSimpleType(property.PropertyType)) 14 | prop.Props = property.PropertyType.GetProperties() 15 | .Select(p => new EventRecordProp(p)).ToList(); 16 | return prop; 17 | } 18 | 19 | private static bool ShouldExtract(Type type) 20 | { 21 | if (IsSimpleType(type)) return false; 22 | var excludeTypes = new List 23 | { 24 | typeof(List<>), 25 | typeof(IEnumerable<>), 26 | typeof(Dictionary<,>), 27 | typeof(IDictionary<,>) 28 | }; 29 | return excludeTypes.All(p => p == type); 30 | } 31 | 32 | private static bool IsSimpleType(Type type) 33 | { 34 | return 35 | type.IsPrimitive || 36 | type.IsValueType || 37 | new[] 38 | { 39 | typeof(string), 40 | typeof(decimal), 41 | typeof(DateTime), 42 | typeof(DateTimeOffset), 43 | typeof(TimeSpan), 44 | typeof(Guid) 45 | }.Contains(type) || 46 | type.IsEnum || 47 | Convert.GetTypeCode(type) != TypeCode.Object || 48 | (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && 49 | IsSimpleType(type.GetGenericArguments()[0])); 50 | } 51 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventInfo/Models/EventRecordProp.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection; 3 | 4 | namespace Enbiso.NLib.EventInfo.Models; 5 | 6 | public class EventRecordProp 7 | { 8 | public EventRecordProp(PropertyInfo property) 9 | { 10 | Name = property.Name; 11 | Type = property.PropertyType.Name; 12 | } 13 | 14 | public string Name { get; } 15 | public string Type { get; } 16 | public List Props { get; set; } 17 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventInfo/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace Enbiso.NLib.EventInfo; 6 | 7 | public static class ServiceExtensions 8 | { 9 | public static IServiceCollection AddEventInfo(this IServiceCollection collection, Action optBuilder = null) 10 | { 11 | collection.AddOptions(); 12 | optBuilder ??= _ => {}; 13 | collection.Configure(optBuilder); 14 | 15 | collection.AddSingleton(); 16 | return collection; 17 | } 18 | } 19 | 20 | public class EventInfoOption 21 | { 22 | public List SearchTypes { get; set; } = new(); 23 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventLogger.EntityFramework/Enbiso.NLib.EventLogger.EntityFramework.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net7.0 4 | Enbiso.NLib.EventLogger.EntityFramework 5 | 0.0.1-local 6 | $(PACKAGE_VERSION) 7 | Faraj Farook 8 | EventLog Persistence with Entity Framework 9 | false 10 | $(PACKAGE_COPYRIGHT) 11 | Enbiso Enbiso.NLib EventLog 12 | true 13 | en-AU 14 | enbiso 15 | Initial Release 16 | Enbiso - EventLog Persistence with EF in .NET Standard 17 | EventLog Persistence using EF 18 | logo.png 19 | $(PACKAGE_PROJECT) 20 | $(PACKAGE_REPO) 21 | default 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventLogger.EntityFramework/EntityEventLogRepo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Common; 3 | using System.Threading.Tasks; 4 | using Microsoft.EntityFrameworkCore; 5 | 6 | namespace Enbiso.NLib.EventLogger.EntityFramework 7 | { 8 | /// 9 | /// 10 | /// Entity Framework event log repo imelemtations 11 | /// 12 | /// 13 | public class EntityEventLogRepo: IEventLogRepo where TDbContext: DbContext 14 | { 15 | private readonly DbContext _context; 16 | 17 | public EntityEventLogRepo(TDbContext context) 18 | { 19 | _context = context; 20 | } 21 | 22 | /// 23 | /// Find by ID 24 | /// 25 | /// 26 | /// 27 | public Task FindByIdAsync(Guid id) 28 | { 29 | return _context.Set().FirstOrDefaultAsync(e => e.EventId == id); 30 | } 31 | 32 | /// 33 | /// Add 34 | /// 35 | /// 36 | /// 37 | public EventLog Add(EventLog eventLog) 38 | { 39 | return _context.Set().Add(eventLog).Entity; 40 | } 41 | 42 | /// 43 | /// Update 44 | /// 45 | /// 46 | /// 47 | public EventLog Update(EventLog eventLog) 48 | { 49 | return _context.Set().Update(eventLog).Entity; 50 | } 51 | 52 | /// 53 | /// Use transaction 54 | /// 55 | /// 56 | public void UseTransaction(DbTransaction transaction) 57 | { 58 | _context.Database.UseTransaction(transaction); 59 | } 60 | 61 | /// 62 | /// Save async 63 | /// 64 | /// 65 | public Task SaveChangesAsync() 66 | { 67 | return _context.SaveChangesAsync(); 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventLogger.EntityFramework/EventLogEntityConfig.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | 4 | namespace Enbiso.NLib.EventLogger.EntityFramework 5 | { 6 | /// 7 | /// 8 | /// Event log entity configurations 9 | /// 10 | public class EventLogEntityConfig : IEntityTypeConfiguration 11 | { 12 | public void Configure(EntityTypeBuilder builder) 13 | { 14 | builder.Ignore(e => e.State); 15 | 16 | builder.HasKey(e => e.EventId); 17 | 18 | builder.Property(e => e.EventId) 19 | .IsRequired(); 20 | 21 | builder.Property(e => e.Content) 22 | .IsRequired(); 23 | 24 | builder.Property(e => e.CreationTime) 25 | .IsRequired(); 26 | 27 | builder.Property("_state") 28 | .IsRequired().HasColumnName("State"); 29 | 30 | builder.Property(e => e.TimesSent) 31 | .IsRequired(); 32 | 33 | builder.Property(e => e.EventTypeName) 34 | .IsRequired(); 35 | } 36 | } 37 | 38 | /// 39 | /// Model builder extensions 40 | /// 41 | public static class ModelBuilderExtensions 42 | { 43 | public static ModelBuilder ApplyEventLoggerConfiguration(this ModelBuilder builder) 44 | { 45 | return builder.ApplyConfiguration(new EventLogEntityConfig()); 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventLogger.EntityFramework/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Enbiso.NLib.EventLogger.EntityFramework 5 | { 6 | public static class ServiceExtensions 7 | { 8 | public static void AddEventLogger(this IServiceCollection services) where TDbContext: DbContext 9 | { 10 | services.AddEventLogger(); 11 | services.AddTransient>(); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventLogger.Mongo/Enbiso.NLib.EventLogger.Mongo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | Enbiso.NLib.EventLogger.Mongo 6 | 0.0.1-local 7 | $(PACKAGE_VERSION) 8 | Faraj Farook 9 | EventLog Persistence with MongoDb 10 | false 11 | $(PACKAGE_COPYRIGHT) 12 | Enbiso Enbiso.NLib EventLog 13 | true 14 | en-AU 15 | enbiso 16 | Initial Release 17 | Enbiso - EventLog Persistence with Mongo DB in .NET Standard 18 | EventLog Persistence using Mongo 19 | logo.png 20 | $(PACKAGE_PROJECT) 21 | $(PACKAGE_REPO) 22 | default 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventLogger.Mongo/MongoEventLogRepo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Common; 3 | using System.Threading.Tasks; 4 | using MongoDB.Bson.Serialization.Attributes; 5 | using MongoDB.Driver; 6 | 7 | namespace Enbiso.NLib.EventLogger.Mongo 8 | { 9 | public class MongoEventLog 10 | { 11 | [BsonId] 12 | public string Id { get; set; } 13 | public EventLog Log { get; set; } 14 | } 15 | 16 | public class MongoEventLogRepo : IEventLogRepo 17 | { 18 | private readonly IMongoCollection _logs; 19 | private readonly IMongoClient _client; 20 | public MongoEventLogRepo(IMongoClient client, string dbName) 21 | { 22 | _client = client; 23 | _logs = client.GetDatabase(dbName).GetCollection("eventLogs"); 24 | } 25 | 26 | public Task FindByIdAsync(Guid id) 27 | => _logs.Find(l => l.Id == id.ToString()).FirstOrDefaultAsync().ContinueWith(t => t.Result?.Log); 28 | 29 | public EventLog Add(EventLog eventLog) 30 | { 31 | _logs.InsertOne(new MongoEventLog 32 | { 33 | Id = eventLog.EventId.ToString(), 34 | Log = eventLog 35 | }); 36 | return eventLog; 37 | } 38 | 39 | public EventLog Update(EventLog eventLog) => _logs.FindOneAndUpdate(l => l.Id == eventLog.EventId.ToString(), 40 | Builders.Update.Set(l => l.Log, eventLog)).Log; 41 | 42 | public void UseTransaction(DbTransaction transaction) {} 43 | 44 | public Task SaveChangesAsync() { return Task.CompletedTask; } 45 | } 46 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventLogger.Mongo/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using MongoDB.Driver; 3 | 4 | namespace Enbiso.NLib.EventLogger.Mongo 5 | { 6 | public static class ServiceExtensions 7 | { 8 | public static void AddEventLogger(this IServiceCollection services, string dbname) 9 | { 10 | services.AddEventLogger(); 11 | services.AddTransient(sp => new MongoEventLogRepo(sp.GetService(), dbname)); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventLogger/Enbiso.NLib.EventLogger.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.1 4 | Enbiso.NLib.EventLogger 5 | 6 | 0.0.1-local 7 | $(PACKAGE_VERSION) 8 | 9 | Faraj Farook 10 | EventLogs for integration events 11 | false 12 | $(PACKAGE_COPYRIGHT) 13 | Enbiso Enbiso.NLib EventLog 14 | true 15 | en-AU 16 | enbiso 17 | Initial Release 18 | Enbiso - EventLogs for integration events in .NET Standard 19 | EventLogs for integration events 20 | 21 | logo.png 22 | $(PACKAGE_PROJECT) 23 | $(PACKAGE_REPO) 24 | default 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventLogger/EventLog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | using Enbiso.NLib.EventBus; 4 | 5 | namespace Enbiso.NLib.EventLogger 6 | { 7 | public class EventLog 8 | { 9 | private EventLog() { } 10 | public EventLog(IEvent @event, string eventType) 11 | { 12 | EventId = @event.EventId; 13 | CreationTime = @event.EventCreationDate; 14 | EventTypeName = eventType ?? @event.GetType().FullName; 15 | Content = JsonSerializer.Serialize(@event); 16 | State = EventState.NotPublished; 17 | TimesSent = 0; 18 | } 19 | public Guid EventId { get; private set; } 20 | public string EventTypeName { get; private set; } 21 | private string _state; 22 | 23 | public EventState State 24 | { 25 | get => Enum.TryParse(_state, out var val) ? val : default(EventState); 26 | set => _state = value.ToString(); 27 | } 28 | 29 | public int TimesSent { get; set; } 30 | public DateTime CreationTime { get; private set; } 31 | public string Content { get; private set; } 32 | } 33 | 34 | /// 35 | /// Event states 36 | /// 37 | public enum EventState 38 | { 39 | NotPublished = 0, 40 | Published = 1, 41 | Failed = 2 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventLogger/EventLoggerEventService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Enbiso.NLib.EventBus; 7 | 8 | namespace Enbiso.NLib.EventLogger 9 | { 10 | public class EventLoggerEventService: IEventService 11 | { 12 | private readonly IEnumerable _publishers; 13 | private readonly IEnumerable _subscribers; 14 | private readonly IEventLoggerService _service; 15 | 16 | public EventLoggerEventService(IEventLoggerService service, IEnumerable subscribers, 17 | IEnumerable publishers) 18 | { 19 | _service = service; 20 | _subscribers = subscribers; 21 | _publishers = publishers; 22 | } 23 | 24 | public async Task PublishToBus(T @event, string exchange, string eventType, CancellationToken token) where T: IEvent 25 | { 26 | await _service.SaveEventAsync(@event, eventType); 27 | try 28 | { 29 | await Task.WhenAll(_publishers.Select(p => p.Publish(@event, exchange, eventType, token))); 30 | await _service.MarkEventAsPublishedAsync(@event); 31 | } 32 | catch (Exception) 33 | { 34 | await _service.MarkEventAsFailedAsync(@event); 35 | throw; 36 | } 37 | } 38 | 39 | public Task SubscribeAll(CancellationToken token = default) => 40 | Task.WhenAll(_subscribers.Select(s => s.Subscribe(token))); 41 | 42 | } 43 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventLogger/EventLoggerService.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Common; 2 | using System.Threading.Tasks; 3 | using Enbiso.NLib.EventBus; 4 | 5 | namespace Enbiso.NLib.EventLogger 6 | { 7 | /// 8 | /// EventLogger service 9 | /// 10 | public interface IEventLoggerService 11 | { 12 | Task SaveEventAsync(IEvent @event, string eventType, DbTransaction transaction = null); 13 | Task MarkEventAsPublishedAsync(IEvent @event); 14 | Task MarkEventAsFailedAsync(IEvent @event); 15 | } 16 | 17 | /// 18 | /// 19 | /// Event logger implementation 20 | /// 21 | public class EventLoggerService : IEventLoggerService 22 | { 23 | private readonly IEventLogRepo _repo; 24 | 25 | public EventLoggerService(IEventLogRepo repo) 26 | { 27 | _repo = repo; 28 | } 29 | 30 | /// 31 | /// Save events 32 | /// 33 | /// 34 | /// 35 | /// 36 | /// 37 | public Task SaveEventAsync(IEvent @event, string eventType, DbTransaction transaction) 38 | { 39 | if (transaction != null) 40 | _repo.UseTransaction(transaction); 41 | 42 | var eventLogEntry = new EventLog(@event, eventType); 43 | 44 | _repo.Add(eventLogEntry); 45 | return _repo.SaveChangesAsync(); 46 | } 47 | 48 | /// 49 | /// Mark @event as published 50 | /// 51 | /// 52 | /// 53 | public async Task MarkEventAsPublishedAsync(IEvent @event) 54 | { 55 | var eventLogEntry = await _repo.FindByIdAsync(@event.EventId); 56 | eventLogEntry.TimesSent++; 57 | eventLogEntry.State = EventState.Published; 58 | _repo.Update(eventLogEntry); 59 | await _repo.SaveChangesAsync(); 60 | } 61 | 62 | /// 63 | /// Mark as failed 64 | /// 65 | /// 66 | /// 67 | public async Task MarkEventAsFailedAsync(IEvent @event) 68 | { 69 | var eventLogEntry = await _repo.FindByIdAsync(@event.EventId); 70 | eventLogEntry.State = EventState.Failed; 71 | _repo.Update(eventLogEntry); 72 | await _repo.SaveChangesAsync(); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Enbiso.NLib.EventLogger/IEventLogRepo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Common; 3 | using System.Threading.Tasks; 4 | 5 | namespace Enbiso.NLib.EventLogger 6 | { 7 | /// 8 | /// Event log repository 9 | /// 10 | public interface IEventLogRepo 11 | { 12 | Task FindByIdAsync(Guid id); 13 | EventLog Add(EventLog eventLog); 14 | EventLog Update(EventLog eventLog); 15 | void UseTransaction(DbTransaction transaction); 16 | Task SaveChangesAsync(); 17 | } 18 | } -------------------------------------------------------------------------------- /Enbiso.NLib.EventLogger/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Enbiso.NLib.EventBus; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace Enbiso.NLib.EventLogger 6 | { 7 | public static class ServiceExtensions 8 | { 9 | public static void AddEventLogger(this IServiceCollection services) 10 | { 11 | services.AddTransient(); 12 | 13 | var eventService = services.FirstOrDefault(descriptor => descriptor.ServiceType == typeof(IEventService)); 14 | if (eventService != null) services.Remove(eventService); 15 | 16 | services.AddTransient(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Enbiso.NLib.GlobalExceptions/Enbiso.NLib.GlobalExceptions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net7.0 4 | Enbiso.NLib.GlobalExceptions 5 | 6 | 0.0.1-local 7 | $(PACKAGE_VERSION) 8 | 9 | Faraj Farook 10 | Simple Global Exception handler 11 | false 12 | $(PACKAGE_COPYRIGHT) 13 | Enbiso Enbiso.NLib GlobalExceptions 14 | true 15 | en-AU 16 | enbiso 17 | Initial Release 18 | Enbiso - Simple Global Exception handler in .NET Standard 19 | Simple Global Exception handler 20 | 21 | logo.png 22 | $(PACKAGE_PROJECT) 23 | $(PACKAGE_REPO) 24 | default 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Enbiso.NLib.GlobalExceptions/GlobalExceptionFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.AspNetCore.Mvc.Filters; 6 | using Microsoft.Extensions.Logging; 7 | 8 | namespace Enbiso.NLib.GlobalExceptions 9 | { 10 | public class GlobalExceptionFilter : IAsyncExceptionFilter 11 | { 12 | private readonly ILogger _logger; 13 | private readonly IEnumerable _handlers; 14 | 15 | public GlobalExceptionFilter(ILogger logger, IEnumerable handlers) 16 | { 17 | _logger = logger; 18 | _handlers = handlers; 19 | } 20 | 21 | public async Task OnExceptionAsync(ExceptionContext context) 22 | { 23 | _logger.LogError(new EventId(context.Exception.HResult), 24 | context.Exception, 25 | context.Exception.Message); 26 | 27 | var handler = _handlers.FirstOrDefault(h => h.ValidTypes.Contains(context.Exception.GetType())); 28 | if (handler != null) 29 | { 30 | var result = await handler.HandleException(context.Exception); 31 | context.Result = new ObjectResult(new 32 | { 33 | result.Content, 34 | ErrorType = context.Exception.GetType().Name 35 | }); 36 | context.HttpContext.Response.StatusCode = result.StatusCode; 37 | context.ExceptionHandled = true; 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Enbiso.NLib.GlobalExceptions/GlobalExceptionHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace Enbiso.NLib.GlobalExceptions 6 | { 7 | /// 8 | /// Global exception handler interface 9 | /// 10 | public interface IGlobalExceptionHandler 11 | { 12 | IEnumerable ValidTypes { get; } 13 | 14 | Task HandleException(Exception exception); 15 | } 16 | 17 | /// 18 | /// Abstract Global exception handler 19 | /// 20 | /// 21 | public abstract class GlobalExceptionHandler : IGlobalExceptionHandler 22 | where TException : Exception 23 | { 24 | public IEnumerable ValidTypes => new [] { typeof(TException) }; 25 | protected abstract Task Handle(TException ex); 26 | 27 | public Task HandleException(Exception exception) 28 | { 29 | if (exception is TException ex) 30 | { 31 | return Handle(ex); 32 | } 33 | return null; 34 | } 35 | 36 | } 37 | } -------------------------------------------------------------------------------- /Enbiso.NLib.GlobalExceptions/GlobalExceptionHandlerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Enbiso.NLib.GlobalExceptions 4 | { 5 | public static class GlobalExceptionHandlerExtensions 6 | { 7 | public static Task BadRequest(this IGlobalExceptionHandler handler, object result) 8 | { 9 | return Task.FromResult(new GlobalExceptionResponse(result, 400)); 10 | } 11 | 12 | public static Task UnProcessableEntity(this IGlobalExceptionHandler handler, object result) 13 | { 14 | return Task.FromResult(new GlobalExceptionResponse(result, 422)); 15 | } 16 | 17 | public static Task NotFound(this IGlobalExceptionHandler handler, object result) 18 | { 19 | return Task.FromResult(new GlobalExceptionResponse(result, 404)); 20 | } 21 | 22 | public static Task Ok(this IGlobalExceptionHandler handler, object result) 23 | { 24 | return Task.FromResult(new GlobalExceptionResponse(result, 200)); 25 | } 26 | 27 | public static Task InternalServerError(this IGlobalExceptionHandler handler, object result) 28 | { 29 | return Task.FromResult(new GlobalExceptionResponse(result, 500)); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Enbiso.NLib.GlobalExceptions/GlobalExceptionResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.GlobalExceptions 4 | { 5 | /// 6 | /// Exception handler response with type 7 | /// 8 | public class GlobalExceptionResponse 9 | { 10 | public object Content { get; set; } 11 | public int StatusCode { get; set; } 12 | public GlobalExceptionResponse(object content, int statusCode) 13 | { 14 | Content = content; 15 | StatusCode = statusCode; 16 | } 17 | 18 | } 19 | } -------------------------------------------------------------------------------- /Enbiso.NLib.HttpContextService/Enbiso.NLib.HttpContextService.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net7.0 4 | Enbiso.NLib.HttpContextService 5 | 6 | 0.0.1-local 7 | $(PACKAGE_VERSION) 8 | 9 | Faraj Farook 10 | Http Context Fetcher 11 | false 12 | $(PACKAGE_COPYRIGHT) 13 | Enbiso Enbiso.NLib HttpContextService 14 | true 15 | en-AU 16 | enbiso 17 | Initial Release 18 | Enbiso - Http Context Fetcher in .NET Standard 19 | Simple Http Context Service 20 | 21 | logo.png 22 | $(PACKAGE_PROJECT) 23 | $(PACKAGE_REPO) 24 | default 25 | Enbiso.NLib.HttpContextService 26 | Library 27 | true 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Enbiso.NLib.HttpContextService/HttpContextFetcher.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.AspNetCore.Routing; 5 | 6 | namespace Enbiso.NLib.HttpContextService; 7 | 8 | public enum HttpContextSource 9 | { 10 | Query, 11 | Route, 12 | JsonBody 13 | } 14 | 15 | public record HttpContextFetchRequest(HttpContextSource Source, string Name) 16 | { 17 | public static HttpContextFetchRequest FromQuery(string name) => new(HttpContextSource.Query, name); 18 | public static HttpContextFetchRequest FromRoute(string name) => new(HttpContextSource.Route, name); 19 | public static HttpContextFetchRequest FromJsonBody(string name) => new(HttpContextSource.JsonBody, name); 20 | } 21 | 22 | public interface IHttpContextFetcher 23 | { 24 | Task FromRoute(string name); 25 | Task FromJsonBody(string name); 26 | Task FromQuery(string name); 27 | } 28 | 29 | public class HttpContextFetcher : IHttpContextFetcher 30 | { 31 | private readonly IHttpContextAccessor _contextAccessor; 32 | 33 | public HttpContextFetcher(IHttpContextAccessor contextAccessor) 34 | { 35 | _contextAccessor = contextAccessor; 36 | } 37 | 38 | public Task FromRoute(string name) 39 | { 40 | var httpContext = _contextAccessor.HttpContext; 41 | return Task.FromResult(httpContext?.GetRouteValue(name) as string); 42 | } 43 | 44 | public async Task FromJsonBody(string name) 45 | { 46 | var httpContext = _contextAccessor.HttpContext; 47 | if (httpContext == null) return null; 48 | httpContext.Request.EnableBuffering(); 49 | var jsonDocument = await JsonDocument.ParseAsync(httpContext.Request.Body); 50 | httpContext.Request.Body.Position = 0; 51 | return jsonDocument.RootElement.TryGetProperty(name, out var jsonProp) ? jsonProp.GetString() : null; 52 | } 53 | 54 | public Task FromQuery(string name) 55 | { 56 | var httpContext = _contextAccessor.HttpContext; 57 | if (httpContext == null) return null; 58 | var value = httpContext.Request.Query.TryGetValue(name, out var queryValue) 59 | ? queryValue.ToString() 60 | : null; 61 | return Task.FromResult(value); 62 | } 63 | } -------------------------------------------------------------------------------- /Enbiso.NLib.HttpContextService/HttpContextService.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Http; 4 | 5 | namespace Enbiso.NLib.HttpContextService; 6 | 7 | public interface IHttpContextService 8 | { 9 | Task Fetch(HttpContextFetchRequest request); 10 | string ClaimValue(string name); 11 | string UserId(); 12 | bool UserInRole(string role); 13 | } 14 | 15 | public class HttpContextService: IHttpContextService 16 | { 17 | private readonly IHttpContextAccessor _contextAccessor; 18 | private readonly IHttpContextFetcher _fetcher; 19 | 20 | public HttpContextService(IHttpContextAccessor contextAccessor, IHttpContextFetcher fetcher) 21 | { 22 | _contextAccessor = contextAccessor; 23 | _fetcher = fetcher; 24 | } 25 | 26 | public Task Fetch(HttpContextFetchRequest request) 27 | { 28 | var (httpContextDataSource, name) = request; 29 | return httpContextDataSource switch 30 | { 31 | HttpContextSource.Query => _fetcher.FromQuery(name), 32 | HttpContextSource.Route => _fetcher.FromRoute(name), 33 | HttpContextSource.JsonBody => _fetcher.FromJsonBody(name), 34 | _ => null 35 | }; 36 | } 37 | 38 | public string ClaimValue(string name) 39 | { 40 | var user = _contextAccessor.HttpContext?.User; 41 | return user.FindFirstValue(name); 42 | } 43 | 44 | public string UserId() 45 | { 46 | return ClaimValue("sub") ?? ClaimValue(ClaimTypes.NameIdentifier); 47 | } 48 | 49 | public bool UserInRole(string role) 50 | { 51 | return _contextAccessor.HttpContext?.User.IsInRole(role) ?? false; 52 | } 53 | } -------------------------------------------------------------------------------- /Enbiso.NLib.HttpContextService/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace Enbiso.NLib.HttpContextService; 4 | 5 | public static class ServiceExtensions 6 | { 7 | public static void AddHttpContextService(this IServiceCollection services) 8 | { 9 | services.AddHttpContextAccessor(); 10 | services.AddSingleton(); 11 | services.AddSingleton(); 12 | } 13 | } -------------------------------------------------------------------------------- /Enbiso.NLib.Idempotency.EntityFramework/Enbiso.NLib.Idempotency.EntityFramework.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net7.0 4 | Enbiso.NLib.Idempotency.EntityFramework 5 | 6 | 0.0.1-local 7 | $(PACKAGE_VERSION) 8 | 9 | Faraj Farook 10 | Simple Idempotency with Request Persistence with EF 11 | false 12 | $(PACKAGE_COPYRIGHT) 13 | Enbiso Enbiso.NLib Idempotency RequestLog 14 | true 15 | en-AU 16 | enbiso 17 | Initial Release 18 | Enbiso - Simple Idempotency with Request Persistence with EF in .NET Standard 19 | Simple Idempotency with Request Persistence 20 | 21 | logo.png 22 | $(PACKAGE_PROJECT) 23 | $(PACKAGE_REPO) 24 | default 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Enbiso.NLib.Idempotency.EntityFramework/RequestLogEntityConfig.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | 4 | namespace Enbiso.NLib.Idempotency.EntityFramework 5 | { 6 | public class RequestLogEntityConfig : IEntityTypeConfiguration 7 | { 8 | public void Configure(EntityTypeBuilder requestConfiguration) 9 | { 10 | requestConfiguration.Property(cr => cr.Name).IsRequired(); 11 | requestConfiguration.Property(cr => cr.Time).IsRequired(); 12 | } 13 | } 14 | 15 | /// 16 | /// Model builder extensions 17 | /// 18 | public static class ModelBuilderExtensions 19 | { 20 | public static ModelBuilder ApplyIdempotencyConfiguration(this ModelBuilder builder) 21 | { 22 | return builder.ApplyConfiguration(new RequestLogEntityConfig()); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Enbiso.NLib.Idempotency.EntityFramework/RequestLogRepo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.EntityFrameworkCore; 4 | 5 | namespace Enbiso.NLib.Idempotency.EntityFramework 6 | { 7 | /// 8 | /// 9 | /// Repo implementation in EF 10 | /// 11 | /// 12 | public class RequestLogRepo: IRequestLogRepo where TDbContext: DbContext 13 | { 14 | private readonly DbContext _context; 15 | 16 | public RequestLogRepo(TDbContext context) 17 | { 18 | _context = context; 19 | } 20 | 21 | public async Task FindAsync(Guid id) 22 | { 23 | return await _context.Set().FirstOrDefaultAsync(rl => rl.Id == id); 24 | } 25 | 26 | public RequestLog Add(RequestLog requestLog) 27 | { 28 | return _context.Set().Add(requestLog).Entity; 29 | } 30 | 31 | public Task SaveChangesAsync() 32 | { 33 | return _context.SaveChangesAsync(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Enbiso.NLib.Idempotency.EntityFramework/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Enbiso.NLib.Idempotency.EntityFramework 5 | { 6 | public static class ServiceExtensions 7 | { 8 | public static void AddIdempotency(this IServiceCollection services) where TDbContext : DbContext 9 | { 10 | services.AddIdempotency(); 11 | services.AddTransient>(); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Enbiso.NLib.Idempotency/Enbiso.NLib.Idempotency.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.1 4 | Enbiso.NLib.Idempotency 5 | 6 | 0.0.1-local 7 | $(PACKAGE_VERSION) 8 | 9 | Faraj Farook 10 | Simple Idempotency with Client Request Persistence 11 | false 12 | $(PACKAGE_COPYRIGHT) 13 | Enbiso Enbiso.NLib Idempotency ClientRequest 14 | true 15 | en-AU 16 | enbiso 17 | Initial Release 18 | Enbiso - Simple Idempotency with Client Request Persistence in .NET Standard 19 | Simple Idempotency with Client Request Persistence 20 | 21 | logo.png 22 | $(PACKAGE_PROJECT) 23 | $(PACKAGE_REPO) 24 | default 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Enbiso.NLib.Idempotency/IRequestLogRepo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Enbiso.NLib.Idempotency 5 | { 6 | /// 7 | /// Request log repository 8 | /// 9 | public interface IRequestLogRepo 10 | { 11 | /// 12 | /// Find by ID 13 | /// 14 | /// 15 | /// 16 | Task FindAsync(Guid id); 17 | 18 | /// 19 | /// Add New 20 | /// 21 | /// 22 | /// 23 | RequestLog Add(RequestLog requestLog); 24 | 25 | /// 26 | /// Save changes 27 | /// 28 | /// 29 | Task SaveChangesAsync(); 30 | } 31 | } -------------------------------------------------------------------------------- /Enbiso.NLib.Idempotency/IRequestManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Enbiso.NLib.Idempotency 5 | { 6 | /// 7 | /// Request Manager Interface 8 | /// 9 | public interface IRequestManager 10 | { 11 | /// 12 | /// Find Async 13 | /// 14 | /// 15 | /// 16 | Task FindAsync(Guid id); 17 | 18 | /// 19 | /// Create request for command 20 | /// 21 | /// 22 | /// 23 | /// 24 | /// 25 | Task CreateRequestForAsync(Guid id, string response); 26 | 27 | /// 28 | /// Create Request Log by name 29 | /// 30 | /// 31 | /// 32 | /// 33 | /// 34 | Task CreateRequestAsync(Guid id, string name, string response); 35 | } 36 | } -------------------------------------------------------------------------------- /Enbiso.NLib.Idempotency/RequestLog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.Idempotency 4 | { 5 | /// 6 | /// Request Log 7 | /// 8 | public class RequestLog 9 | { 10 | public Guid Id { get; set; } 11 | public string Name { get; set; } 12 | public DateTime Time { get; set; } 13 | public string Response { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Enbiso.NLib.Idempotency/RequestManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Enbiso.NLib.Idempotency 5 | { 6 | /// 7 | /// 8 | /// Request manager implementation 9 | /// 10 | public class RequestManager : IRequestManager 11 | { 12 | private readonly IRequestLogRepo _repo; 13 | 14 | public RequestManager(IRequestLogRepo repo) 15 | { 16 | _repo = repo; 17 | } 18 | 19 | public Task FindAsync(Guid id) 20 | { 21 | return _repo.FindAsync(id); 22 | } 23 | 24 | public Task CreateRequestForAsync(Guid id, string response) 25 | { 26 | return CreateRequestAsync(id, typeof(T).Name, response); 27 | } 28 | 29 | public async Task CreateRequestAsync(Guid id, string name, string response) 30 | { 31 | var request = new RequestLog 32 | { 33 | Id = id, 34 | Name = name, 35 | Time = DateTime.UtcNow, 36 | Response = response 37 | }; 38 | _repo.Add(request); 39 | await _repo.SaveChangesAsync(); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /Enbiso.NLib.Idempotency/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace Enbiso.NLib.Idempotency 4 | { 5 | public static class ServiceExtensions 6 | { 7 | public static void AddIdempotency(this IServiceCollection services) 8 | { 9 | services.AddTransient(); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Enbiso.NLib.IdentityServer.Mongo/Enbiso.NLib.IdentityServer.Mongo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | Enbiso.NLib.IdentityServer.Mongo 7 | 8 | 0.0.1-local 9 | $(PACKAGE_VERSION) 10 | 11 | Faraj Farook 12 | IdentityServer4 MongoDB data provider 13 | false 14 | $(PACKAGE_COPYRIGHT) 15 | Enbiso Enbiso.NLib IdentityServer IdentityServer4 16 | true 17 | en-AU 18 | enbiso 19 | Initial Release 20 | Enbiso - IdentityServer4 MongoDB data provider 21 | IdentityServer4 MongoDB data provider 22 | 23 | logo.png 24 | $(PACKAGE_PROJECT) 25 | $(PACKAGE_REPO) 26 | default 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Enbiso.NLib.IdentityServer.Mongo/Events/IdentityServerMongoEvents.cs: -------------------------------------------------------------------------------- 1 | namespace Enbiso.NLib.IdentityServer.Mongo.Events; 2 | 3 | public interface IIdentityServerMongoEvent 4 | { 5 | } 6 | 7 | public interface IIdentityServerMongoEventHandler where TEvent: IIdentityServerMongoEvent 8 | { 9 | Task Handle(TEvent @event, CancellationToken cancellationToken); 10 | } -------------------------------------------------------------------------------- /Enbiso.NLib.IdentityServer.Mongo/Events/IdentityServerMongoUserEvents.cs: -------------------------------------------------------------------------------- 1 | namespace Enbiso.NLib.IdentityServer.Mongo.Events; 2 | 3 | public class IdentityServerMongoUserCreateEvent: IIdentityServerMongoEvent 4 | { 5 | public IdentityServerMongoUserCreateEvent(TUser user) 6 | { 7 | User = user; 8 | } 9 | 10 | public TUser User { get; } 11 | } 12 | 13 | public class IdentityServerMongoUserDeleteEvent: IIdentityServerMongoEvent 14 | { 15 | public IdentityServerMongoUserDeleteEvent(TUser user) 16 | { 17 | User = user; 18 | } 19 | 20 | public TUser User { get; } 21 | } 22 | 23 | public class IdentityServerMongoUserUpdateEvent: IIdentityServerMongoEvent 24 | { 25 | public IdentityServerMongoUserUpdateEvent(TUser user) 26 | { 27 | User = user; 28 | } 29 | 30 | public TUser User { get; } 31 | } -------------------------------------------------------------------------------- /Enbiso.NLib.IdentityServer.Mongo/Models/ApiResourceData.cs: -------------------------------------------------------------------------------- 1 | using Duende.IdentityServer.Models; 2 | using MongoDB.Bson.Serialization.Attributes; 3 | 4 | namespace Enbiso.NLib.IdentityServer.Mongo.Models 5 | { 6 | public class ApiResourceData 7 | { 8 | [BsonId] 9 | public string Id { get; set; } 10 | 11 | public ApiResource Resource { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /Enbiso.NLib.IdentityServer.Mongo/Models/ClientData.cs: -------------------------------------------------------------------------------- 1 | using Duende.IdentityServer.Models; 2 | using MongoDB.Bson.Serialization.Attributes; 3 | 4 | namespace Enbiso.NLib.IdentityServer.Mongo.Models 5 | { 6 | public class ClientData 7 | { 8 | [BsonId] 9 | public string Id { get; set; } 10 | public Client Client { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /Enbiso.NLib.IdentityServer.Mongo/Models/IMongoIdentityRole.cs: -------------------------------------------------------------------------------- 1 | namespace Enbiso.NLib.IdentityServer.Mongo.Models; 2 | 3 | public interface IMongoIdentityRole 4 | { 5 | public IList Claims { get; set; } 6 | } -------------------------------------------------------------------------------- /Enbiso.NLib.IdentityServer.Mongo/Models/IMongoIdentityUser.cs: -------------------------------------------------------------------------------- 1 | namespace Enbiso.NLib.IdentityServer.Mongo.Models; 2 | 3 | public interface IMongoIdentityUser 4 | { 5 | IList Roles { get; set; } 6 | IList Logins { get; set; } 7 | IList Claims { get; set; } 8 | string AuthenticatorKey { get; set; } 9 | IDictionary Tokens { get; set; } 10 | IList RecoveryCodes { get; set; } 11 | } -------------------------------------------------------------------------------- /Enbiso.NLib.IdentityServer.Mongo/Models/IdentityResourceData.cs: -------------------------------------------------------------------------------- 1 | using Duende.IdentityServer.Models; 2 | using MongoDB.Bson.Serialization.Attributes; 3 | 4 | namespace Enbiso.NLib.IdentityServer.Mongo.Models 5 | { 6 | public class IdentityResourceData 7 | { 8 | [BsonId] 9 | public string Id { get; set; } 10 | 11 | public IdentityResource Resource { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /Enbiso.NLib.IdentityServer.Mongo/Models/MongoClaim.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | using MongoDB.Bson.Serialization.Attributes; 3 | 4 | namespace Enbiso.NLib.IdentityServer.Mongo.Models; 5 | 6 | [BsonIgnoreExtraElements] 7 | public class MongoClaim 8 | { 9 | public MongoClaim() 10 | { 11 | 12 | } 13 | public MongoClaim(Claim claim) 14 | { 15 | Type = claim.Type; 16 | Value = claim.Value; 17 | ValueType = claim.ValueType; 18 | Issuer = claim.Issuer; 19 | OriginalIssuer = claim.OriginalIssuer; 20 | } 21 | 22 | public Claim ToClaim() => new(Type, Value, ValueType, Issuer, OriginalIssuer); 23 | public string Type { get; set; } 24 | public string OriginalIssuer { get; set; } 25 | public string Issuer { get; set; } 26 | public string ValueType { get; set; } 27 | public string Value { get; set; } 28 | 29 | public override bool Equals(object obj) 30 | { 31 | return Equals(obj as MongoClaim); 32 | } 33 | 34 | public override int GetHashCode() 35 | { 36 | return HashCode.Combine(Type, OriginalIssuer, Issuer, ValueType, Value); 37 | } 38 | 39 | private bool Equals ( MongoClaim obj ) 40 | { 41 | return obj != null && obj.Type == Type && obj.Value == Value; 42 | } 43 | } -------------------------------------------------------------------------------- /Enbiso.NLib.IdentityServer.Mongo/Models/MongoUserLoginInfo.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | using MongoDB.Bson.Serialization.Attributes; 3 | 4 | namespace Enbiso.NLib.IdentityServer.Mongo.Models; 5 | 6 | [BsonIgnoreExtraElements] 7 | public class MongoUserLoginInfo : UserLoginInfo 8 | { 9 | public MongoUserLoginInfo(string loginProvider, string providerKey, string displayName) : base(loginProvider, 10 | providerKey, displayName) 11 | { 12 | } 13 | } -------------------------------------------------------------------------------- /Enbiso.NLib.IdentityServer.Mongo/Models/PersistedGrantData.cs: -------------------------------------------------------------------------------- 1 | using Duende.IdentityServer.Models; 2 | using MongoDB.Bson.Serialization.Attributes; 3 | 4 | namespace Enbiso.NLib.IdentityServer.Mongo.Models 5 | { 6 | public class PersistedGrantData 7 | { 8 | [BsonId] 9 | public string Id { get; set; } 10 | 11 | public PersistedGrant PersistedGrant { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /Enbiso.NLib.IdentityServer.Mongo/Models/RecoveryCode.cs: -------------------------------------------------------------------------------- 1 | namespace Enbiso.NLib.IdentityServer.Mongo.Models; 2 | 3 | public class RecoveryCode 4 | { 5 | public string Code { get; set; } 6 | public bool Used { get; set; } 7 | } -------------------------------------------------------------------------------- /Enbiso.NLib.IdentityServer.Mongo/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using Duende.IdentityServer.Stores; 2 | using Enbiso.NLib.IdentityServer.Mongo.Models; 3 | using Enbiso.NLib.IdentityServer.Mongo.Stores; 4 | using Microsoft.AspNetCore.Identity; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using MongoDB.Driver; 7 | 8 | namespace Enbiso.NLib.IdentityServer.Mongo 9 | { 10 | public static class ServiceExtensions 11 | { 12 | public static IServiceCollection AddIdentityServerMongo(this IServiceCollection services, 13 | Action optSetup) 14 | where TRole : IdentityRole, IMongoIdentityRole 15 | where TUser : IdentityUser, IMongoIdentityUser 16 | { 17 | var options = new IdentityServerMongoOptions(); 18 | optSetup.Invoke(options); 19 | var mongo = new MongoClient(options.ConnectionString); 20 | var db = mongo.GetDatabase(options.Database); 21 | services.AddSingleton(sp => db.GetCollection(options.ClientCollection)); 22 | services.AddSingleton(sp => db.GetCollection(options.PersistedGrantCollection)); 23 | services.AddSingleton(sp => db.GetCollection(options.ApiResourceCollection)); 24 | services.AddSingleton(sp => db.GetCollection(options.IdentityResourceCollection)); 25 | services.AddSingleton(sp => db.GetCollection(options.RoleCollection)); 26 | services.AddSingleton(sp => db.GetCollection(options.UserCollection)); 27 | 28 | services.AddTransient(); 29 | services.AddTransient(); 30 | services.AddTransient(); 31 | services.AddTransient, MongoUserStore>(); 32 | services.AddTransient, MongoRoleStore>(); 33 | 34 | return services; 35 | } 36 | } 37 | 38 | public class IdentityServerMongoOptions 39 | { 40 | public string ConnectionString { get; set; } = "localhost"; 41 | public string Database { get; set; } = "identity"; 42 | public string ClientCollection { get; set; } = "clients"; 43 | public string PersistedGrantCollection { get; set; } = "persisted-grants"; 44 | public string ApiResourceCollection { get; set; } = "api-resources"; 45 | public string IdentityResourceCollection { get; set; } = "identity-resources"; 46 | public string RoleCollection { get; set; } = "roles"; 47 | public string UserCollection { get; set; } = "users"; 48 | } 49 | } -------------------------------------------------------------------------------- /Enbiso.NLib.IdentityServer.Mongo/Stores/MongoClientStore.cs: -------------------------------------------------------------------------------- 1 | using Duende.IdentityServer.Models; 2 | using Duende.IdentityServer.Stores; 3 | using Enbiso.NLib.IdentityServer.Mongo.Models; 4 | using MongoDB.Driver; 5 | 6 | namespace Enbiso.NLib.IdentityServer.Mongo.Stores 7 | { 8 | public class MongoClientStore: IClientStore 9 | { 10 | private readonly IMongoCollection _clients; 11 | 12 | public MongoClientStore(IMongoCollection clients) 13 | { 14 | _clients = clients; 15 | } 16 | 17 | public async Task FindClientByIdAsync(string clientId) 18 | { 19 | var wrapper = await _clients.Find(c => c.Id == clientId).FirstOrDefaultAsync(); 20 | return wrapper?.Client; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Enbiso.NLib.IdentityServer.Mongo/Stores/MongoPersistedGrantStore.cs: -------------------------------------------------------------------------------- 1 | using Duende.IdentityServer.Models; 2 | using Duende.IdentityServer.Stores; 3 | using Enbiso.NLib.IdentityServer.Mongo.Models; 4 | using MongoDB.Driver; 5 | 6 | namespace Enbiso.NLib.IdentityServer.Mongo.Stores 7 | { 8 | public class MongoPersistedGrantStore: IPersistedGrantStore 9 | { 10 | 11 | private readonly IMongoCollection _persistedGrants; 12 | 13 | public MongoPersistedGrantStore(IMongoCollection persistedGrants) 14 | { 15 | _persistedGrants = persistedGrants; 16 | } 17 | 18 | public async Task StoreAsync(PersistedGrant grant) 19 | { 20 | if(await _persistedGrants.Find(g => g.Id == grant.Key).AnyAsync()) 21 | await RemoveAsync(grant.Key); 22 | 23 | await _persistedGrants.InsertOneAsync(new PersistedGrantData 24 | { 25 | Id = grant.Key, 26 | PersistedGrant = grant 27 | }); 28 | } 29 | 30 | 31 | public Task GetAsync(string key) => _persistedGrants.Find(p => p.Id == key) 32 | .Project(p => p.PersistedGrant).FirstOrDefaultAsync(); 33 | 34 | public Task> GetAllAsync(PersistedGrantFilter filter) 35 | { 36 | var results = _persistedGrants 37 | .Find(p => 38 | p.PersistedGrant.Type == filter.Type && 39 | p.PersistedGrant.SessionId == filter.SessionId && 40 | p.PersistedGrant.SubjectId == filter.SubjectId && 41 | p.PersistedGrant.ClientId == filter.ClientId).Project(p => p.PersistedGrant).ToEnumerable(); 42 | return Task.FromResult(results); 43 | } 44 | 45 | public Task RemoveAsync(string key) => _persistedGrants.DeleteOneAsync(w => w.Id == key); 46 | public Task RemoveAllAsync(PersistedGrantFilter filter) 47 | { 48 | return _persistedGrants.DeleteManyAsync(p => 49 | p.PersistedGrant.Type == filter.Type && 50 | p.PersistedGrant.SessionId == filter.SessionId && 51 | p.PersistedGrant.SubjectId == filter.SubjectId && 52 | p.PersistedGrant.ClientId == filter.ClientId); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /Enbiso.NLib.IdentityServer.Mongo/Stores/MongoResourceStore.cs: -------------------------------------------------------------------------------- 1 | using Duende.IdentityServer.Models; 2 | using Duende.IdentityServer.Stores; 3 | using Enbiso.NLib.IdentityServer.Mongo.Models; 4 | using MongoDB.Driver; 5 | 6 | namespace Enbiso.NLib.IdentityServer.Mongo.Stores 7 | { 8 | public class MongoResourceStore : IResourceStore 9 | { 10 | private readonly IMongoCollection _apiResources; 11 | private readonly IMongoCollection _identityResources; 12 | 13 | public MongoResourceStore(IMongoCollection identityResources, 14 | IMongoCollection apiResources) 15 | { 16 | _identityResources = identityResources; 17 | _apiResources = apiResources; 18 | } 19 | 20 | public async Task> FindIdentityResourcesByScopeNameAsync(IEnumerable scopeNames) 21 | { 22 | var wrappers = await _identityResources 23 | .Find(Builders.Filter.In(w => w.Id, scopeNames)).ToListAsync(); 24 | return wrappers.Select(w => w.Resource); 25 | } 26 | 27 | public async Task> FindApiResourcesByScopeNameAsync(IEnumerable scopeNames) 28 | { 29 | return await _apiResources.Find(Builders.Filter.AnyIn(a => a.Resource.Scopes, scopeNames)) 30 | .Project(w => w.Resource) 31 | .ToListAsync(); 32 | } 33 | 34 | public async Task> FindApiScopesByNameAsync(IEnumerable scopeNames) 35 | { 36 | var resources = await FindApiResourcesByScopeNameAsync(scopeNames); 37 | resources ??= new List(); 38 | return resources.SelectMany(r => r.Scopes) 39 | .Distinct() 40 | .Select(s => new ApiScope(s)); 41 | } 42 | 43 | public async Task> FindApiResourcesByNameAsync(IEnumerable apiResourceNames) 44 | { 45 | var resources = await _apiResources 46 | .Find(Builders.Filter.In(a => a.Resource.Name, apiResourceNames)) 47 | .Project(w => w.Resource) 48 | .ToListAsync(); 49 | return resources; 50 | } 51 | 52 | public async Task GetAllResourcesAsync() 53 | { 54 | var apiResTask = _apiResources.Find(_ => true).Project(a => a.Resource).ToListAsync(); 55 | var idResTask = _identityResources.Find(_ => true).Project(a => a.Resource).ToListAsync(); 56 | await Task.WhenAll(apiResTask, idResTask); 57 | var apiResources = await apiResTask; 58 | var idResources = await idResTask; 59 | var apiScopes = apiResources.SelectMany(r => r.Scopes) 60 | .Distinct() 61 | .Where(s => !string.IsNullOrEmpty(s)) 62 | .Select(s => new ApiScope(s)); 63 | return new Resources(idResources, apiResources, apiScopes); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Enbiso.NLib.IdentityServer.Mongo/Stores/MongoRoleStore.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | using Enbiso.NLib.IdentityServer.Mongo.Models; 3 | using Microsoft.AspNetCore.Identity; 4 | using MongoDB.Driver; 5 | 6 | namespace Enbiso.NLib.IdentityServer.Mongo.Stores 7 | { 8 | public class MongoRoleStore : IQueryableRoleStore, IRoleClaimStore where TRole : IdentityRole, IMongoIdentityRole 9 | { 10 | private readonly IMongoCollection _roles; 11 | 12 | public MongoRoleStore(IMongoCollection roles) 13 | { 14 | _roles = roles; 15 | } 16 | 17 | public void Dispose() 18 | { 19 | } 20 | 21 | public async Task CreateAsync(TRole role, CancellationToken cancellationToken) 22 | { 23 | try 24 | { 25 | await _roles.InsertOneAsync(role, cancellationToken: cancellationToken); 26 | return IdentityResult.Success; 27 | } 28 | catch (Exception e) 29 | { 30 | return IdentityResult.Failed(new IdentityError 31 | { 32 | Description = e.Message, 33 | }); 34 | } 35 | } 36 | 37 | public async Task UpdateAsync(TRole role, CancellationToken cancellationToken) 38 | { 39 | try 40 | { 41 | await _roles.ReplaceOneAsync(r => r.Id == role.Id, role, cancellationToken: cancellationToken); 42 | return IdentityResult.Success; 43 | } 44 | catch (Exception e) 45 | { 46 | return IdentityResult.Failed(new IdentityError 47 | { 48 | Description = e.Message, 49 | }); 50 | } 51 | } 52 | 53 | public async Task DeleteAsync(TRole role, CancellationToken cancellationToken) 54 | { 55 | try 56 | { 57 | await _roles.DeleteOneAsync(r => r.Id == role.Id, cancellationToken: cancellationToken); 58 | return IdentityResult.Success; 59 | } 60 | catch (Exception e) 61 | { 62 | return IdentityResult.Failed(new IdentityError 63 | { 64 | Description = e.Message, 65 | }); 66 | } 67 | } 68 | 69 | public Task GetRoleIdAsync(TRole role, CancellationToken cancellationToken) => 70 | Task.FromResult(role.Id); 71 | 72 | public Task GetRoleNameAsync(TRole role, CancellationToken cancellationToken) => 73 | Task.FromResult(role.Name); 74 | 75 | public Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken) 76 | { 77 | role.Name = roleName; 78 | return Task.CompletedTask; 79 | } 80 | 81 | public Task GetNormalizedRoleNameAsync(TRole role, CancellationToken cancellationToken) => 82 | Task.FromResult(role.NormalizedName); 83 | 84 | public Task SetNormalizedRoleNameAsync(TRole role, string normalizedName, CancellationToken cancellationToken) 85 | { 86 | role.NormalizedName = normalizedName; 87 | return Task.CompletedTask; 88 | } 89 | 90 | public Task FindByIdAsync(string roleId, CancellationToken token) => 91 | _roles.Find(r => r.Id == roleId).FirstOrDefaultAsync(token); 92 | 93 | public Task FindByNameAsync(string normalizedRoleName, CancellationToken token) => 94 | _roles.Find(r => r.NormalizedName == normalizedRoleName).FirstOrDefaultAsync(token); 95 | 96 | public IQueryable Roles => _roles.AsQueryable(); 97 | 98 | public Task> GetClaimsAsync(TRole role, 99 | CancellationToken cancellationToken = new CancellationToken()) 100 | { 101 | IList claims = role.Claims.Select(c => c.ToClaim()).ToList(); 102 | return Task.FromResult(claims); 103 | } 104 | 105 | public Task AddClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken) 106 | { 107 | var mongoClaim = new MongoClaim(claim); 108 | role.Claims.Add(mongoClaim); 109 | return _roles.UpdateOneAsync(r => r.Id == role.Id, Builders.Update.Push(r => r.Claims, mongoClaim), 110 | cancellationToken: cancellationToken); 111 | } 112 | 113 | public Task RemoveClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken) 114 | { 115 | var mongoClaim = role.Claims.FirstOrDefault(c => c.Type == claim.Type && c.Value == claim.Value); 116 | role.Claims.Remove(mongoClaim); 117 | return _roles.UpdateOneAsync(r => r.Id == role.Id, Builders.Update.Pull(r => r.Claims, mongoClaim), 118 | cancellationToken: cancellationToken); 119 | } 120 | } 121 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Actions/ExtActionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.OpenApi.Any; 3 | 4 | namespace Enbiso.NLib.OpenApi.Attributes.Actions 5 | { 6 | [AttributeUsage(AttributeTargets.Class)] 7 | public class ExtActionAttribute : ExtAttribute 8 | { 9 | 10 | public ExtActionAttribute(string type, string display, string link) : base("x-action", 11 | new OpenApiObject 12 | { 13 | ["type"] = new OpenApiString(type), 14 | ["display"] = new OpenApiString(display), 15 | ["link"] = new OpenApiString(link), 16 | }) 17 | { 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Actions/ExtActionCreateAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.OpenApi.Attributes.Actions 4 | { 5 | [AttributeUsage(AttributeTargets.Class)] 6 | public class ExtActionCreateAttribute : ExtActionAttribute 7 | { 8 | public ExtActionCreateAttribute() : base("create", "Create", "create") 9 | { 10 | } 11 | 12 | public ExtActionCreateAttribute(string display) : base("create", display, "create") 13 | { 14 | } 15 | 16 | public ExtActionCreateAttribute(string display, string link) : base("create", display, link) 17 | { 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Actions/ExtActionDeleteAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.OpenApi.Attributes.Actions 4 | { 5 | [AttributeUsage(AttributeTargets.Class)] 6 | public class ExtActionDeleteAttribute : ExtActionAttribute 7 | { 8 | public ExtActionDeleteAttribute() : base("delete", "Delete", "{key}/delete") 9 | { 10 | } 11 | 12 | public ExtActionDeleteAttribute(string display) : base("delete", display, "{key}/delete") 13 | { 14 | } 15 | 16 | public ExtActionDeleteAttribute(string display, string link) : base("delete", display, link) 17 | { 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Actions/ExtActionDetailsAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.OpenApi.Attributes.Actions 4 | { 5 | [AttributeUsage(AttributeTargets.Class)] 6 | public class ExtActionDetailsAttribute : ExtActionAttribute 7 | { 8 | public ExtActionDetailsAttribute() : base("details", "Details", "{key}") 9 | { 10 | } 11 | 12 | public ExtActionDetailsAttribute(string display) : base("details", display, "{key}") 13 | { 14 | } 15 | 16 | public ExtActionDetailsAttribute(string display, string link) : base("details", display, link) 17 | { 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Actions/ExtActionUpdateAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.OpenApi.Attributes.Actions 4 | { 5 | [AttributeUsage(AttributeTargets.Class)] 6 | public class ExtActionUpdateAttribute : ExtActionAttribute 7 | { 8 | public ExtActionUpdateAttribute() : base("update", "Update", "{key}/update") 9 | { 10 | } 11 | 12 | public ExtActionUpdateAttribute(string display) : base("update", display, "{key}/update") 13 | { 14 | } 15 | 16 | public ExtActionUpdateAttribute(string display, string link) : base("update", display, link) 17 | { 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/ExtAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.OpenApi.Any; 3 | 4 | namespace Enbiso.NLib.OpenApi.Attributes 5 | { 6 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Field | 7 | AttributeTargets.Enum)] 8 | public class ExtAttribute : Attribute 9 | { 10 | public readonly string Name; 11 | public readonly IOpenApiAny Value; 12 | 13 | public ExtAttribute(string name, IOpenApiAny value) 14 | { 15 | Name = name.StartsWith("x-") ? name : "x-" + name; 16 | Value = value; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/ExtHiddenAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.OpenApi.Any; 3 | 4 | namespace Enbiso.NLib.OpenApi.Attributes 5 | { 6 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 7 | public class ExtHiddenAttribute : ExtAttribute 8 | { 9 | public ExtHiddenAttribute() : base("x-hidden", new OpenApiBoolean(true)) 10 | { 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/ExtKeyAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.OpenApi.Any; 3 | 4 | namespace Enbiso.NLib.OpenApi.Attributes 5 | { 6 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 7 | public class ExtKeyAttribute : ExtAttribute 8 | { 9 | public ExtKeyAttribute() : base("x-key", new OpenApiBoolean(true)) 10 | { 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Inputs/ExtInputAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.OpenApi.Any; 3 | 4 | namespace Enbiso.NLib.OpenApi.Attributes.Inputs 5 | { 6 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 7 | public class ExtInputAttribute : ExtAttribute 8 | { 9 | public ExtInputAttribute(string type) : base("x-input", new OpenApiString(type)) 10 | { 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Inputs/ExtInputColorAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.OpenApi.Attributes.Inputs 4 | { 5 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 6 | public class ExtInputColorAttribute : ExtInputAttribute 7 | { 8 | public ExtInputColorAttribute() : base("color") 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Inputs/ExtInputDateAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.OpenApi.Attributes.Inputs 4 | { 5 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 6 | public class ExtInputDateAttribute : ExtInputAttribute 7 | { 8 | public ExtInputDateAttribute() : base("date") 9 | { 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Inputs/ExtInputDateTimeLocalAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.OpenApi.Attributes.Inputs 4 | { 5 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 6 | public class ExtInputDateTimeLocalAttribute : ExtInputAttribute 7 | { 8 | public ExtInputDateTimeLocalAttribute() : base("datetime-local") 9 | { 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Inputs/ExtInputEmailAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.OpenApi.Attributes.Inputs 4 | { 5 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 6 | public class ExtInputEmailAttribute : ExtInputAttribute 7 | { 8 | public ExtInputEmailAttribute() : base("email") 9 | { 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Inputs/ExtInputMonthAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.OpenApi.Attributes.Inputs 4 | { 5 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 6 | public class ExtInputMonthAttribute : ExtInputAttribute 7 | { 8 | public ExtInputMonthAttribute() : base("month") 9 | { 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Inputs/ExtInputNumberAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.OpenApi.Attributes.Inputs 4 | { 5 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 6 | public class ExtInputNumberAttribute : ExtInputAttribute 7 | { 8 | public ExtInputNumberAttribute() : base("number") 9 | { 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Inputs/ExtInputPasswordAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.OpenApi.Attributes.Inputs 4 | { 5 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 6 | public class ExtInputPasswordAttribute : ExtInputAttribute 7 | { 8 | public ExtInputPasswordAttribute() : base("password") 9 | { 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Inputs/ExtInputSearchAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.OpenApi.Attributes.Inputs 4 | { 5 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 6 | public class ExtInputSearchAttribute : ExtInputAttribute 7 | { 8 | public ExtInputSearchAttribute() : base("search") 9 | { 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Inputs/ExtInputTelAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.OpenApi.Attributes.Inputs 4 | { 5 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 6 | public class ExtInputTelAttribute : ExtInputAttribute 7 | { 8 | public ExtInputTelAttribute() : base("tel") 9 | { 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Inputs/ExtInputTextAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.OpenApi.Attributes.Inputs 4 | { 5 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 6 | public class ExtInputTextAttribute : ExtInputAttribute 7 | { 8 | public ExtInputTextAttribute() : base("text") 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Inputs/ExtInputTimeAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.OpenApi.Attributes.Inputs 4 | { 5 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 6 | public class ExtInputTimeAttribute : ExtInputAttribute 7 | { 8 | public ExtInputTimeAttribute() : base("time") 9 | { 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Inputs/ExtInputUrlAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.OpenApi.Attributes.Inputs 4 | { 5 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 6 | public class ExtInputUrlAttribute : ExtInputAttribute 7 | { 8 | public ExtInputUrlAttribute() : base("url") 9 | { 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Attributes/Inputs/ExtInputWeekAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Enbiso.NLib.OpenApi.Attributes.Inputs 4 | { 5 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 6 | public class ExtInputWeekAttribute : ExtInputAttribute 7 | { 8 | public ExtInputWeekAttribute() : base("week") 9 | { 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/Enbiso.NLib.OpenApi.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | Enbiso.NLib.OpenApi 6 | 7 | 0.0.1-local 8 | $(PACKAGE_VERSION) 9 | 10 | Faraj Farook 11 | Simple OpenApi extensions as attributes 12 | false 13 | $(PACKAGE_COPYRIGHT) 14 | Enbiso Enbiso.NLib OpenApi Attributes 15 | true 16 | en-AU 17 | enbiso 18 | Initial Release 19 | Enbiso - Simple OpenApi extensions as attributes in .NET Standard 20 | Simple OpenApi extensions as attributes 21 | 22 | logo.png 23 | $(PACKAGE_PROJECT) 24 | $(PACKAGE_REPO) 25 | default 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/SchemaExtensionFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Enbiso.NLib.OpenApi.Attributes; 4 | using Enbiso.NLib.OpenApi.Attributes.Actions; 5 | using Microsoft.OpenApi.Any; 6 | using Microsoft.OpenApi.Interfaces; 7 | using Microsoft.OpenApi.Models; 8 | using Swashbuckle.AspNetCore.SwaggerGen; 9 | 10 | namespace Enbiso.NLib.OpenApi 11 | { 12 | public class SchemaExtensionFilter : ISchemaFilter 13 | { 14 | public void Apply(OpenApiSchema schema, SchemaFilterContext context) 15 | { 16 | var type = context.Type; 17 | var attributes = type.GetCustomAttributes(false).OfType(); 18 | 19 | foreach (var attribute in attributes) 20 | schema.Extensions.Add(attribute); 21 | 22 | var properties = type.GetProperties(); 23 | foreach (var property in properties) 24 | { 25 | var propAttributes = property.GetCustomAttributes(false).OfType(); 26 | foreach (var attribute in propAttributes) 27 | { 28 | var key = char.ToLowerInvariant(property.Name[0]) + property.Name.Substring(1); 29 | schema.Properties[key]?.Extensions.Add(attribute); 30 | } 31 | } 32 | } 33 | } 34 | internal static class DictionaryExtensions 35 | { 36 | internal static void Add(this IDictionary dictionary, ExtAttribute attribute) 37 | { 38 | var value = attribute.Value; 39 | if (attribute is ExtActionAttribute) 40 | { 41 | if (dictionary.ContainsKey(attribute.Name)) 42 | { 43 | (dictionary[attribute.Name] as OpenApiArray)?.Add(attribute.Value); 44 | return; 45 | } 46 | value = new OpenApiArray { attribute.Value }; 47 | } 48 | dictionary.Add(attribute.Name, value); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /Enbiso.NLib.OpenApi/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Options; 8 | using Microsoft.OpenApi.Models; 9 | using Swashbuckle.AspNetCore.SwaggerGen; 10 | 11 | namespace Enbiso.NLib.OpenApi 12 | { 13 | public static class ServiceExtensions 14 | { 15 | /// 16 | /// Add open Api with builder 17 | /// 18 | /// 19 | /// 20 | public static void AddOpenApi(this IServiceCollection services, Action optBuilder) 21 | { 22 | services.AddOptions(); 23 | 24 | optBuilder ??= _ => {}; 25 | services.Configure(optBuilder); 26 | 27 | var opts = new OpenApiOptions(); 28 | optBuilder.Invoke(opts); 29 | 30 | services.AddSwaggerGen(c => 31 | { 32 | c.SchemaFilter(); 33 | c.SwaggerDoc("swagger", new OpenApiInfo 34 | { 35 | Title = opts.Id?.ToUpper(), 36 | Version = opts.Version, 37 | Description = opts.Description 38 | }); 39 | if (string.IsNullOrEmpty(opts.Authority)) return; 40 | 41 | var scopes = opts.ExtraScopes?.ToDictionary(s => s, s => s.ToUpper()) ?? 42 | new Dictionary(); 43 | 44 | if (opts.Id != null) scopes.Add(opts.Id, opts.Description); 45 | 46 | c.AddSecurityDefinition("OAuth2", new OpenApiSecurityScheme 47 | { 48 | Type = SecuritySchemeType.OAuth2, 49 | Flows = new OpenApiOAuthFlows 50 | { 51 | Implicit = new OpenApiOAuthFlow 52 | { 53 | AuthorizationUrl = new Uri($"{opts.Authority}/connect/authorize"), 54 | TokenUrl = new Uri($"{opts.Authority}/connect/token"), 55 | Scopes = scopes 56 | } 57 | }, 58 | }); 59 | 60 | c.AddSecurityRequirement(new OpenApiSecurityRequirement 61 | { 62 | { 63 | new OpenApiSecurityScheme 64 | { 65 | Reference = new OpenApiReference 66 | { 67 | Type = ReferenceType.SecurityScheme, 68 | Id = "OAuth2" 69 | }, 70 | Scheme = "oauth2", 71 | Name = "Bearer", 72 | In = ParameterLocation.Header, 73 | }, 74 | scopes.Keys.ToList() 75 | } 76 | }); 77 | }); 78 | } 79 | 80 | public static void UseOpenApi(this IApplicationBuilder app) 81 | { 82 | var settings = app.ApplicationServices.GetRequiredService>().Value; 83 | app.UseSwagger(c => 84 | { 85 | c.RouteTemplate = "/{documentName}.json"; 86 | c.PreSerializeFilters.Add(((document, request) => 87 | { 88 | document.Servers = new List 89 | { 90 | new OpenApiServer 91 | { 92 | Url = settings.BasePath 93 | } 94 | }; 95 | })); 96 | }); 97 | app.UseSwaggerUI(c => { c.SwaggerEndpoint($"{settings.BasePath}swagger.json", settings.Id.ToUpper());}); 98 | } 99 | } 100 | 101 | public class OpenApiOptions 102 | { 103 | /// 104 | /// Api ID 105 | /// 106 | public string Id { get; set; } 107 | /// 108 | /// API Version 109 | /// 110 | public string Version { get; set; } 111 | /// 112 | /// API description 113 | /// 114 | public string Description { get; set; } 115 | /// 116 | /// Swagger authority 117 | /// 118 | public string Authority { get; set; } 119 | /// 120 | /// Api base path 121 | /// 122 | public string BasePath { get; set; } 123 | /// 124 | /// Extra scopes to add to Swagger UI 125 | /// 126 | public string[] ExtraScopes { get; set; } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Enbiso.NLib.RestClient/Enbiso.NLib.RestClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | Enbiso.NLib.RestClient 6 | 7 | 0.0.1-local 8 | $(PACKAGE_VERSION) 9 | 10 | Faraj Farook 11 | Simple REST Client with OpenID Connect Client Credential Security 12 | false 13 | $(PACKAGE_COPYRIGHT) 14 | Enbiso Enbiso.NLib RestClient 15 | true 16 | en-AU 17 | enbiso 18 | Initial Release 19 | Enbiso - Simple REST Client with OpenID Connect Client Credential Security 20 | Simple REST Client 21 | 22 | logo.png 23 | $(PACKAGE_PROJECT) 24 | $(PACKAGE_REPO) 25 | default 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Enbiso.NLib.RestClient/Exceptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace Enbiso.NLib.RestClient 5 | { 6 | public class RestRequestFailedException: Exception 7 | { 8 | public HttpStatusCode StatusCode { get; } 9 | 10 | public RestRequestFailedException(HttpStatusCode statusCode, string message) : base(message) 11 | { 12 | StatusCode = statusCode; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Enbiso.NLib.RestClient/RestClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Net.Http; 5 | using System.Net.Http.Headers; 6 | using System.Text; 7 | using System.Text.Json; 8 | using System.Text.Json.Serialization; 9 | using System.Threading.Tasks; 10 | using Microsoft.Extensions.Options; 11 | 12 | namespace Enbiso.NLib.RestClient 13 | { 14 | public interface IRestClient 15 | { 16 | Task Post(string service, string path, object payload); 17 | Task Get(string service, string path); 18 | Task Put(string service, string path, object payload); 19 | Task Delete(string service, string path); 20 | Task Post(string service, string path, object payload); 21 | Task Get(string service, string path); 22 | Task Put(string service, string path, object payload); 23 | Task Delete(string service, string path); 24 | } 25 | 26 | public class RestClient: IRestClient 27 | { 28 | private readonly HttpClient _client; 29 | private readonly RestClientOptions _options; 30 | 31 | public RestClient(IHttpClientFactory clientFactory, IOptions options) 32 | { 33 | _options = options.Value; 34 | if (_options.IgnoreInvalidSSL) 35 | { 36 | var handler = new HttpClientHandler(); 37 | handler.ClientCertificateOptions = ClientCertificateOption.Manual; 38 | handler.ServerCertificateCustomValidationCallback = (_, _, _, _) => true; 39 | _client = new HttpClient(handler); 40 | } 41 | else 42 | { 43 | _client = clientFactory.CreateClient(); 44 | } 45 | } 46 | 47 | public Task Post(string service, string path, object payload) 48 | => _retryPerform(service, path, 49 | endpoint => _client.PostAsync(endpoint, 50 | new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"))); 51 | 52 | public Task Put(string service, string path, object payload) 53 | => _retryPerform(service, path, 54 | endpoint => _client.PutAsync(endpoint, 55 | new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"))); 56 | 57 | public Task Get(string service, string path) 58 | => _retryPerform(service, path, endpoint => _client.GetAsync(endpoint)); 59 | 60 | public Task Delete(string service, string path) 61 | => _retryPerform(service, path, endpoint => _client.DeleteAsync(endpoint)); 62 | 63 | public async Task Post(string service, string path, object payload) => 64 | await ParseResponse(await Post(service, path, payload)); 65 | 66 | public async Task Get(string service, string path) => await ParseResponse(await Get(service, path)); 67 | 68 | public async Task Put(string service, string path, object payload) => 69 | await ParseResponse(await Put(service, path, payload)); 70 | 71 | public async Task Delete(string service, string path) => 72 | await ParseResponse(await Delete(service, path)); 73 | 74 | private async Task UpdateToken() 75 | { 76 | if (string.IsNullOrEmpty(_options.TokenEndpoint)) return; 77 | var resp = await _client.PostAsync(_options.TokenEndpoint, new FormUrlEncodedContent( 78 | new Dictionary 79 | { 80 | {"client_id", _options.ClientId}, 81 | {"client_secret", _options.ClientSecret}, 82 | {"grant_type", _options.GrantType}, 83 | {"scope", _options.Scope}, 84 | })); 85 | 86 | if (!resp.IsSuccessStatusCode) 87 | throw new Exception("Unable to fetch user information from Identity Service"); 88 | var content = await resp.Content.ReadAsStringAsync(); 89 | var token = JsonSerializer.Deserialize(content); 90 | _client.DefaultRequestHeaders.Authorization = 91 | new AuthenticationHeaderValue(token.TokenType, token.AccessToken); 92 | } 93 | 94 | private async Task _retryPerform(string service, string path, Func> perform) 95 | { 96 | if (string.IsNullOrWhiteSpace(_client.DefaultRequestHeaders.Authorization?.Parameter)) 97 | await UpdateToken(); 98 | 99 | if(!_options.Services.ContainsKey(service)) 100 | throw new Exception($"Service {service} not found"); 101 | 102 | var endpoint = $"{_options.Services[service]}{path}"; 103 | 104 | var resp = await perform.Invoke(endpoint); 105 | if (resp.StatusCode != HttpStatusCode.Unauthorized) return resp; 106 | 107 | await UpdateToken(); 108 | resp = await perform.Invoke(endpoint); 109 | return resp; 110 | } 111 | 112 | private static async Task ParseResponse(HttpResponseMessage resp) 113 | { 114 | var content = await resp.Content.ReadAsStringAsync(); 115 | if (!resp.IsSuccessStatusCode) 116 | throw new RestRequestFailedException(resp.StatusCode, content); 117 | return JsonSerializer.Deserialize(content); 118 | } 119 | 120 | private class Token 121 | { 122 | [JsonPropertyName("access_token")] 123 | public string AccessToken { get; set; } 124 | [JsonPropertyName("expires_in")] 125 | public int ExpireIn { get; set; } 126 | [JsonPropertyName("token_type")] 127 | public string TokenType { get; set; } 128 | } 129 | } 130 | } -------------------------------------------------------------------------------- /Enbiso.NLib.RestClient/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace Enbiso.NLib.RestClient 7 | { 8 | public static class ServiceExtensions 9 | { 10 | public static IServiceCollection AddRestClient(this IServiceCollection services, Action optSetup) 11 | { 12 | services.AddOptions(); 13 | services.Configure(optSetup); 14 | services.AddSingleton(); 15 | return services; 16 | } 17 | } 18 | 19 | public class RestClientOptions 20 | { 21 | public string TokenEndpoint { get; set; } 22 | public string ClientId { get; set; } 23 | public string ClientSecret { get; set; } 24 | public string Scope { get; set; } 25 | public IDictionary Services { get; set; } 26 | public string GrantType { get; set; } = "client_credentials"; 27 | public bool IgnoreInvalidSSL { get; set; } = false; 28 | } 29 | } -------------------------------------------------------------------------------- /Enbiso.NLib.Tests/Enbiso.NLib.Domain.Tests/Enbiso.NLib.Domain.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Enbiso.NLib.Tests/Enbiso.NLib.Domain.Tests/EntityTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using AutoFixture; 5 | using AutoFixture.AutoNSubstitute; 6 | using Xunit; 7 | 8 | namespace Enbiso.NLib.Domain.Tests 9 | { 10 | public class EntityTests 11 | { 12 | private readonly IFixture _fixture = new Fixture().Customize(new AutoNSubstituteCustomization()); 13 | 14 | [Fact] 15 | public void IsTransient_NoNull_False() 16 | { 17 | var entity = _fixture.Create>(); 18 | entity.Id = Guid.NewGuid(); 19 | Assert.False(entity.IsTransient()); 20 | } 21 | 22 | [Fact] 23 | public void IsTransient_Empty_True() 24 | { 25 | var entity = _fixture.Create>(); 26 | Assert.True(entity.IsTransient()); 27 | } 28 | 29 | [Theory] 30 | [InlineData(0, false)] 31 | [InlineData(3, true)] 32 | public void Equals_NotNull_Result(int id, bool result) 33 | { 34 | var e1 = _fixture.Create>(); 35 | var e2 = _fixture.Create>(); 36 | e1.Id = id; 37 | e2.Id = id; 38 | Assert.True(e1 == e2 == result); 39 | } 40 | 41 | [Fact] 42 | public void AddDomainEvent_ListOfIDomainEvent_Success() 43 | { 44 | var entity = _fixture.Create>(); 45 | var events = _fixture.Create>(); 46 | entity.GetEvents().Clear(); 47 | 48 | events.ForEach(entity.AddEvent); 49 | Assert.Equal(entity.GetEvents().Count, events.Count); 50 | } 51 | 52 | [Fact] 53 | public void RemoveDomainEvent_ValidEvent_Success() 54 | { 55 | var entity = _fixture.Create>(); 56 | var events = _fixture.Create>(); 57 | 58 | events.ForEach(entity.AddEvent); 59 | entity.RemoveEvent(events.First()); 60 | Assert.Equal(entity.GetEvents().Count, events.Count - 1); 61 | } 62 | 63 | 64 | [Fact] 65 | public void RemoveDomainEvent_Null_Success() 66 | { 67 | var events = _fixture.Create>(); 68 | var entity = _fixture.Create>(); 69 | events.ForEach(entity.AddEvent); 70 | Assert.Throws(() => entity.RemoveEvent(null)); 71 | } 72 | 73 | [Fact] 74 | public void AddDomainEvent_Nulls_Success() 75 | { 76 | var entity = _fixture.Create>(); 77 | Assert.Throws(() => entity.AddEvent(null)); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Enbiso.NLib.Tests/Enbiso.NLib.OpenApi.Tests/Enbiso.NLib.OpenApi.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Enbiso.NLib.Tests/Enbiso.NLib.OpenApi.Tests/ServiceExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | using AutoFixture.AutoNSubstitute; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using NSubstitute; 5 | using Xunit; 6 | 7 | namespace Enbiso.NLib.OpenApi.Tests 8 | { 9 | public class ServiceExtensionsTests 10 | { 11 | private readonly IFixture _fixture = new Fixture().Customize(new AutoNSubstituteCustomization()); 12 | 13 | [Fact] 14 | public void FetchingConfigurationTest() 15 | { 16 | var services = _fixture.Create(); 17 | services.AddOpenApi(o => o.Id = "Test"); 18 | services.ReceivedWithAnyArgs().Configure(opt => {}); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Enbiso NLib 2 | 3 | .Net Standard Libraries to build simple microservices 4 | 5 | ![Nugets CI/CD](https://github.com/enbiso/Enbiso.NLib/workflows/Nugets%20CI/CD/badge.svg) 6 | 7 | ## Domain 8 | 9 | Simple domain seedwork libraries to use in DDD designs 10 | 11 | - Enbiso.NLib.Domain 12 | - Enbiso.NLib.Domain.Events 13 | 14 | ## CQRS 15 | 16 | Simple command and query segregation library using MediatR 17 | 18 | - Enbiso.NLib.Cqrs 19 | 20 | Track your commands using Idempotent extension 21 | 22 | - Enbiso.NLib.Cqrs.Idempotent 23 | 24 | ## IOC Extensions 25 | 26 | IOC extensions based off microsoft dependency injection abstractions to provide attribute based injection. 27 | 28 | - Enbiso.NLib.DependencyInjection 29 | 30 | ## Event Bus 31 | 32 | Async message publishing and subscription library set 33 | 34 | - Enbiso.NLib.EventBus 35 | - Enbiso.NLib.EventBus.Abstractions 36 | 37 | Implementations available for NATS and RabbitMQ 38 | 39 | - Enbiso.NLib.EventBus.Nats 40 | - Enbiso.NLib.EventBus.RabbitMq 41 | 42 | ## Event Logger 43 | 44 | Set of libraries to keep your async events persistant 45 | 46 | - Enbiso.NLib.EventLogger 47 | 48 | Implementation available for EF and MongoDB 49 | 50 | - Enbiso.NLib.EventLogger.EntityFramework 51 | - Enbiso.NLib.EventLogger.Mongo 52 | 53 | ## API Exception Handlers 54 | 55 | Simple yet extensible API global exception handler 56 | 57 | - Enbiso.NLib.GlobalExceptions 58 | 59 | ## Idempotency 60 | 61 | Simple Idempotent library to track your API calls to the end. 62 | 63 | - Enbiso.NLib.Idempotency 64 | 65 | Implementation available for EF 66 | 67 | - Enbiso.NLib.Idempotency.EntityFramework 68 | 69 | ## OpenAPI Extensions 70 | 71 | Simple wrapper on top of Swashbuckle to maintain uniformity amoung the APIs 72 | 73 | - Enbiso.NLib.OpenApi 74 | 75 | ## REST Client implementation 76 | 77 | REST client built using httpclient with Open ID Connect 78 | 79 | - Enbiso.NLib.RestClient 80 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enbiso/nlib/e132f8f425908af62c12f3717c4ab0c52ab624bf/logo.png --------------------------------------------------------------------------------