├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github ├── WorkflowGen │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── WorkflowGen.csproj │ └── WorkflowGen.v3.ncrunchproject ├── dependabot.yml └── workflows │ ├── aspnet-core-ci.yml │ ├── bullseye-ci.yml │ ├── codeql-analysis.yml │ ├── configuration-ci.yml │ ├── github-ci.yml │ ├── hosting-ci.yml │ ├── lambda-ci.yml │ ├── pulumi-ci.yml │ ├── system-extensions-ci.yml │ └── webhook-relay-ci.yml ├── .gitignore ├── .ncrunch ├── Actions.Workflow.Tests.v3.ncrunchproject ├── Actions.Workflow.v3.ncrunchproject ├── AspNetCore.Tests.v3.ncrunchproject ├── AspNetCore.v3.ncrunchproject ├── Build.v3.ncrunchproject ├── Bullseye.Tests.v3.ncrunchproject ├── Bullseye.v3.ncrunchproject ├── EventSourcing.Domain.Testing.v3.ncrunchproject ├── EventSourcing.Domain.Tests.v3.ncrunchproject ├── EventSourcing.Domain.v3.ncrunchproject ├── Extensions.Configuration.Tests.v3.ncrunchproject ├── Extensions.Configuration.v3.ncrunchproject ├── Extensions.Hosting.Docker.v3.ncrunchproject ├── Extensions.Hosting.Example.v3.ncrunchproject ├── Extensions.Hosting.SerilogConsoleLogging.v3.ncrunchproject ├── Extensions.Hosting.Tests.v3.ncrunchproject ├── Extensions.Hosting.v3.ncrunchproject ├── Lambda.ClientExtensions.v3.ncrunchproject ├── Lambda.Example.v3.ncrunchproject ├── Lambda.TestHost.Tests.v3.ncrunchproject ├── Lambda.TestHost.v3.ncrunchproject ├── Lambda.TestUtilities.v3.ncrunchproject ├── Lambda.Tests.v3.ncrunchproject ├── Lambda.v3.ncrunchproject ├── Pulumi.Automation.v3.ncrunchproject ├── Pulumi.Aws.IntegrationTests.v3.ncrunchproject ├── Pulumi.Aws.v3.ncrunchproject ├── Pulumi.v3.ncrunchproject ├── SystemExtensions.Tests.v3.ncrunchproject ├── SystemExtensions.v3.ncrunchproject ├── Testing.Fixtures.Tests.v3.ncrunchproject ├── Testing.Fixtures.v3.ncrunchproject └── WorkflowGen.v3.ncrunchproject ├── LICENSE ├── PlatformLibs.sln ├── PlatformLibs.sln.DotSettings ├── PlatformLibs.v3.ncrunchsolution ├── artifacts └── .gitignore ├── build.ps1 ├── build ├── Build.csproj ├── Build.v3.ncrunchproject ├── Program.cs ├── Properties │ └── launchSettings.json └── coverlet-settings.xml ├── libs ├── aspnet-core │ ├── src │ │ ├── AspNetCore │ │ │ ├── AspNetCore.csproj │ │ │ ├── AspNetCore.v3.ncrunchproject │ │ │ └── Hosting │ │ │ │ ├── SetUserStartupFilter.cs │ │ │ │ ├── WebApplicationExtensions.cs │ │ │ │ └── WebHostExtensions.cs │ │ └── Directory.Build.props │ └── tests │ │ ├── AspNetCore.Tests │ │ ├── AspNetCore.Tests.csproj │ │ ├── AspNetCore.Tests.net5.0.v3.ncrunchproject │ │ ├── AspNetCore.Tests.net6.0.v3.ncrunchproject │ │ ├── AspNetCore.Tests.net7.0.v3.ncrunchproject │ │ ├── AspNetCore.Tests.netcoreapp3.1.v3.ncrunchproject │ │ ├── AspNetCore.Tests.v3.ncrunchproject │ │ └── Hosting │ │ │ ├── SetUserStartupFilterTests.cs │ │ │ └── WebHostExtensionsTests.cs │ │ └── Directory.Build.props ├── bullseye │ ├── src │ │ ├── Bullseye │ │ │ ├── Bullseye.csproj │ │ │ ├── Bullseye.v3.ncrunchproject │ │ │ └── BullseyeUtils.cs │ │ └── Directory.Build.props │ └── tests │ │ ├── Bullseye.Tests │ │ ├── Bullseye.Tests.csproj │ │ ├── Bullseye.Tests.v3.ncrunchproject │ │ └── UnitTest1.cs │ │ └── Directory.Build.props ├── configuration │ ├── README.md │ ├── src │ │ ├── Configuration │ │ │ ├── ConfigInfo.cs │ │ │ ├── ConfigItem.cs │ │ │ ├── ConfigurationBuilderExtensions.cs │ │ │ ├── ConfigurationRootExtensions.cs │ │ │ ├── Extensions.Configuration.csproj │ │ │ ├── Extensions.Configuration.v3.ncrunchproject │ │ │ ├── RuntimeConfiguration.cs │ │ │ ├── RuntimeConfigurationProvider.cs │ │ │ └── RuntimeConfigurationSource.cs │ │ └── Directory.Build.props │ └── tests │ │ ├── Configuration.Tests │ │ ├── ConfigurationBuilderExtensionsTests.cs │ │ ├── ConfigurationRootExtensionsTests.cs │ │ ├── Extensions.Configuration.Tests.csproj │ │ ├── Extensions.Configuration.Tests.v3.ncrunchproject │ │ ├── RuntimeConfigurationTests.cs │ │ └── testappsettings.json │ │ └── Directory.Build.props ├── eventsourcing-domain │ ├── example │ │ ├── ExampleDomain.Tests │ │ │ ├── ExampleDomain.Tests.csproj │ │ │ ├── ExampleDomain.Tests.v3.ncrunchproject │ │ │ └── Profile │ │ │ │ └── ProfileScenarios.cs │ │ └── ExampleDomain │ │ │ ├── ExampleDomain.csproj │ │ │ ├── ExampleDomain.v3.ncrunchproject │ │ │ └── Profile │ │ │ ├── Profile.cs │ │ │ ├── ProfileId.cs │ │ │ └── UserRegistered.cs │ ├── src │ │ ├── Directory.Build.props │ │ ├── EventSourcing.Domain.Testing │ │ │ ├── EventSourcing.Domain.Testing.csproj │ │ │ ├── EventSourcing.Domain.Testing.v3.ncrunchproject │ │ │ ├── RecordedEvent.cs │ │ │ ├── Scenario.cs │ │ │ ├── ScenarioAssertions.cs │ │ │ ├── ScenarioPassed.cs │ │ │ ├── ScenarioRecordedOtherEvents.cs │ │ │ ├── ScenarioReturnedOtherResult.cs │ │ │ ├── ScenarioRunner.cs │ │ │ └── ScenarioThrewException.cs │ │ ├── EventSourcing.Domain │ │ │ ├── AggregateRoot.cs │ │ │ ├── Determine.cs │ │ │ ├── EventPlayer.cs │ │ │ ├── EventRecorder.cs │ │ │ ├── EventSourcedEntity.cs │ │ │ ├── EventSourcedEntityEntry.cs │ │ │ ├── EventSourcing.Domain.csproj │ │ │ ├── EventSourcing.Domain.v3.ncrunchproject │ │ │ ├── IEventSourcedEntityChangeTracker.cs │ │ │ ├── MessageNameResolver.cs │ │ │ ├── MessageTypeResolver.cs │ │ │ ├── NamespaceBasedGuidGenerator.cs │ │ │ ├── ResolveMessageName.cs │ │ │ ├── ResolveMessageType.cs │ │ │ └── StreamName.cs │ │ └── icon.png │ └── tests │ │ ├── Directory.Build.props │ │ └── EventSourcing.Domain.Tests │ │ ├── EventPlayerTests.cs │ │ ├── EventRecorderTests.cs │ │ ├── EventSourcedEntityTests.cs │ │ ├── EventSourcing.Domain.Tests.csproj │ │ └── EventSourcing.Domain.Tests.v3.ncrunchproject ├── github │ ├── GitHub.sln │ ├── readme.md │ ├── src │ │ ├── Actions.Workflow.Extensions │ │ │ ├── Actions.Workflow.Extensions.csproj │ │ │ ├── Actions.Workflow.Extensions.v3.ncrunchproject │ │ │ ├── README.md │ │ │ └── StepExtensions.cs │ │ ├── Actions.Workflow │ │ │ ├── Actions.Workflow.csproj │ │ │ ├── Actions.Workflow.v3.ncrunchproject │ │ │ ├── Event.cs │ │ │ ├── GitHubHostedRunner.cs │ │ │ ├── GitTrigger.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── Job.cs │ │ │ ├── JobContainer.cs │ │ │ ├── JobContainerEnv.cs │ │ │ ├── JobDefaults.cs │ │ │ ├── JobEnv.cs │ │ │ ├── JobKeyValueMap.cs │ │ │ ├── JobOutputs.cs │ │ │ ├── JobSecrets.cs │ │ │ ├── JobService.cs │ │ │ ├── JobServiceEnv.cs │ │ │ ├── JobServices.cs │ │ │ ├── JobWith.cs │ │ │ ├── KeyValueMap.cs │ │ │ ├── MapExtensions.cs │ │ │ ├── Matrix.cs │ │ │ ├── On.cs │ │ │ ├── Permission.cs │ │ │ ├── PermissionConfig.cs │ │ │ ├── PermissionHelper.cs │ │ │ ├── PermissionKeys.cs │ │ │ ├── README.md │ │ │ ├── ScheduleTrigger.cs │ │ │ ├── Shells.cs │ │ │ ├── Step.cs │ │ │ ├── StepEnv.cs │ │ │ ├── StepKeyValueMap.cs │ │ │ ├── StepWith.cs │ │ │ ├── Strategy.cs │ │ │ ├── Trigger.cs │ │ │ ├── Workflow.cs │ │ │ ├── WorkflowCall.cs │ │ │ ├── WorkflowCallType.cs │ │ │ ├── WorkflowDefaults.cs │ │ │ ├── WorkflowDispatch.cs │ │ │ ├── WorkflowKeyValueMap.cs │ │ │ └── WorkflowRunTrigger.cs │ │ └── Directory.Build.props │ └── tests │ │ ├── Actions.Workflow.Tests │ │ ├── Actions.Workflow.Tests.csproj │ │ ├── Actions.Workflow.Tests.v3.ncrunchproject │ │ ├── Example.cs │ │ ├── Extensions │ │ │ └── StepExtensionsTests.cs │ │ ├── JobStepTests.cs │ │ ├── JobTests.cs │ │ └── WorkflowTests.cs │ │ └── Directory.Build.props ├── hosting │ ├── README.md │ ├── console.png │ ├── src │ │ ├── Directory.Build.props │ │ ├── Hosting.Docker │ │ │ ├── DockerHostedService.cs │ │ │ ├── Extensions.Hosting.Docker.csproj │ │ │ └── Extensions.Hosting.Docker.v3.ncrunchproject │ │ ├── Hosting.Example │ │ │ ├── AdminWebAppHostedService.cs │ │ │ ├── Extensions.Hosting.Example.csproj │ │ │ ├── Extensions.Hosting.Example.v3.ncrunchproject │ │ │ ├── HostedServiceContext.cs │ │ │ ├── MainWebAppHostedService.cs │ │ │ ├── MySqlHostedService.cs │ │ │ ├── Program.cs │ │ │ ├── Properties │ │ │ │ └── launchSettings.json │ │ │ └── SeqHostedService.cs │ │ ├── Hosting.SerilogConsoleLogging │ │ │ ├── Extensions.Hosting.SerilogConsoleLogging.csproj │ │ │ ├── Extensions.Hosting.SerilogConsoleLogging.v3.ncrunchproject │ │ │ └── LoggerConfigurationExtensions.cs │ │ └── Hosting │ │ │ ├── Extensions.Hosting.csproj │ │ │ ├── Extensions.Hosting.v3.ncrunchproject │ │ │ ├── HostedServiceBag.cs │ │ │ ├── HostedServiceWrapper.cs │ │ │ ├── ParallelHostedServiceBuilder.cs │ │ │ ├── ParallelHostedServices.cs │ │ │ ├── SequentialHostedServiceBuilder.cs │ │ │ ├── SequentialHostedServices.cs │ │ │ └── ServiceCollectionExtensions.cs │ └── tests │ │ ├── Directory.Build.props │ │ └── Hosting.Tests │ │ ├── ContainerHostedServiceTests.cs │ │ ├── Context.cs │ │ ├── ExampleHostedService.cs │ │ ├── Extensions.Hosting.Tests.csproj │ │ ├── Extensions.Hosting.Tests.v3.ncrunchproject │ │ └── SequentialAndParallelHostedServiceTests.cs ├── icon.png ├── lambda │ ├── readme.md │ ├── src │ │ ├── Directory.Build.props │ │ ├── Lambda.ClientExtensions │ │ │ ├── Lambda.ClientExtensions.csproj │ │ │ ├── Lambda.ClientExtensions.v3.ncrunchproject │ │ │ └── LambdaClientExtensions.cs │ │ ├── Lambda.Example │ │ │ ├── ExampleAsynchronousInvokeFunction.cs │ │ │ ├── ExampleAsynchronousInvokeHandler.cs │ │ │ ├── ExampleOptions.cs │ │ │ ├── ExampleSynchronousInvokeFunction.cs │ │ │ ├── ExampleSynchronousInvokeHandler.cs │ │ │ ├── Lambda.Example.csproj │ │ │ ├── Lambda.Example.v3.ncrunchproject │ │ │ ├── Properties │ │ │ │ └── launchSettings.json │ │ │ ├── Readme.md │ │ │ ├── Request.cs │ │ │ ├── Response.cs │ │ │ └── aws-lambda-tools-defaults.json │ │ ├── Lambda.TestHost │ │ │ ├── DefaultLambdaFunctionActivator.cs │ │ │ ├── DelegateLambdaFunctionActivator.cs │ │ │ ├── ILambdaFunctionActivator.cs │ │ │ ├── ILambdaFunctionInfo.cs │ │ │ ├── Lambda.TestHost.csproj │ │ │ ├── Lambda.TestHost.v3.ncrunchproject │ │ │ ├── LambdaAccountPool.cs │ │ │ ├── LambdaFunctionInfo.cs │ │ │ ├── LambdaInstance.cs │ │ │ ├── LambdaInstancePool.cs │ │ │ ├── LambdaTestHost.cs │ │ │ ├── LambdaTestHostSettings.cs │ │ │ ├── TestLambdaContext.cs │ │ │ └── TestLambdaLogger.cs │ │ └── Lambda │ │ │ ├── AsynchronousInvokeFunction.cs │ │ │ ├── AsynchronousInvokeHandler.cs │ │ │ ├── FunctionBase.cs │ │ │ ├── IAsynchronousInvokeHandler.cs │ │ │ ├── ISynchronousInvokeHandler.cs │ │ │ ├── Lambda.csproj │ │ │ ├── Lambda.v3.ncrunchproject │ │ │ ├── SynchronousInvokeFunction.cs │ │ │ └── SynchronousInvokeHandler.cs │ └── tests │ │ ├── Directory.Build.props │ │ ├── Lambda.TestHost.Tests │ │ ├── FixtureUtils.cs │ │ ├── Functions │ │ │ ├── APIGatewayFunction.cs │ │ │ ├── BrokenFunction.cs │ │ │ ├── ReverseStringFunction.cs │ │ │ ├── SimpleLambdaFunction.cs │ │ │ └── SleepFunction.cs │ │ ├── Lambda.TestHost.Tests.csproj │ │ ├── Lambda.TestHost.Tests.v3.ncrunchproject │ │ ├── LambdaAccountPoolTests.cs │ │ ├── LambdaTestHostTests.cs │ │ ├── LocalStack │ │ │ ├── LocalStackFixture.cs │ │ │ ├── LocalStackIntegrationsTests.cs │ │ │ └── SQSLambdaFunction.cs │ │ ├── SimpleLambdaFunction.cs │ │ ├── StepFunctions │ │ │ └── StepFunctionsIntegrationTests.cs │ │ └── XunitLambdaLogger.cs │ │ └── Lambda.Tests │ │ ├── AsynchronousInvokeFunctionTests.cs │ │ ├── Lambda.Tests.csproj │ │ ├── Lambda.Tests.v3.ncrunchproject │ │ └── SynchronousInvokeFunctionTests.cs ├── pulumi │ ├── src │ │ ├── Directory.Build.props │ │ ├── Pulumi.Automation │ │ │ ├── Aws │ │ │ │ ├── AwsConfiguration.cs │ │ │ │ ├── AwsConfigurationKeys.cs │ │ │ │ └── ProviderEndpoints.cs │ │ │ ├── EnvironmentVariableKeys.cs │ │ │ ├── LocalWorkspaceOptionsExtensions.cs │ │ │ ├── Pulumi.Automation.csproj │ │ │ ├── Pulumi.Automation.net6.0.v3.ncrunchproject │ │ │ ├── Pulumi.Automation.net7.0.v3.ncrunchproject │ │ │ ├── Pulumi.Automation.v3.ncrunchproject │ │ │ └── WorkspaceExtensions.cs │ │ ├── Pulumi.Aws │ │ │ ├── Ec2 │ │ │ │ ├── StandardVpc.cs │ │ │ │ └── StandardVpcArgs.cs │ │ │ ├── Pulumi.Aws.csproj │ │ │ ├── Pulumi.Aws.net6.0.v3.ncrunchproject │ │ │ ├── Pulumi.Aws.net7.0.v3.ncrunchproject │ │ │ └── Pulumi.Aws.v3.ncrunchproject │ │ └── Pulumi │ │ │ ├── Pulumi.csproj │ │ │ ├── Pulumi.net6.0.v3.ncrunchproject │ │ │ ├── Pulumi.net7.0.v3.ncrunchproject │ │ │ └── Pulumi.v3.ncrunchproject │ └── tests │ │ ├── Directory.Build.props │ │ └── Pulumi.Aws.IntegrationTests │ │ ├── Ec2 │ │ └── StandardVpcTests.cs │ │ ├── Pulumi.Aws.IntegrationTests.csproj │ │ └── Pulumi.Aws.IntegrationTests.v3.ncrunchproject ├── src.props ├── system-extensions │ ├── src │ │ ├── Directory.Build.props │ │ └── SystemExtensions │ │ │ ├── AsyncDelegateDisposable.cs │ │ │ ├── AsyncDisposableAction.cs │ │ │ ├── DelegateDisposable.cs │ │ │ ├── DeterministicGuidFactory.cs │ │ │ ├── DisposableAction.cs │ │ │ ├── GetUtcNow.cs │ │ │ ├── Net │ │ │ ├── Http │ │ │ │ └── FileDownload.cs │ │ │ └── Sockets │ │ │ │ └── PortFinder.cs │ │ │ ├── SystemExtensions.csproj │ │ │ ├── SystemExtensions.v3.ncrunchproject │ │ │ └── Text │ │ │ └── Json │ │ │ ├── SnakeCaseNamingPolicy.cs │ │ │ └── StringUtils.cs │ └── tests │ │ ├── Directory.Build.props │ │ └── SystemExtensions.Tests │ │ ├── DeterministicGuidFactoryTests.cs │ │ ├── Net │ │ ├── Http │ │ │ └── FileDownloadTests.cs │ │ └── Sockets │ │ │ └── PortFinderTests.cs │ │ ├── SystemExtensions.Tests.csproj │ │ └── SystemExtensions.Tests.v3.ncrunchproject ├── test.props └── webhook-relay │ ├── WebhookRelay.slnf │ ├── readme.md │ ├── src │ ├── Directory.Build.props │ └── WebhookRelay │ │ ├── ClientState.cs │ │ ├── ClientTrigger.cs │ │ ├── Metadata.cs │ │ ├── WebhookMessage.cs │ │ ├── WebhookRelay.csproj │ │ ├── WebhookRelay.v3.ncrunchproject │ │ ├── WebhookRelayClient.cs │ │ └── WebhookRelayClientConfiguration.cs │ ├── statemachine.png │ └── tests │ ├── Directory.Build.props │ └── WebhookRelay.Tests │ ├── Usings.cs │ ├── WebhookRelay.Tests.csproj │ ├── WebhookRelay.Tests.v3.ncrunchproject │ ├── WebhookRelayClientExtensions.cs │ └── WebhookRelayClientTests.cs ├── nuget.config ├── readme.md ├── statemachine.md └── temp └── .gitignore /.dockerignore: -------------------------------------------------------------------------------- 1 | **/bin/ 2 | **/obj/ 3 | **/global.json 4 | **/Dockerfile* 5 | **/.dockerignore* 6 | **/*.user 7 | **/*.Custom.json 8 | **/*.CustomDotSettings 9 | .idea/ 10 | .vs/ 11 | .vscode/ 12 | .git/ 13 | adr/ 14 | artifacts/ 15 | temp/ 16 | **/_NCrunch_* -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/logicality-io/libs/af5b790d0d01372d07b5823478eaa7a15a2afd0b/.editorconfig -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behaviour, in case users don't have core.autocrlf set. 2 | * text=auto 3 | 4 | [attr]utf16 diff merge -lf 5 | *.ncrunchproject utf16 eol=lf 6 | 7 | # Custom for Visual Studio 8 | *.sln text eol=crlf merge=union 9 | *.csproj merge=union 10 | *.vbproj merge=union 11 | *.fsproj merge=union 12 | *.dbproj merge=union 13 | 14 | # Explicitly declare text files we want to always be normalized and converted 15 | # to native line endings on checkout. 16 | *.c text 17 | *.h text 18 | *.cs text 19 | *.config text 20 | *.xml text 21 | *.manifest text 22 | *.bat text 23 | *.cmd text 24 | *.sh text 25 | *.txt text 26 | *.dat text 27 | *.rc text 28 | *.ps1 text 29 | *.psm1 text 30 | *.js text 31 | *.css text 32 | *.html text 33 | *.sln text 34 | *.DotSettings text 35 | *.csproj text 36 | *.ncrunchproject text 37 | *.fs text 38 | *.fsproj text 39 | *.liquid text 40 | *.boo text 41 | *.pp text 42 | *.targets text 43 | *.markdown text 44 | *.md text 45 | *.bat text 46 | *.xslt text 47 | 48 | # Declare files that will always have CRLF line endings on checkout. 49 | 50 | # Denote all files that are truly binary and should not be modified. 51 | *.ico binary 52 | *.gif binary 53 | *.png binary 54 | *.jpg binary 55 | *.dll binary 56 | *.exe binary 57 | *.pdb binary 58 | -------------------------------------------------------------------------------- /.github/WorkflowGen/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "WorkflowGen": { 4 | "commandName": "Project", 5 | "workingDirectory": "C:\\dev\\logicality\\platform-libs\\.github\\WorkflowGen" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /.github/WorkflowGen/WorkflowGen.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.github/WorkflowGen/WorkflowGen.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "nuget" 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | open-pull-requests-limit: 50 13 | 14 | - package-ecosystem: "github-actions" 15 | directory: "/" # Location of package manifests 16 | schedule: 17 | interval: "daily" -------------------------------------------------------------------------------- /.github/workflows/aspnet-core-ci.yml: -------------------------------------------------------------------------------- 1 | # This was generated by tool. Edits will be overwritten. 2 | 3 | name: aspnet-core-ci 4 | on: 5 | pull_request: 6 | paths: 7 | - .github/workflows/aspnet-core-** 8 | - libs/aspnet-core** 9 | - build/** 10 | push: 11 | branches: 12 | - main 13 | paths: 14 | - .github/workflows/aspnet-core-** 15 | - libs/aspnet-core** 16 | - build/** 17 | tags: 18 | - aspnet-core-** 19 | permissions: 20 | packages: write 21 | jobs: 22 | build: 23 | runs-on: ubuntu-latest 24 | env: 25 | GITHUB_TOKEN: ${secrets.GITHUB_TOKEN} 26 | LOGICALITY_NUGET_ORG: ${secrets.LOGICALITY_NUGET_ORG} 27 | WEBHOOKRELAYTOKENKEY: ${secrets.WEBHOOKRELAYTOKENKEY} 28 | WEBHOOKRELAYTOKENSECRET: ${secrets.WEBHOOKRELAYTOKENSECRET} 29 | WEBHOOKURL: ${secrets.WEBHOOKURL} 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | with: 34 | fetch-depth: 0 35 | - name: Log into GitHub Container Registry 36 | run: echo "${{secrets.GITHUB_TOKEN}}" | docker login ghcr.io -u ${{github.actor}} --password-stdin 37 | - name: Setup Dotnet 38 | uses: actions/setup-dotnet@v4 39 | with: 40 | dotnet-version: 8.0.x 41 | - name: Print Env 42 | run: printenv 43 | shell: bash 44 | - name: Test 45 | timeout-minutes: 5 46 | run: ./build.ps1 aspnet-core-test 47 | shell: pwsh 48 | - name: Pack 49 | run: ./build.ps1 aspnet-core-pack 50 | shell: pwsh 51 | - name: Push to GitHub 52 | if: github.event_name == 'push' 53 | continue-on-error: true 54 | run: ./build.ps1 push-github 55 | shell: pwsh 56 | - name: Push to Nuget.org (on tag) 57 | if: startsWith(github.ref, 'refs/tags/aspnet-core') 58 | continue-on-error: true 59 | run: ./build.ps1 push-nugetorg 60 | shell: pwsh 61 | - name: Upload Artifacts 62 | uses: actions/upload-artifact@v4 63 | with: 64 | name: artifacts 65 | path: artifacts 66 | -------------------------------------------------------------------------------- /.github/workflows/bullseye-ci.yml: -------------------------------------------------------------------------------- 1 | # This was generated by tool. Edits will be overwritten. 2 | 3 | name: bullseye-ci 4 | on: 5 | pull_request: 6 | paths: 7 | - .github/workflows/bullseye-** 8 | - libs/bullseye** 9 | - build/** 10 | push: 11 | branches: 12 | - main 13 | paths: 14 | - .github/workflows/bullseye-** 15 | - libs/bullseye** 16 | - build/** 17 | tags: 18 | - bullseye-** 19 | permissions: 20 | packages: write 21 | jobs: 22 | build: 23 | runs-on: ubuntu-latest 24 | env: 25 | GITHUB_TOKEN: ${secrets.GITHUB_TOKEN} 26 | LOGICALITY_NUGET_ORG: ${secrets.LOGICALITY_NUGET_ORG} 27 | WEBHOOKRELAYTOKENKEY: ${secrets.WEBHOOKRELAYTOKENKEY} 28 | WEBHOOKRELAYTOKENSECRET: ${secrets.WEBHOOKRELAYTOKENSECRET} 29 | WEBHOOKURL: ${secrets.WEBHOOKURL} 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | with: 34 | fetch-depth: 0 35 | - name: Log into GitHub Container Registry 36 | run: echo "${{secrets.GITHUB_TOKEN}}" | docker login ghcr.io -u ${{github.actor}} --password-stdin 37 | - name: Setup Dotnet 38 | uses: actions/setup-dotnet@v4 39 | with: 40 | dotnet-version: 8.0.x 41 | - name: Print Env 42 | run: printenv 43 | shell: bash 44 | - name: Test 45 | timeout-minutes: 5 46 | run: ./build.ps1 bullseye-test 47 | shell: pwsh 48 | - name: Pack 49 | run: ./build.ps1 bullseye-pack 50 | shell: pwsh 51 | - name: Push to GitHub 52 | if: github.event_name == 'push' 53 | continue-on-error: true 54 | run: ./build.ps1 push-github 55 | shell: pwsh 56 | - name: Push to Nuget.org (on tag) 57 | if: startsWith(github.ref, 'refs/tags/bullseye') 58 | continue-on-error: true 59 | run: ./build.ps1 push-nugetorg 60 | shell: pwsh 61 | - name: Upload Artifacts 62 | uses: actions/upload-artifact@v4 63 | with: 64 | name: artifacts 65 | path: artifacts 66 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # This was generated by tool. Edits will be overwritten. 2 | 3 | name: CodeQL 4 | on: 5 | push: 6 | branches: 7 | - main 8 | pull_request: 9 | branches: 10 | - main 11 | schedule: 12 | - cron: '39 8 * * 1' 13 | jobs: 14 | analyze: 15 | name: Analyse 16 | runs-on: ubuntu-latest 17 | permissions: 18 | actions: read 19 | contents: read 20 | security-events: write 21 | strategy: 22 | matrix: 23 | language: 24 | - csharp 25 | fail-fast: false 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | with: 30 | fetch-depth: 0 31 | - name: Setup Dotnet 32 | uses: actions/setup-dotnet@v4 33 | with: 34 | dotnet-version: 8.0.x 35 | - run: dotnet --info 36 | - name: Initialize CodeQL 37 | uses: github/codeql-action/init@v2 38 | with: 39 | languages: ${{ matrix.language }} 40 | - run: ./build.ps1 build 41 | shell: pwsh 42 | - name: Perform CodeQL Analysis 43 | uses: github/codeql-action/analyze@v2 44 | -------------------------------------------------------------------------------- /.github/workflows/configuration-ci.yml: -------------------------------------------------------------------------------- 1 | # This was generated by tool. Edits will be overwritten. 2 | 3 | name: configuration-ci 4 | on: 5 | pull_request: 6 | paths: 7 | - .github/workflows/configuration-** 8 | - libs/configuration** 9 | - build/** 10 | push: 11 | branches: 12 | - main 13 | paths: 14 | - .github/workflows/configuration-** 15 | - libs/configuration** 16 | - build/** 17 | tags: 18 | - configuration-** 19 | permissions: 20 | packages: write 21 | jobs: 22 | build: 23 | runs-on: ubuntu-latest 24 | env: 25 | GITHUB_TOKEN: ${secrets.GITHUB_TOKEN} 26 | LOGICALITY_NUGET_ORG: ${secrets.LOGICALITY_NUGET_ORG} 27 | WEBHOOKRELAYTOKENKEY: ${secrets.WEBHOOKRELAYTOKENKEY} 28 | WEBHOOKRELAYTOKENSECRET: ${secrets.WEBHOOKRELAYTOKENSECRET} 29 | WEBHOOKURL: ${secrets.WEBHOOKURL} 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | with: 34 | fetch-depth: 0 35 | - name: Log into GitHub Container Registry 36 | run: echo "${{secrets.GITHUB_TOKEN}}" | docker login ghcr.io -u ${{github.actor}} --password-stdin 37 | - name: Setup Dotnet 38 | uses: actions/setup-dotnet@v4 39 | with: 40 | dotnet-version: 8.0.x 41 | - name: Print Env 42 | run: printenv 43 | shell: bash 44 | - name: Test 45 | timeout-minutes: 5 46 | run: ./build.ps1 configuration-test 47 | shell: pwsh 48 | - name: Pack 49 | run: ./build.ps1 configuration-pack 50 | shell: pwsh 51 | - name: Push to GitHub 52 | if: github.event_name == 'push' 53 | continue-on-error: true 54 | run: ./build.ps1 push-github 55 | shell: pwsh 56 | - name: Push to Nuget.org (on tag) 57 | if: startsWith(github.ref, 'refs/tags/configuration') 58 | continue-on-error: true 59 | run: ./build.ps1 push-nugetorg 60 | shell: pwsh 61 | - name: Upload Artifacts 62 | uses: actions/upload-artifact@v4 63 | with: 64 | name: artifacts 65 | path: artifacts 66 | -------------------------------------------------------------------------------- /.github/workflows/github-ci.yml: -------------------------------------------------------------------------------- 1 | # This was generated by tool. Edits will be overwritten. 2 | 3 | name: github-ci 4 | on: 5 | pull_request: 6 | paths: 7 | - .github/workflows/github-** 8 | - libs/github** 9 | - build/** 10 | push: 11 | branches: 12 | - main 13 | paths: 14 | - .github/workflows/github-** 15 | - libs/github** 16 | - build/** 17 | tags: 18 | - github-** 19 | permissions: 20 | packages: write 21 | jobs: 22 | build: 23 | runs-on: ubuntu-latest 24 | env: 25 | GITHUB_TOKEN: ${secrets.GITHUB_TOKEN} 26 | LOGICALITY_NUGET_ORG: ${secrets.LOGICALITY_NUGET_ORG} 27 | WEBHOOKRELAYTOKENKEY: ${secrets.WEBHOOKRELAYTOKENKEY} 28 | WEBHOOKRELAYTOKENSECRET: ${secrets.WEBHOOKRELAYTOKENSECRET} 29 | WEBHOOKURL: ${secrets.WEBHOOKURL} 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | with: 34 | fetch-depth: 0 35 | - name: Log into GitHub Container Registry 36 | run: echo "${{secrets.GITHUB_TOKEN}}" | docker login ghcr.io -u ${{github.actor}} --password-stdin 37 | - name: Setup Dotnet 38 | uses: actions/setup-dotnet@v4 39 | with: 40 | dotnet-version: 8.0.x 41 | - name: Print Env 42 | run: printenv 43 | shell: bash 44 | - name: Test 45 | timeout-minutes: 5 46 | run: ./build.ps1 github-test 47 | shell: pwsh 48 | - name: Pack 49 | run: ./build.ps1 github-pack 50 | shell: pwsh 51 | - name: Push to GitHub 52 | if: github.event_name == 'push' 53 | continue-on-error: true 54 | run: ./build.ps1 push-github 55 | shell: pwsh 56 | - name: Push to Nuget.org (on tag) 57 | if: startsWith(github.ref, 'refs/tags/github') 58 | continue-on-error: true 59 | run: ./build.ps1 push-nugetorg 60 | shell: pwsh 61 | - name: Upload Artifacts 62 | uses: actions/upload-artifact@v4 63 | with: 64 | name: artifacts 65 | path: artifacts 66 | -------------------------------------------------------------------------------- /.github/workflows/hosting-ci.yml: -------------------------------------------------------------------------------- 1 | # This was generated by tool. Edits will be overwritten. 2 | 3 | name: hosting-ci 4 | on: 5 | pull_request: 6 | paths: 7 | - .github/workflows/hosting-** 8 | - libs/hosting** 9 | - build/** 10 | push: 11 | branches: 12 | - main 13 | paths: 14 | - .github/workflows/hosting-** 15 | - libs/hosting** 16 | - build/** 17 | tags: 18 | - hosting-** 19 | permissions: 20 | packages: write 21 | jobs: 22 | build: 23 | runs-on: ubuntu-latest 24 | env: 25 | GITHUB_TOKEN: ${secrets.GITHUB_TOKEN} 26 | LOGICALITY_NUGET_ORG: ${secrets.LOGICALITY_NUGET_ORG} 27 | WEBHOOKRELAYTOKENKEY: ${secrets.WEBHOOKRELAYTOKENKEY} 28 | WEBHOOKRELAYTOKENSECRET: ${secrets.WEBHOOKRELAYTOKENSECRET} 29 | WEBHOOKURL: ${secrets.WEBHOOKURL} 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | with: 34 | fetch-depth: 0 35 | - name: Log into GitHub Container Registry 36 | run: echo "${{secrets.GITHUB_TOKEN}}" | docker login ghcr.io -u ${{github.actor}} --password-stdin 37 | - name: Setup Dotnet 38 | uses: actions/setup-dotnet@v4 39 | with: 40 | dotnet-version: 8.0.x 41 | - name: Print Env 42 | run: printenv 43 | shell: bash 44 | - name: Test 45 | timeout-minutes: 5 46 | run: ./build.ps1 hosting-test 47 | shell: pwsh 48 | - name: Pack 49 | run: ./build.ps1 hosting-pack 50 | shell: pwsh 51 | - name: Push to GitHub 52 | if: github.event_name == 'push' 53 | continue-on-error: true 54 | run: ./build.ps1 push-github 55 | shell: pwsh 56 | - name: Push to Nuget.org (on tag) 57 | if: startsWith(github.ref, 'refs/tags/hosting') 58 | continue-on-error: true 59 | run: ./build.ps1 push-nugetorg 60 | shell: pwsh 61 | - name: Upload Artifacts 62 | uses: actions/upload-artifact@v4 63 | with: 64 | name: artifacts 65 | path: artifacts 66 | -------------------------------------------------------------------------------- /.github/workflows/lambda-ci.yml: -------------------------------------------------------------------------------- 1 | # This was generated by tool. Edits will be overwritten. 2 | 3 | name: lambda-ci 4 | on: 5 | pull_request: 6 | paths: 7 | - .github/workflows/lambda-** 8 | - libs/lambda** 9 | - build/** 10 | push: 11 | branches: 12 | - main 13 | paths: 14 | - .github/workflows/lambda-** 15 | - libs/lambda** 16 | - build/** 17 | tags: 18 | - lambda-** 19 | permissions: 20 | packages: write 21 | jobs: 22 | build: 23 | runs-on: ubuntu-latest 24 | env: 25 | GITHUB_TOKEN: ${secrets.GITHUB_TOKEN} 26 | LOGICALITY_NUGET_ORG: ${secrets.LOGICALITY_NUGET_ORG} 27 | WEBHOOKRELAYTOKENKEY: ${secrets.WEBHOOKRELAYTOKENKEY} 28 | WEBHOOKRELAYTOKENSECRET: ${secrets.WEBHOOKRELAYTOKENSECRET} 29 | WEBHOOKURL: ${secrets.WEBHOOKURL} 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | with: 34 | fetch-depth: 0 35 | - name: Log into GitHub Container Registry 36 | run: echo "${{secrets.GITHUB_TOKEN}}" | docker login ghcr.io -u ${{github.actor}} --password-stdin 37 | - name: Setup Dotnet 38 | uses: actions/setup-dotnet@v4 39 | with: 40 | dotnet-version: 8.0.x 41 | - name: Print Env 42 | run: printenv 43 | shell: bash 44 | - name: Test 45 | timeout-minutes: 5 46 | run: ./build.ps1 lambda-test 47 | shell: pwsh 48 | - name: Pack 49 | run: ./build.ps1 lambda-pack 50 | shell: pwsh 51 | - name: Push to GitHub 52 | if: github.event_name == 'push' 53 | continue-on-error: true 54 | run: ./build.ps1 push-github 55 | shell: pwsh 56 | - name: Push to Nuget.org (on tag) 57 | if: startsWith(github.ref, 'refs/tags/lambda') 58 | continue-on-error: true 59 | run: ./build.ps1 push-nugetorg 60 | shell: pwsh 61 | - name: Upload Artifacts 62 | uses: actions/upload-artifact@v4 63 | with: 64 | name: artifacts 65 | path: artifacts 66 | -------------------------------------------------------------------------------- /.github/workflows/pulumi-ci.yml: -------------------------------------------------------------------------------- 1 | # This was generated by tool. Edits will be overwritten. 2 | 3 | name: pulumi-ci 4 | on: 5 | pull_request: 6 | paths: 7 | - .github/workflows/pulumi-** 8 | - libs/pulumi** 9 | - build/** 10 | push: 11 | branches: 12 | - main 13 | paths: 14 | - .github/workflows/pulumi-** 15 | - libs/pulumi** 16 | - build/** 17 | tags: 18 | - pulumi-** 19 | permissions: 20 | packages: write 21 | jobs: 22 | build: 23 | runs-on: ubuntu-latest 24 | env: 25 | GITHUB_TOKEN: ${secrets.GITHUB_TOKEN} 26 | LOGICALITY_NUGET_ORG: ${secrets.LOGICALITY_NUGET_ORG} 27 | WEBHOOKRELAYTOKENKEY: ${secrets.WEBHOOKRELAYTOKENKEY} 28 | WEBHOOKRELAYTOKENSECRET: ${secrets.WEBHOOKRELAYTOKENSECRET} 29 | WEBHOOKURL: ${secrets.WEBHOOKURL} 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | with: 34 | fetch-depth: 0 35 | - name: Log into GitHub Container Registry 36 | run: echo "${{secrets.GITHUB_TOKEN}}" | docker login ghcr.io -u ${{github.actor}} --password-stdin 37 | - name: Setup Dotnet 38 | uses: actions/setup-dotnet@v4 39 | with: 40 | dotnet-version: 8.0.x 41 | - name: Print Env 42 | run: printenv 43 | shell: bash 44 | - name: Test 45 | timeout-minutes: 5 46 | run: ./build.ps1 pulumi-test 47 | shell: pwsh 48 | - name: Pack 49 | run: ./build.ps1 pulumi-pack 50 | shell: pwsh 51 | - name: Push to GitHub 52 | if: github.event_name == 'push' 53 | continue-on-error: true 54 | run: ./build.ps1 push-github 55 | shell: pwsh 56 | - name: Push to Nuget.org (on tag) 57 | if: startsWith(github.ref, 'refs/tags/pulumi') 58 | continue-on-error: true 59 | run: ./build.ps1 push-nugetorg 60 | shell: pwsh 61 | - name: Upload Artifacts 62 | uses: actions/upload-artifact@v4 63 | with: 64 | name: artifacts 65 | path: artifacts 66 | -------------------------------------------------------------------------------- /.github/workflows/system-extensions-ci.yml: -------------------------------------------------------------------------------- 1 | # This was generated by tool. Edits will be overwritten. 2 | 3 | name: system-extensions-ci 4 | on: 5 | pull_request: 6 | paths: 7 | - .github/workflows/system-extensions-** 8 | - libs/system-extensions** 9 | - build/** 10 | push: 11 | branches: 12 | - main 13 | paths: 14 | - .github/workflows/system-extensions-** 15 | - libs/system-extensions** 16 | - build/** 17 | tags: 18 | - system-extensions-** 19 | permissions: 20 | packages: write 21 | jobs: 22 | build: 23 | runs-on: ubuntu-latest 24 | env: 25 | GITHUB_TOKEN: ${secrets.GITHUB_TOKEN} 26 | LOGICALITY_NUGET_ORG: ${secrets.LOGICALITY_NUGET_ORG} 27 | WEBHOOKRELAYTOKENKEY: ${secrets.WEBHOOKRELAYTOKENKEY} 28 | WEBHOOKRELAYTOKENSECRET: ${secrets.WEBHOOKRELAYTOKENSECRET} 29 | WEBHOOKURL: ${secrets.WEBHOOKURL} 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | with: 34 | fetch-depth: 0 35 | - name: Log into GitHub Container Registry 36 | run: echo "${{secrets.GITHUB_TOKEN}}" | docker login ghcr.io -u ${{github.actor}} --password-stdin 37 | - name: Setup Dotnet 38 | uses: actions/setup-dotnet@v4 39 | with: 40 | dotnet-version: 8.0.x 41 | - name: Print Env 42 | run: printenv 43 | shell: bash 44 | - name: Test 45 | timeout-minutes: 5 46 | run: ./build.ps1 system-extensions-test 47 | shell: pwsh 48 | - name: Pack 49 | run: ./build.ps1 system-extensions-pack 50 | shell: pwsh 51 | - name: Push to GitHub 52 | if: github.event_name == 'push' 53 | continue-on-error: true 54 | run: ./build.ps1 push-github 55 | shell: pwsh 56 | - name: Push to Nuget.org (on tag) 57 | if: startsWith(github.ref, 'refs/tags/system-extensions') 58 | continue-on-error: true 59 | run: ./build.ps1 push-nugetorg 60 | shell: pwsh 61 | - name: Upload Artifacts 62 | uses: actions/upload-artifact@v4 63 | with: 64 | name: artifacts 65 | path: artifacts 66 | -------------------------------------------------------------------------------- /.github/workflows/webhook-relay-ci.yml: -------------------------------------------------------------------------------- 1 | # This was generated by tool. Edits will be overwritten. 2 | 3 | name: webhook-relay-ci 4 | on: 5 | pull_request: 6 | paths: 7 | - .github/workflows/webhook-relay-** 8 | - libs/webhook-relay** 9 | - build/** 10 | push: 11 | branches: 12 | - main 13 | paths: 14 | - .github/workflows/webhook-relay-** 15 | - libs/webhook-relay** 16 | - build/** 17 | tags: 18 | - webhook-relay-** 19 | permissions: 20 | packages: write 21 | jobs: 22 | build: 23 | runs-on: ubuntu-latest 24 | env: 25 | GITHUB_TOKEN: ${secrets.GITHUB_TOKEN} 26 | LOGICALITY_NUGET_ORG: ${secrets.LOGICALITY_NUGET_ORG} 27 | WEBHOOKRELAYTOKENKEY: ${secrets.WEBHOOKRELAYTOKENKEY} 28 | WEBHOOKRELAYTOKENSECRET: ${secrets.WEBHOOKRELAYTOKENSECRET} 29 | WEBHOOKURL: ${secrets.WEBHOOKURL} 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | with: 34 | fetch-depth: 0 35 | - name: Log into GitHub Container Registry 36 | run: echo "${{secrets.GITHUB_TOKEN}}" | docker login ghcr.io -u ${{github.actor}} --password-stdin 37 | - name: Setup Dotnet 38 | uses: actions/setup-dotnet@v4 39 | with: 40 | dotnet-version: 8.0.x 41 | - name: Print Env 42 | run: printenv 43 | shell: bash 44 | - name: Test 45 | timeout-minutes: 5 46 | run: ./build.ps1 webhook-relay-test 47 | shell: pwsh 48 | - name: Pack 49 | run: ./build.ps1 webhook-relay-pack 50 | shell: pwsh 51 | - name: Push to GitHub 52 | if: github.event_name == 'push' 53 | continue-on-error: true 54 | run: ./build.ps1 push-github 55 | shell: pwsh 56 | - name: Push to Nuget.org (on tag) 57 | if: startsWith(github.ref, 'refs/tags/webhook-relay') 58 | continue-on-error: true 59 | run: ./build.ps1 push-nugetorg 60 | shell: pwsh 61 | - name: Upload Artifacts 62 | uses: actions/upload-artifact@v4 63 | with: 64 | name: artifacts 65 | path: artifacts 66 | -------------------------------------------------------------------------------- /.ncrunch/Actions.Workflow.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Actions.Workflow.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/AspNetCore.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | AspNetTestHostCompatibility 5 | 6 | 7 | -------------------------------------------------------------------------------- /.ncrunch/AspNetCore.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Build.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | LostReference 5 | 6 | 7 | -------------------------------------------------------------------------------- /.ncrunch/Bullseye.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Bullseye.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/EventSourcing.Domain.Testing.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/EventSourcing.Domain.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/EventSourcing.Domain.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Extensions.Configuration.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Extensions.Configuration.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Extensions.Hosting.Docker.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Extensions.Hosting.Example.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Extensions.Hosting.SerilogConsoleLogging.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Extensions.Hosting.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Extensions.Hosting.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Lambda.ClientExtensions.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Lambda.Example.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Lambda.TestHost.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Lambda.TestHost.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Lambda.TestUtilities.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Lambda.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Lambda.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Pulumi.Automation.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | LostReference 5 | 6 | 7 | -------------------------------------------------------------------------------- /.ncrunch/Pulumi.Aws.IntegrationTests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | LostReference 5 | 6 | 7 | -------------------------------------------------------------------------------- /.ncrunch/Pulumi.Aws.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | LostReference 5 | 6 | 7 | -------------------------------------------------------------------------------- /.ncrunch/Pulumi.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | LostReference 5 | 6 | 7 | -------------------------------------------------------------------------------- /.ncrunch/SystemExtensions.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/SystemExtensions.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Testing.Fixtures.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/Testing.Fixtures.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /.ncrunch/WorkflowGen.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Logicality B.V. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /PlatformLibs.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True 4 | True 5 | True 6 | True 7 | True 8 | True 9 | True 10 | DB 11 | True -------------------------------------------------------------------------------- /PlatformLibs.v3.ncrunchsolution: -------------------------------------------------------------------------------- 1 |  2 | 3 | True 4 | True 5 | True 6 | True 7 | 8 | -------------------------------------------------------------------------------- /artifacts/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = "Stop" 2 | 3 | $GitHubToken=$Env:GITHUB_TOKEN 4 | $GitHubRunNumber=$Env:GITHUB_RUN_NUMBER 5 | $LOGICALITY_NUGET_ORG=$Env:LOGICALITY_NUGET_ORG 6 | 7 | if ($GitHubToken -eq $null -or $GitHubToken -eq "") { 8 | Write-Warning "GITHUB_TOKEN environment variable empty or missing." 9 | } 10 | 11 | if ($GitHubRunNumber -eq $null -or $GitHubRunNumber -eq "") { 12 | Write-Warning "GITHUB_RUN_NUMBER environment variable empty or missing." 13 | } 14 | 15 | if ($LOGICALITY_NUGET_ORG -eq $null -or $LOGICALITY_NUGET_ORG -eq "") { 16 | Write-Warning "LOGICALITY_NUGET_ORG environment variable empty or missing." 17 | } 18 | 19 | dotnet run --project build/Build.csproj -c Release -- $args -------------------------------------------------------------------------------- /build/Build.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net6.0 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /build/Build.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /build/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Build": { 4 | "commandName": "Project", 5 | "workingDirectory": "../" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /build/coverlet-settings.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | opencover 8 | [*.Tests]* 9 | Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverage,ExcludeFromCodeCoverageAttribute 10 | false 11 | true 12 | true 13 | true 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /libs/aspnet-core/src/AspNetCore/AspNetCore.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | A set of extensions and helpers for ASP.NET Core. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /libs/aspnet-core/src/AspNetCore/AspNetCore.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/aspnet-core/src/AspNetCore/Hosting/SetUserStartupFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Http; 4 | 5 | namespace Microsoft.AspNetCore.Hosting; 6 | 7 | /// 8 | /// Provides a mechanism to set the User on the HttpContext. Primary use case is for 9 | /// injecting a user into an aspnet core application to test or satisfy authorization 10 | /// concerns. 11 | /// 12 | public class SetUserStartupFilter : IStartupFilter 13 | { 14 | private readonly Func _getClaimsPrincipal; 15 | 16 | /// 17 | /// Creates a new instance of 18 | /// 19 | /// A func to get a claims principal. Called on each request. 20 | public SetUserStartupFilter(Func getClaimsPrincipal) 21 | { 22 | _getClaimsPrincipal = getClaimsPrincipal; 23 | } 24 | 25 | /// 26 | public Action Configure(Action next) 27 | { 28 | return builder => 29 | { 30 | builder.Use((context, next2) => 31 | { 32 | var user = _getClaimsPrincipal(context); 33 | context.User = user; 34 | return next2(); 35 | }); 36 | next(builder); 37 | }; 38 | } 39 | } -------------------------------------------------------------------------------- /libs/aspnet-core/src/AspNetCore/Hosting/WebApplicationExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting.Server.Features; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | // ReSharper disable once CheckNamespace 7 | namespace Microsoft.AspNetCore.Hosting; 8 | 9 | public static class WebApplicationExtensions 10 | { 11 | public static Uri[] GetServerUris(this WebApplication webApplication) 12 | { 13 | var host = webApplication.Services.GetRequiredService(); 14 | var serverAddressesFeature = host.Services.GetRequiredService(); 15 | return serverAddressesFeature.Addresses.Select(a => new Uri(a)).ToArray(); 16 | } 17 | } -------------------------------------------------------------------------------- /libs/aspnet-core/src/AspNetCore/Hosting/WebHostExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting.Server.Features; 2 | 3 | // ReSharper disable once CheckNamespace 4 | namespace Microsoft.AspNetCore.Hosting; 5 | 6 | /// 7 | /// A set of extensions over 8 | /// 9 | public static class WebHostExtensions 10 | { 11 | /// 12 | /// Gets the addresses the server is listening on. 13 | /// 14 | /// The WebHost 15 | /// The collection of URI the host is listening on. 16 | public static Uri[] GetServerUris(this IWebHost webHost) => 17 | webHost 18 | .ServerFeatures 19 | .Get()! 20 | .Addresses 21 | .Select(a => new Uri(a)) 22 | .ToArray(); 23 | } -------------------------------------------------------------------------------- /libs/aspnet-core/src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | aspnet-core- 8 | 0.1 9 | 10 | 11 | -------------------------------------------------------------------------------- /libs/aspnet-core/tests/AspNetCore.Tests/AspNetCore.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Logicality.AspNetCore.Tests 5 | Logicality.AspNetCore 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /libs/aspnet-core/tests/AspNetCore.Tests/AspNetCore.Tests.net5.0.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | AspNetTestHostCompatibility 5 | 6 | 7 | -------------------------------------------------------------------------------- /libs/aspnet-core/tests/AspNetCore.Tests/AspNetCore.Tests.net6.0.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | AspNetTestHostCompatibility 5 | 6 | 7 | -------------------------------------------------------------------------------- /libs/aspnet-core/tests/AspNetCore.Tests/AspNetCore.Tests.net7.0.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | AspNetTestHostCompatibility 5 | 6 | 7 | -------------------------------------------------------------------------------- /libs/aspnet-core/tests/AspNetCore.Tests/AspNetCore.Tests.netcoreapp3.1.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | AspNetTestHostCompatibility 5 | 6 | 7 | -------------------------------------------------------------------------------- /libs/aspnet-core/tests/AspNetCore.Tests/AspNetCore.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/aspnet-core/tests/AspNetCore.Tests/Hosting/WebHostExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Shouldly; 4 | using Xunit; 5 | 6 | namespace Logicality.AspNetCore.Hosting; 7 | 8 | public class WebHostExtensionsTests 9 | { 10 | [Fact] 11 | public async Task Can_get_server_port() 12 | { 13 | using var webHost = WebHost 14 | .CreateDefaultBuilder(Array.Empty()) 15 | .Configure(app => {}) 16 | .UseKestrel() 17 | .UseUrls("http://127.0.0.1:0") 18 | .Build(); 19 | 20 | await webHost.StartAsync(); 21 | 22 | var serverPort = webHost.GetServerUris().First().Port; 23 | 24 | serverPort.ShouldNotBe(0); 25 | } 26 | } -------------------------------------------------------------------------------- /libs/aspnet-core/tests/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /libs/bullseye/src/Bullseye/Bullseye.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | A set of extensions and helpers for bullseye. 4 | 5 | 6 | -------------------------------------------------------------------------------- /libs/bullseye/src/Bullseye/Bullseye.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/bullseye/src/Bullseye/BullseyeUtils.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.Bullseye; 2 | 3 | /// 4 | /// Set of helper functions typically used in bullseye build programs. 5 | /// 6 | public static class BullseyeUtils 7 | { 8 | /// 9 | /// Deletes all files (except .gitignore) and subdirectories from specified path. 10 | /// 11 | /// The path whose files and subdirectories will be deleted 12 | public static void CleanDirectory(string path) 13 | { 14 | var filesToDelete = Directory 15 | .GetFiles(path, "*.*", SearchOption.AllDirectories) 16 | .Where(f => !f.EndsWith(".gitignore")); 17 | foreach (var file in filesToDelete) 18 | { 19 | Console.WriteLine($"Deleting file {file}"); 20 | File.SetAttributes(file, FileAttributes.Normal); 21 | File.Delete(file); 22 | } 23 | 24 | var directoriesToDelete = Directory.GetDirectories(path); 25 | foreach (var directory in directoriesToDelete) 26 | { 27 | Console.WriteLine($"Deleting directory {directory}"); 28 | Directory.Delete(directory, true); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /libs/bullseye/src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | bullseye- 8 | 0.1 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /libs/bullseye/tests/Bullseye.Tests/Bullseye.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /libs/bullseye/tests/Bullseye.Tests/Bullseye.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/bullseye/tests/Bullseye.Tests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace Bullseye.Tests; 4 | 5 | public class UnitTest1 6 | { 7 | [Fact] 8 | public void Test1() 9 | { 10 | 11 | } 12 | } -------------------------------------------------------------------------------- /libs/bullseye/tests/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /libs/configuration/README.md: -------------------------------------------------------------------------------- 1 | # Configuration Extensions 2 | 3 | ![Nuget](https://img.shields.io/nuget/v/Logicality.Extensions.Configuration?label=Logicality.Extensions.Configuration&style=flat-square) 4 | 5 | A set of opinionated extensions over `Microsoft.Extensions.Configuration`. 6 | 7 | ## Licence 8 | 9 | MIT -------------------------------------------------------------------------------- /libs/configuration/src/Configuration/ConfigInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace Logicality.Extensions.Configuration; 4 | 5 | /// 6 | /// Represents information about all configuration providers, paths and values. 7 | /// 8 | public class ConfigInfo(IReadOnlyCollection items) 9 | { 10 | public IReadOnlyCollection Items { get; } = items; 11 | 12 | public override string ToString() 13 | { 14 | var stringBuilder = new StringBuilder(); 15 | foreach (var item in Items) 16 | { 17 | stringBuilder.AppendLine(item.ToString()); 18 | } 19 | 20 | return stringBuilder.ToString(); 21 | } 22 | } -------------------------------------------------------------------------------- /libs/configuration/src/Configuration/ConfigItem.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.Extensions.Configuration; 2 | 3 | /// 4 | /// Represents a configuration item 5 | /// 6 | /// The configuration path. 7 | /// The configuration value. 8 | /// The configuration provider. 9 | public record ConfigItem(string Path, string Value, string Provider) 10 | { 11 | public override string ToString() 12 | => $"{Path} = {Value} ({Provider})"; 13 | } -------------------------------------------------------------------------------- /libs/configuration/src/Configuration/ConfigurationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System.Text.Json; 3 | using Logicality.Extensions.Configuration; 4 | 5 | // ReSharper disable once CheckNamespace 6 | namespace Microsoft.Extensions.Configuration; 7 | 8 | /// 9 | /// Extensions over 10 | /// 11 | public static class ConfigurationBuilderExtensions 12 | { 13 | /// 14 | /// Adds an object to the configuration that will first be serialized to json. 15 | /// 16 | /// The configuration builder. 17 | /// The object to be serialized and added to configuration. 18 | /// 19 | public static IConfigurationBuilder AddObject(this IConfigurationBuilder config, T value) where T:class 20 | { 21 | var jsonVersion = JsonSerializer.Serialize(value); 22 | var stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonVersion)) 23 | { 24 | Position = 0 25 | }; 26 | return config.AddJsonStream(stream); 27 | } 28 | 29 | /// 30 | /// Add a runtime configuration provider that allows setting/overriding configuration 31 | /// at runtime 32 | /// 33 | /// 34 | /// 35 | /// 36 | public static IConfigurationBuilder AddRuntimeConfiguration( 37 | this IConfigurationBuilder builder, 38 | RuntimeConfiguration runtimeConfiguration) => 39 | builder.Add(new RuntimeConfigurationSource(runtimeConfiguration)); 40 | } -------------------------------------------------------------------------------- /libs/configuration/src/Configuration/Extensions.Configuration.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Extensions over Microsoft.Extensions.Configuration. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /libs/configuration/src/Configuration/Extensions.Configuration.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/configuration/src/Configuration/RuntimeConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.Extensions.Configuration; 2 | 3 | /// 4 | /// Represents in memory configuration that can be changed at runtime. 5 | /// 6 | public class RuntimeConfiguration 7 | { 8 | private RuntimeConfigurationProvider? _configurationProvider; 9 | 10 | internal void SetProvider(RuntimeConfigurationProvider configurationProvider) 11 | => _configurationProvider = configurationProvider; 12 | 13 | public void SetOverride(string key, string value) 14 | { 15 | _configurationProvider!.Set(key, value); 16 | } 17 | 18 | public void RemoveOverride(string key) 19 | { 20 | _configurationProvider!.Remove(key); 21 | } 22 | } -------------------------------------------------------------------------------- /libs/configuration/src/Configuration/RuntimeConfigurationProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | 3 | namespace Logicality.Extensions.Configuration; 4 | 5 | public class RuntimeConfigurationProvider : ConfigurationProvider 6 | { 7 | private readonly object _lockObject = new(); 8 | 9 | public override void Set(string key, string? value) 10 | { 11 | lock (_lockObject) 12 | { 13 | base.Set(key, value); 14 | OnReload(); 15 | } 16 | } 17 | 18 | public void Remove(string key) 19 | { 20 | lock (_lockObject) 21 | { 22 | if (Data.ContainsKey(key)) 23 | { 24 | Data.Remove(key); 25 | OnReload(); 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /libs/configuration/src/Configuration/RuntimeConfigurationSource.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | 3 | namespace Logicality.Extensions.Configuration; 4 | 5 | public class RuntimeConfigurationSource(RuntimeConfiguration runtimeConfiguration) : IConfigurationSource 6 | { 7 | public IConfigurationProvider Build(IConfigurationBuilder _) 8 | { 9 | var provider = new RuntimeConfigurationProvider(); 10 | runtimeConfiguration.SetProvider(provider); 11 | return provider; 12 | } 13 | } -------------------------------------------------------------------------------- /libs/configuration/src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | configuration- 8 | 0.1 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /libs/configuration/tests/Configuration.Tests/ConfigurationBuilderExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Shouldly; 3 | using Xunit; 4 | 5 | namespace Logicality.Extensions.Configuration; 6 | 7 | public class ConfigurationBuilderExtensionsTests 8 | { 9 | [Fact] 10 | public void CanAddObjectToConfiguration() 11 | { 12 | var config = new ConfigurationBuilder() 13 | .AddObject(new Foo("baz")) 14 | .Build(); 15 | var value = config.GetValue("Bar"); 16 | 17 | value.ShouldBe("baz"); 18 | } 19 | 20 | private record Foo(string Bar); 21 | } -------------------------------------------------------------------------------- /libs/configuration/tests/Configuration.Tests/Extensions.Configuration.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(Org).Extensions.Configuration.Tests 5 | $(Org).Extensions.Configuration 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | PreserveNewest 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /libs/configuration/tests/Configuration.Tests/Extensions.Configuration.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/configuration/tests/Configuration.Tests/testappsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "file": true, 3 | "Serilog": { 4 | "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ], 5 | "MinimumLevel": "Debug", 6 | "WriteTo": [ 7 | { "Name": "Console" }, 8 | { 9 | "Name": "File", 10 | "Args": { "path": "Logs/log.txt" } 11 | } 12 | ], 13 | "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ], 14 | "Destructure": [ 15 | { 16 | "Name": "With", 17 | "Args": { "policy": "Sample.CustomPolicy, Sample" } 18 | }, 19 | { 20 | "Name": "ToMaximumDepth", 21 | "Args": { "maximumDestructuringDepth": 4 } 22 | }, 23 | { 24 | "Name": "ToMaximumStringLength", 25 | "Args": { "maximumStringLength": 100 } 26 | }, 27 | { 28 | "Name": "ToMaximumCollectionCount", 29 | "Args": { "maximumCollectionCount": 10 } 30 | } 31 | ], 32 | "Properties": { 33 | "Application": "Sample" 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /libs/configuration/tests/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /libs/eventsourcing-domain/example/ExampleDomain.Tests/ExampleDomain.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | runtime; build; native; contentfiles; analyzers; buildtransitive 14 | all 15 | 16 | 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | all 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /libs/eventsourcing-domain/example/ExampleDomain.Tests/ExampleDomain.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/eventsourcing-domain/example/ExampleDomain/ExampleDomain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /libs/eventsourcing-domain/example/ExampleDomain/ExampleDomain.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/eventsourcing-domain/example/ExampleDomain/Profile/Profile.cs: -------------------------------------------------------------------------------- 1 | using Logicality.EventSourcing.Domain; 2 | 3 | namespace ExampleDomain.Profile; 4 | 5 | public class Profile : EventSourcedEntity 6 | { 7 | public static readonly Func Factory = () => new Profile(); 8 | 9 | public ProfileId? Id { get; private set; } 10 | 11 | public Profile() 12 | { 13 | On(e => 14 | { 15 | Id = ProfileId.From(e.ProfileId); 16 | }); 17 | } 18 | 19 | public static Profile Register( 20 | ProfileId profileId, 21 | string firstName, 22 | string lastName, 23 | string emailAddress) 24 | { 25 | var profile = new Profile(); 26 | profile.Apply(new UserRegistered(profileId.Value, firstName, lastName, emailAddress)); 27 | return profile; 28 | } 29 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/example/ExampleDomain/Profile/ProfileId.cs: -------------------------------------------------------------------------------- 1 | using ValueOf; 2 | 3 | namespace ExampleDomain.Profile; 4 | 5 | public sealed class ProfileId : ValueOf 6 | { 7 | public const int MaxLength = 50; 8 | 9 | protected override void Validate() 10 | { 11 | if (string.IsNullOrWhiteSpace(Value)) 12 | { 13 | throw new ArgumentException("Profile Id cannot be null or empty"); 14 | } 15 | 16 | if (Value.Length < 1 || Value.Length >= MaxLength) 17 | { 18 | throw new ArgumentOutOfRangeException( 19 | nameof(Value), 20 | $"Profile Id length must be greater than 0 and smaller than {MaxLength} characters") 21 | { 22 | Data = 23 | { 24 | { "Value", Value } 25 | }, 26 | }; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/example/ExampleDomain/Profile/UserRegistered.cs: -------------------------------------------------------------------------------- 1 | namespace ExampleDomain.Profile; 2 | 3 | public record UserRegistered(string ProfileId, string FirstName, string LastName, string EmailAddress); -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | eventsourcing-domain- 7 | 0.1 8 | 9 | 10 | -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain.Testing/EventSourcing.Domain.Testing.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(AssemblyName) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain.Testing/EventSourcing.Domain.Testing.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain.Testing/RecordedEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.EventSourcing.Domain.Testing; 2 | 3 | public class RecordedEvent(StreamName stream, object message) 4 | { 5 | public StreamName Stream { get; } = stream; 6 | 7 | public object Message { get; } = message ?? throw new ArgumentNullException(nameof(message)); 8 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain.Testing/ScenarioPassed.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.EventSourcing.Domain.Testing; 2 | 3 | public class ScenarioPassed 4 | { 5 | public Scenario Scenario { get; } 6 | 7 | internal ScenarioPassed(Scenario scenario) 8 | { 9 | Scenario = scenario ?? throw new ArgumentNullException(nameof(scenario)); 10 | } 11 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain.Testing/ScenarioRecordedOtherEvents.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.EventSourcing.Domain.Testing; 2 | 3 | public class ScenarioRecordedOtherEvents 4 | { 5 | public Scenario Scenario { get; } 6 | 7 | public IReadOnlyCollection Actual { get; } 8 | 9 | internal ScenarioRecordedOtherEvents(Scenario scenario, IReadOnlyCollection actual) 10 | { 11 | Scenario = scenario ?? throw new ArgumentNullException(nameof(scenario)); 12 | Actual = actual ?? throw new ArgumentNullException(nameof(actual)); 13 | } 14 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain.Testing/ScenarioReturnedOtherResult.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.EventSourcing.Domain.Testing; 2 | 3 | public class ScenarioReturnedOtherResult 4 | { 5 | public Scenario Scenario { get; } 6 | 7 | public object Actual { get; } 8 | 9 | internal ScenarioReturnedOtherResult(Scenario scenario, object actual) 10 | { 11 | Scenario = scenario ?? throw new ArgumentNullException(nameof(scenario)); 12 | Actual = actual; 13 | } 14 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain.Testing/ScenarioThrewException.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.EventSourcing.Domain.Testing; 2 | 3 | public class ScenarioThrewException(Scenario scenario, Exception exception) 4 | { 5 | public Scenario Scenario { get; } = scenario; 6 | 7 | public Exception Exception { get; } = exception; 8 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain/AggregateRoot.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.EventSourcing.Domain; 2 | 3 | public class AggregateRoot : EventSourcedEntity 4 | { 5 | public int? Version { get; private set; } 6 | 7 | public void SetVersion(int version) => Version = version; 8 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain/Determine.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.EventSourcing.Domain; 2 | 3 | public static class Determine 4 | { 5 | private static readonly NamespaceBasedGuidGenerator Generator = new( 6 | new Guid("10a8d5dd-c836-434e-bc42-b62e559171b5") 7 | ); 8 | 9 | public static Guid NextId(Guid previous, int index) 10 | { 11 | var input = new List(16 + 4); 12 | input.AddRange(previous.ToByteArray()); 13 | input.AddRange(BitConverter.GetBytes(index)); 14 | return Generator.Create(input.ToArray()); 15 | } 16 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain/EventPlayer.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.EventSourcing.Domain; 2 | 3 | public class EventPlayer 4 | { 5 | private readonly Dictionary> _handlers = new(); 6 | 7 | public void Register(Action handler) 8 | { 9 | if (handler == null) 10 | { 11 | throw new ArgumentNullException(nameof(handler)); 12 | } 13 | 14 | var typeOfEvent = typeof(TEvent); 15 | if (_handlers.ContainsKey(typeOfEvent)) 16 | { 17 | throw new InvalidOperationException( 18 | "There's already a handler registered for the event of type " + 19 | $"'{typeOfEvent.Name}'"); 20 | } 21 | 22 | _handlers.Add(typeOfEvent, @event => handler((TEvent)@event)); 23 | } 24 | 25 | public void Register(Type typeOfEvent, Action handler) 26 | { 27 | if (typeOfEvent == null) 28 | { 29 | throw new ArgumentNullException(nameof(typeOfEvent)); 30 | } 31 | 32 | if (handler == null) 33 | { 34 | throw new ArgumentNullException(nameof(handler)); 35 | } 36 | 37 | if (_handlers.ContainsKey(typeOfEvent)) 38 | { 39 | throw new InvalidOperationException( 40 | "There's already a handler registered for the event of type " + 41 | $"'{typeOfEvent.Name}'"); 42 | } 43 | 44 | _handlers.Add(typeOfEvent, handler); 45 | } 46 | 47 | public void Play(object @event) 48 | { 49 | if (@event == null) 50 | { 51 | throw new ArgumentNullException(nameof(@event)); 52 | } 53 | 54 | if (_handlers.TryGetValue(@event.GetType(), out var handler)) 55 | { 56 | handler(@event); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain/EventRecorder.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.EventSourcing.Domain; 2 | 3 | public class EventRecorder 4 | { 5 | private readonly List _recorded = new(); 6 | 7 | public bool HasRecordedEvents => _recorded.Count != 0; 8 | 9 | public object[] RecordedEvents => _recorded.ToArray(); 10 | 11 | public void Record(object @event) 12 | { 13 | if (@event == null) 14 | { 15 | throw new ArgumentNullException(nameof(@event)); 16 | } 17 | 18 | _recorded.Add(@event); 19 | } 20 | 21 | public void Reset() => _recorded.Clear(); 22 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain/EventSourcedEntity.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.EventSourcing.Domain; 2 | 3 | public abstract class EventSourcedEntity 4 | { 5 | private readonly EventPlayer _player = new(); 6 | private readonly EventRecorder _recorder = new(); 7 | 8 | public void RestoreFromEvents(object[] events) 9 | { 10 | if (_recorder.HasRecordedEvents) 11 | { 12 | throw new InvalidOperationException("Restoring from events can not be done on an instance with recorded events."); 13 | } 14 | 15 | foreach (var @event in events) 16 | { 17 | _player.Play(@event); 18 | } 19 | } 20 | 21 | /// 22 | /// 23 | /// 24 | /// 25 | public object[] TakeEvents() 26 | { 27 | var recorded = _recorder.RecordedEvents; 28 | _recorder.Reset(); 29 | return recorded; 30 | } 31 | 32 | protected void On(Action handler) => _player.Register(handler); 33 | 34 | protected void Apply(object @event) 35 | { 36 | _player.Play(@event); 37 | _recorder.Record(@event); 38 | } 39 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain/EventSourcedEntityEntry.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.EventSourcing.Domain; 2 | 3 | public class EventSourcedEntityEntry(StreamName stream, int expectedVersion, EventSourcedEntity entity) 4 | { 5 | public StreamName Stream { get; } = stream; 6 | 7 | public int ExpectedVersion { get; } = expectedVersion; 8 | 9 | public EventSourcedEntity Entity { get; } = entity ?? throw new ArgumentNullException(nameof(entity)); 10 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain/EventSourcing.Domain.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(AssemblyName) 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain/EventSourcing.Domain.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain/IEventSourcedEntityChangeTracker.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.EventSourcing.Domain; 2 | 3 | public interface IEventSourcedEntityChangeTracker 4 | { 5 | bool TryGetEntry(StreamName stream, out EventSourcedEntityEntry entry); 6 | 7 | void TrackEntry(EventSourcedEntityEntry entry); 8 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain/MessageNameResolver.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.EventSourcing.Domain; 2 | 3 | public delegate string MessageNameResolver(Type type); -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain/MessageTypeResolver.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.EventSourcing.Domain; 2 | 3 | public delegate Type MessageTypeResolver(string name); -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain/NamespaceBasedGuidGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | 3 | namespace Logicality.EventSourcing.Domain; 4 | 5 | public class NamespaceBasedGuidGenerator 6 | { 7 | private static readonly Tuple[] ByteOrderPairsToSwap = [ 8 | Tuple.Create(0, 3), 9 | Tuple.Create(1, 2), 10 | Tuple.Create(4, 5), 11 | Tuple.Create(6, 7) 12 | ]; 13 | 14 | private readonly byte[] _namespace; 15 | 16 | public NamespaceBasedGuidGenerator(Guid @namespace) 17 | { 18 | _namespace = @namespace.ToByteArray(); 19 | SwapPairs(_namespace, ByteOrderPairsToSwap); 20 | } 21 | 22 | public Guid Create(byte[] input) 23 | { 24 | using var algorithm = SHA1.Create(); 25 | algorithm.TransformBlock(_namespace, 0, _namespace.Length, null, 0); 26 | algorithm.TransformFinalBlock(input, 0, input.Length); 27 | var hash = algorithm.Hash!; 28 | 29 | var buffer = new byte[16]; 30 | Array.Copy(hash, 0, buffer, 0, 16); 31 | 32 | buffer[6] = (byte)((buffer[6] & 0x0F) | (5 << 4)); 33 | buffer[8] = (byte)((buffer[8] & 0x3F) | 0x80); 34 | 35 | SwapPairs(buffer, ByteOrderPairsToSwap); 36 | return new Guid(buffer); 37 | } 38 | 39 | private static void SwapPairs(IList buffer, IEnumerable> pairs) 40 | { 41 | foreach (var (left, right) in pairs) 42 | { 43 | (buffer[left], buffer[right]) = (buffer[right], buffer[left]); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain/ResolveMessageName.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.EventSourcing.Domain; 2 | 3 | public static class ResolveMessageName 4 | { 5 | public static MessageNameResolver WhenEqualToTypeName(IEnumerable types) 6 | { 7 | if (types == null) 8 | { 9 | throw new ArgumentNullException(nameof(types)); 10 | } 11 | 12 | var cache = types.ToDictionary(type => type, type => type.Name); 13 | 14 | return type => 15 | { 16 | if (type == null) 17 | { 18 | throw new ArgumentNullException(nameof(type)); 19 | } 20 | 21 | if (!cache.TryGetValue(type, out var name)) 22 | { 23 | throw new InvalidOperationException($"The name for message typed as {type.FullName} could not be found."); 24 | } 25 | 26 | return name; 27 | }; 28 | } 29 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain/ResolveMessageType.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.EventSourcing.Domain; 2 | 3 | public static class ResolveMessageType 4 | { 5 | private static void WithSupersededTypes(this IDictionary cache) 6 | { 7 | } 8 | 9 | public static MessageTypeResolver WhenEqualToTypeName(IEnumerable types) 10 | { 11 | if (types == null) 12 | { 13 | throw new ArgumentNullException(nameof(types)); 14 | } 15 | 16 | var cache = types.ToDictionary(type => type.Name); 17 | cache.WithSupersededTypes(); 18 | 19 | return name => 20 | { 21 | if (!cache.TryGetValue(name, out var type)) 22 | { 23 | throw new InvalidOperationException($"The type for message named {name} could not be found."); 24 | } 25 | 26 | return type; 27 | }; 28 | } 29 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/EventSourcing.Domain/StreamName.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.EventSourcing.Domain; 2 | 3 | public record StreamName(string Value) 4 | { 5 | public override string ToString() => Value; 6 | } -------------------------------------------------------------------------------- /libs/eventsourcing-domain/src/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/logicality-io/libs/af5b790d0d01372d07b5823478eaa7a15a2afd0b/libs/eventsourcing-domain/src/icon.png -------------------------------------------------------------------------------- /libs/eventsourcing-domain/tests/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /libs/eventsourcing-domain/tests/EventSourcing.Domain.Tests/EventSourcing.Domain.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(Org).$(AssemblyName) 5 | $(Org).EventSourcing.Domain 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /libs/eventsourcing-domain/tests/EventSourcing.Domain.Tests/EventSourcing.Domain.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/github/readme.md: -------------------------------------------------------------------------------- 1 | # Logicality GitHub Libraries 2 | 3 | A set of libraries to help working with GitHub 4 | 5 | 1. [Logicality.GitHub.Action.Workflow](src/Actions.Workflow/README.md) - A 6 | fluent GitHub actions workflow builder. 7 | 2. [Logicality.GitHub.Action.Workflow.Extensions](src/Actions.Workflow.Extensions/README.md) - A 8 | set of GitHub Actions workflow builder extensions, common jobs and steps. -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow.Extensions/Actions.Workflow.Extensions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | enable 5 | enable 6 | Logicality.GitHub.Actions.Workflow.Extensions 7 | Logicality.GitHub.Actions.Workflow 8 | README.md 9 | A collection of extensions and common steps and jobs for GitHub Actions Workflow library. 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow.Extensions/Actions.Workflow.Extensions.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow.Extensions/README.md: -------------------------------------------------------------------------------- 1 | # GitHub Actions Workflow Extensions 2 | 3 | ![Nuget](https://img.shields.io/nuget/v/Logicality.GitHub.Actions.Workflow.Extensions?label=Logicality.GitHub.Actions.Workflow.Extensions&style=flat-square) 4 | 5 | ## Introduction 6 | 7 | A collection of extensions and common steps/jobs for GitHub Actions Workflow 8 | library. 9 | -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/Actions.Workflow.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | enable 5 | Logicality.GitHub.Actions.Workflow 6 | Logicality.GitHub.Actions.Workflow 7 | README.md 8 | A GitHub Actions Workflow fluent builder library. 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/Actions.Workflow.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/Event.cs: -------------------------------------------------------------------------------- 1 | using YamlDotNet.Core.Events; 2 | using YamlDotNet.RepresentationModel; 3 | 4 | namespace Logicality.GitHub.Actions.Workflow; 5 | 6 | public class EventTrigger : Trigger 7 | { 8 | private string[] _types = Array.Empty(); 9 | 10 | internal EventTrigger( 11 | string eventName, 12 | On @on, 13 | Workflow workflow) 14 | : base(eventName, @on, workflow) 15 | { 16 | } 17 | 18 | public EventTrigger Types(params string[] types) 19 | { 20 | _types = types; 21 | return this; 22 | } 23 | 24 | internal override void Build(YamlMappingNode parent, SequenceStyle sequenceStyle) 25 | { 26 | var yamlMappingNode = new YamlMappingNode(); 27 | 28 | if (_types.Any()) 29 | { 30 | var sequence = new YamlSequenceNode(_types.Select(s => new YamlScalarNode(s))) 31 | { 32 | Style = sequenceStyle 33 | }; 34 | yamlMappingNode.Add("types", sequence); 35 | } 36 | 37 | parent.Add(EventName, yamlMappingNode); 38 | } 39 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/GitHubHostedRunner.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | public static class GitHubHostedRunners 4 | { 5 | public static string MacOSLatest = "macos-latest"; 6 | public static string MacOS15 = "macos-15"; 7 | public static string MacOS14 = "macos-14"; 8 | public static string UbuntuLatest = "ubuntu-latest"; 9 | public static string Ubuntu24_04 = "ubuntu-24.04"; 10 | public static string Ubuntu24_04_ARM = "ubuntu-24.04-arm"; 11 | public static string Ubuntu22_04 = "ubuntu-22.04"; 12 | public static string Ubuntu22_04_ARM = "ubuntu-22.04-arm"; 13 | public static string Ubuntu20_04 = "ubuntu-20.04"; 14 | public static string WindowsLatest = "windows-latest"; 15 | public static string Windows2025 = "windows-2025"; 16 | public static string Windows2022 = "windows-2022"; 17 | public static string Windows2019 = "windows-2019"; 18 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/JobContainerEnv.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | public class JobContainerEnv(JobContainer jobContainer, IDictionary map) 4 | : JobKeyValueMap(jobContainer.Job, "env", map) 5 | { 6 | public JobContainer JobContainer { get; } = jobContainer; 7 | } 8 | -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/JobDefaults.cs: -------------------------------------------------------------------------------- 1 | using YamlDotNet.RepresentationModel; 2 | 3 | namespace Logicality.GitHub.Actions.Workflow; 4 | 5 | public class JobDefaults : JobKeyValueMap 6 | { 7 | private string? _shell; 8 | private string? _workingDirectory; 9 | 10 | public JobDefaults(Job job) : base(job, "defaults") 11 | { 12 | } 13 | 14 | public JobDefaults(Job job, IDictionary map) 15 | : base(job, "defaults", map) 16 | { 17 | } 18 | 19 | /// 20 | /// Set default shell and working-directory to all run steps in the job. 21 | /// https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_iddefaultsrun 22 | /// 23 | /// 24 | /// 25 | /// 26 | public JobDefaults Run(string shell, string workingDirectory) 27 | { 28 | _shell = shell; 29 | _workingDirectory = workingDirectory; 30 | return this; 31 | } 32 | 33 | internal override void Build(YamlMappingNode yamlMappingNode) 34 | { 35 | if (Map.Any() || !string.IsNullOrWhiteSpace(_shell)) 36 | { 37 | var defaultsMappingNode = new YamlMappingNode(); 38 | foreach (var property in Map) 39 | { 40 | defaultsMappingNode.Add(property.Key, new YamlScalarNode(property.Value)); 41 | } 42 | // Defaults Run 43 | if (!string.IsNullOrWhiteSpace(_shell) && !string.IsNullOrWhiteSpace(_workingDirectory)) 44 | { 45 | var defaultsRunMappingNode = new YamlMappingNode 46 | { 47 | { "shell", _shell }, 48 | { "working-directory", _workingDirectory } 49 | }; 50 | defaultsMappingNode.Add("run", defaultsRunMappingNode); 51 | } 52 | yamlMappingNode.Add("defaults", defaultsMappingNode); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/JobEnv.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | public class JobEnv(Job job, IDictionary map) : JobKeyValueMap(job, "env", map); -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/JobKeyValueMap.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | public abstract class JobKeyValueMap : KeyValueMap where T : JobKeyValueMap 4 | { 5 | protected JobKeyValueMap(Job job, string nodeName) 6 | : base(nodeName) 7 | { 8 | Job = job; 9 | } 10 | 11 | protected JobKeyValueMap(Job job, string nodeName, IDictionary map) 12 | : base(nodeName, map) 13 | { 14 | Job = job; 15 | } 16 | 17 | /// 18 | /// The associated Job 19 | /// 20 | public Job Job { get; } 21 | 22 | /// 23 | /// The associated Workflow 24 | /// 25 | public Workflow Workflow => Job.Workflow; 26 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/JobOutputs.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | public class JobOutputs(Job job, IDictionary map) : JobKeyValueMap(job, "outputs", map); -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/JobSecrets.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | public class JobSecrets(Job job, IDictionary map) : JobKeyValueMap(job, "secrets", map); -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/JobServiceEnv.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | public class JobServiceEnv(JobService jobService, IDictionary map) 4 | : JobKeyValueMap(jobService.Job, "env", map) 5 | { 6 | public JobService Service { get; } = jobService; 7 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/JobServices.cs: -------------------------------------------------------------------------------- 1 | using YamlDotNet.Core.Events; 2 | using YamlDotNet.RepresentationModel; 3 | 4 | namespace Logicality.GitHub.Actions.Workflow; 5 | 6 | public class JobServices(Job job) 7 | { 8 | private readonly IDictionary _services = new Dictionary(); 9 | 10 | public Job Job { get; } = job; 11 | 12 | /// 13 | /// Used to host service containers for a job in a workflow. 14 | /// See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservices 15 | /// 16 | /// The service identifier. 17 | /// The Docker image to use as the service container to run the action. 18 | /// 19 | public JobService Service(string id, string image) 20 | { 21 | var service = new JobService(this, id, image); 22 | _services.Add(id, service); 23 | return service; 24 | } 25 | 26 | internal void Build(YamlMappingNode mappingNode, SequenceStyle sequenceStyle) 27 | { 28 | if (_services.Any()) 29 | { 30 | var servicesNode = new YamlMappingNode(); 31 | foreach (var jobService in _services) 32 | { 33 | jobService.Value.Build(servicesNode, sequenceStyle); 34 | } 35 | 36 | mappingNode.Add("services", servicesNode); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/JobWith.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | public class JobWith(Job job, IDictionary map) : JobKeyValueMap(job, "with", map); -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/KeyValueMap.cs: -------------------------------------------------------------------------------- 1 | using YamlDotNet.RepresentationModel; 2 | 3 | namespace Logicality.GitHub.Actions.Workflow; 4 | 5 | public abstract class KeyValueMap where T : KeyValueMap 6 | { 7 | private readonly string _nodeName; 8 | 9 | protected KeyValueMap(string nodeName) 10 | { 11 | _nodeName = nodeName; 12 | } 13 | 14 | protected KeyValueMap(string nodeName, IDictionary map) 15 | { 16 | _nodeName = nodeName; 17 | Map = map; 18 | } 19 | 20 | protected IDictionary Map { get; } = new Dictionary(); 21 | 22 | /// 23 | /// Adds a Key and Value. 24 | /// 25 | /// The key 26 | /// The value 27 | /// 28 | public T Key(string key, string value) 29 | { 30 | Map.Add(key, value); 31 | return (T)this; 32 | } 33 | 34 | internal virtual void Build(YamlMappingNode yamlMappingNode) 35 | { 36 | if (Map.Any()) 37 | { 38 | var mappingNode = new YamlMappingNode(); 39 | foreach (var property in Map) 40 | { 41 | mappingNode.Add(property.Key, property.Value); 42 | } 43 | 44 | yamlMappingNode.Add(_nodeName, mappingNode); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/MapExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | internal static class MapExtensions 4 | { 5 | internal static IDictionary ToDictionary(this (string Key, string Value)[] map) 6 | => map.ToDictionary(m => m.Key, m => m.Value); 7 | 8 | internal static IDictionary ToDictionary(this (string Key, string[] Values)[] map) 9 | => map.ToDictionary(m => m.Key, m => m.Values); 10 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/Matrix.cs: -------------------------------------------------------------------------------- 1 | using YamlDotNet.Core.Events; 2 | using YamlDotNet.RepresentationModel; 3 | 4 | namespace Logicality.GitHub.Actions.Workflow; 5 | 6 | public class Matrix 7 | { 8 | private readonly IDictionary _properties = new Dictionary(); 9 | 10 | public Matrix(Strategy strategy, IDictionary properties) 11 | { 12 | Strategy = strategy; 13 | _properties = properties; 14 | } 15 | 16 | /// 17 | /// Adds a Key and Values. 18 | /// 19 | /// The key 20 | /// The values 21 | /// 22 | public Matrix Key(string key, params string[] values) 23 | { 24 | _properties.Add(key, values); 25 | return this; 26 | } 27 | 28 | /// 29 | /// The associated Strategy 30 | /// 31 | public Strategy Strategy { get; } 32 | 33 | /// 34 | /// The associated Workflow 35 | /// 36 | public Workflow Workflow => Strategy.Workflow; 37 | 38 | /// 39 | /// The associated Job 40 | /// 41 | public Job Job => Strategy.Job; 42 | 43 | internal void Build(YamlMappingNode yamlMappingNode, SequenceStyle sequenceStyle) 44 | { 45 | if (_properties.Any()) 46 | { 47 | var matrixMappingNode = new YamlMappingNode(); 48 | foreach (var property in _properties) 49 | { 50 | var values = new YamlSequenceNode { Style = sequenceStyle }; 51 | foreach (var value in property.Value) 52 | { 53 | values.Add(value); 54 | } 55 | matrixMappingNode.Add(property.Key, values); 56 | } 57 | 58 | yamlMappingNode.Add("matrix", matrixMappingNode); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/Permission.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | /// 4 | /// A permission type. 5 | /// 6 | public enum Permission 7 | { 8 | Read, 9 | Write, 10 | None 11 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/PermissionConfig.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | internal enum PermissionConfig 4 | { 5 | NotSpecified, 6 | ReadAll, 7 | WriteAll, 8 | Custom 9 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/PermissionHelper.cs: -------------------------------------------------------------------------------- 1 | using YamlDotNet.RepresentationModel; 2 | 3 | namespace Logicality.GitHub.Actions.Workflow; 4 | 5 | internal static class PermissionHelper 6 | { 7 | internal static void BuildPermissionsNode( 8 | YamlMappingNode node, 9 | PermissionConfig permissionConfig, 10 | Dictionary permissions) 11 | { 12 | if (permissionConfig == PermissionConfig.NotSpecified) 13 | { 14 | return; 15 | } 16 | 17 | switch (permissionConfig) 18 | { 19 | case PermissionConfig.ReadAll: 20 | node.Add("permissions", "read-all"); 21 | break; 22 | case PermissionConfig.WriteAll: 23 | node.Add("permissions", "write-all"); 24 | break; 25 | case PermissionConfig.Custom: 26 | { 27 | var mappingNode = new YamlMappingNode(); 28 | foreach (var permission in permissions) 29 | { 30 | if (permission.Value != Permission.None) 31 | { 32 | mappingNode.Add(permission.Key, permission.Value.ToString().ToLower()); 33 | } 34 | } 35 | node.Add("permissions", mappingNode); 36 | break; 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/PermissionKeys.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | internal static class PermissionKeys 4 | { 5 | public const string Actions = "actions"; 6 | public const string Checks = "checks"; 7 | public const string Contents = "contents"; 8 | public const string Deployments = "deployments"; 9 | public const string IdToken = "id-token"; 10 | public const string Issues = "issues"; 11 | public const string Discussions = "discussions"; 12 | public const string Packages = "packages"; 13 | public const string Pages = "pages"; 14 | public const string PullRequests = "pull-requests"; 15 | public const string RepositoryProjects = "repository-projects"; 16 | public const string SecurityEvents = "security-events"; 17 | public const string Statuses = "statuses"; 18 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/ScheduleTrigger.cs: -------------------------------------------------------------------------------- 1 | using YamlDotNet.Core; 2 | using YamlDotNet.Core.Events; 3 | using YamlDotNet.RepresentationModel; 4 | 5 | namespace Logicality.GitHub.Actions.Workflow; 6 | 7 | public class ScheduleTrigger( 8 | string eventName, 9 | string[] crons, 10 | On on, 11 | Workflow workflow) 12 | : Trigger(eventName, on, workflow) 13 | { 14 | internal override void Build(YamlMappingNode parent, SequenceStyle sequenceStyle) 15 | { 16 | var sequence = new YamlSequenceNode 17 | { 18 | Style = sequenceStyle 19 | }; 20 | foreach (var cron in crons) 21 | { 22 | sequence.Add(new YamlMappingNode("cron", new YamlScalarNode(cron){ Style = ScalarStyle.SingleQuoted})); 23 | } 24 | 25 | parent.Add("schedule", sequence); 26 | } 27 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/Shells.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | /// 4 | /// Some constants for Shells used in Steps 5 | /// 6 | public static class Shells 7 | { 8 | /// 9 | /// "pwsh" 10 | /// 11 | public const string Pwsh = "pwsh"; 12 | 13 | /// 14 | /// "bash" 15 | /// 16 | public const string Bash = "bash"; 17 | 18 | /// 19 | /// "python" 20 | /// 21 | public const string Python = "python"; 22 | 23 | /// 24 | /// "cmd" 25 | /// 26 | public const string Cmd = "cmd"; 27 | 28 | /// 29 | /// "powershell" 30 | /// 31 | public const string Powershell = "powershell"; 32 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/StepEnv.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | public class StepEnv(Step step, IDictionary map) : StepKeyValueMap(step, "env", map); -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/StepKeyValueMap.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | public abstract class StepKeyValueMap : KeyValueMap where T : StepKeyValueMap 4 | { 5 | protected StepKeyValueMap(Step step, string nodeName) 6 | : base(nodeName) 7 | { 8 | Step = step; 9 | } 10 | 11 | protected StepKeyValueMap(Step step, string nodeName, IDictionary map) 12 | : base(nodeName, map) 13 | { 14 | Step = step; 15 | } 16 | 17 | /// 18 | /// The associated Step 19 | /// 20 | public Step Step { get; } 21 | 22 | /// 23 | /// The associated Job 24 | /// 25 | public Job Job => Step.Job; 26 | 27 | /// 28 | /// The associated Workflow 29 | /// 30 | public Workflow Workflow => Job.Workflow; 31 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/StepWith.cs: -------------------------------------------------------------------------------- 1 | using YamlDotNet.Core; 2 | using YamlDotNet.Core.Tokens; 3 | using YamlDotNet.RepresentationModel; 4 | 5 | namespace Logicality.GitHub.Actions.Workflow; 6 | 7 | public class StepWith(Step step, IDictionary map) 8 | { 9 | /// 10 | /// The associated Step 11 | /// 12 | public Step Step { get; } = step; 13 | 14 | /// 15 | /// The associated Job 16 | /// 17 | public Job Job => Step.Job; 18 | 19 | /// 20 | /// The associated Workflow 21 | /// 22 | public Workflow Workflow => Job.Workflow; 23 | 24 | internal void Build(YamlMappingNode yamlMappingNode) 25 | { 26 | if (map.Any()) 27 | { 28 | var mappingNode = new YamlMappingNode(); 29 | foreach (var property in map) 30 | { 31 | if (property.Value.Contains(Environment.NewLine)) 32 | { 33 | var yamlScalarNode = new YamlScalarNode(property.Value) 34 | { 35 | Style = ScalarStyle.Literal 36 | }; 37 | mappingNode.Add(property.Key, yamlScalarNode); 38 | } 39 | else 40 | { 41 | mappingNode.Add(property.Key, property.Value); 42 | } 43 | } 44 | 45 | yamlMappingNode.Add("with", mappingNode); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/Trigger.cs: -------------------------------------------------------------------------------- 1 | using YamlDotNet.Core.Events; 2 | using YamlDotNet.RepresentationModel; 3 | 4 | namespace Logicality.GitHub.Actions.Workflow; 5 | 6 | public abstract class Trigger(string eventName, On on, Workflow workflow) 7 | { 8 | public string EventName { get; } = eventName; 9 | public On On { get; } = on; 10 | public Workflow Workflow { get; } = workflow; 11 | 12 | internal abstract void Build(YamlMappingNode parent, SequenceStyle sequenceStyle); 13 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/WorkflowCallType.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | /// 4 | /// Workflow call types. 5 | /// 6 | public enum WorkflowCallType 7 | { 8 | Boolean, 9 | Number, 10 | String 11 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/WorkflowDefaults.cs: -------------------------------------------------------------------------------- 1 | using YamlDotNet.RepresentationModel; 2 | 3 | namespace Logicality.GitHub.Actions.Workflow; 4 | 5 | /// 6 | /// Represents a the defaults for a workflow. 7 | /// 8 | public class WorkflowDefaults : WorkflowKeyValueMap 9 | { 10 | private string? _shell; 11 | private string? _workingDirectory; 12 | 13 | public WorkflowDefaults(Workflow workflow) : base(workflow, "defaults") 14 | { 15 | } 16 | 17 | public WorkflowDefaults(Workflow workflow, IDictionary map) 18 | : base(workflow, "defaults", map) 19 | { 20 | } 21 | 22 | /// 23 | /// You can use defaults.run to provide default shell and working-directory options for all run steps in a workflow. 24 | /// 25 | /// The shell. 26 | /// The working directory. 27 | /// The workflow defauls. 28 | public WorkflowDefaults Run(string shell, string workingDirectory) 29 | { 30 | _shell = shell; 31 | _workingDirectory = workingDirectory; 32 | return this; 33 | } 34 | 35 | internal override void Build(YamlMappingNode yamlMappingNode) 36 | { 37 | if (Map.Any() || !string.IsNullOrWhiteSpace(_shell)) 38 | { 39 | var defaultsMappingNode = new YamlMappingNode(); 40 | foreach (var property in Map) 41 | { 42 | defaultsMappingNode.Add(property.Key, new YamlScalarNode(property.Value)); 43 | } 44 | // Defaults Run 45 | if (!string.IsNullOrWhiteSpace(_shell) && !string.IsNullOrWhiteSpace(_workingDirectory)) 46 | { 47 | var defaultsRunMappingNode = new YamlMappingNode 48 | { 49 | { "shell", _shell }, 50 | { "working-directory", _workingDirectory } 51 | }; 52 | defaultsMappingNode.Add("run", defaultsRunMappingNode); 53 | } 54 | yamlMappingNode.Add("defaults", defaultsMappingNode); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /libs/github/src/Actions.Workflow/WorkflowKeyValueMap.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | public abstract class WorkflowKeyValueMap : KeyValueMap where T : WorkflowKeyValueMap 4 | { 5 | protected WorkflowKeyValueMap(Workflow workflow, string nodeName) 6 | : base(nodeName) 7 | { 8 | Workflow = workflow; 9 | } 10 | 11 | protected WorkflowKeyValueMap(Workflow workflow, string nodeName, IDictionary map) 12 | : base(nodeName, map) 13 | { 14 | Workflow = workflow; 15 | } 16 | 17 | /// 18 | /// The associated Workflow 19 | /// 20 | public Workflow Workflow { get; } 21 | } -------------------------------------------------------------------------------- /libs/github/src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | github- 8 | 0.1 9 | true 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /libs/github/tests/Actions.Workflow.Tests/Actions.Workflow.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | enable 5 | false 6 | Logicality.GitHub.Actions.Workflow.Tests 7 | Logicality.GitHub.Actions.Workflow 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /libs/github/tests/Actions.Workflow.Tests/Actions.Workflow.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/github/tests/Actions.Workflow.Tests/Example.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.GitHub.Actions.Workflow; 2 | 3 | public class Example 4 | { 5 | public void ReadMeExample() 6 | { 7 | var workflow = new Workflow("my-workflow"); 8 | 9 | workflow.On 10 | .Push() 11 | .Branches("main"); 12 | 13 | 14 | var buildJob = workflow 15 | .Job("build") 16 | .RunsOn(GitHubHostedRunners.UbuntuLatest); 17 | 18 | buildJob.Step().ActionsCheckout(); 19 | 20 | buildJob.Step() 21 | .Name("Build") 22 | .Run("./build.ps1") 23 | .Shell(Shells.Pwsh); 24 | 25 | var fileName = "my-workflow.yaml"; 26 | var filePath = $"../workflows/{fileName}"; 27 | workflow.WriteYaml(filePath); 28 | } 29 | } -------------------------------------------------------------------------------- /libs/github/tests/Actions.Workflow.Tests/Extensions/StepExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Logicality.GitHub.Actions.Workflow.Extensions 8 | { 9 | public class StepExtensions 10 | { 11 | [Fact] 12 | public void ActionsDotnetSetupTests() 13 | { 14 | var actual = new Workflow("workflow") 15 | .Job("build") 16 | .Step() 17 | .ActionsSetupDotNet(["8.0.x", "9.0.x"]) 18 | .Workflow 19 | .GetYaml(); 20 | 21 | var expected = Workflow.Header + @" 22 | 23 | name: workflow 24 | jobs: 25 | build: 26 | steps: 27 | - name: Setup Dotnet 28 | uses: actions/setup-dotnet@v4 29 | with: 30 | dotnet-version: |- 31 | 8.0.x 32 | 9.0.x 33 | "; 34 | 35 | actual.ShouldBe(expected.ReplaceLineEndings()); ; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /libs/github/tests/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /libs/hosting/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/logicality-io/libs/af5b790d0d01372d07b5823478eaa7a15a2afd0b/libs/hosting/console.png -------------------------------------------------------------------------------- /libs/hosting/src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | hosting- 8 | 0.1 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /libs/hosting/src/Hosting.Docker/Extensions.Hosting.Docker.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | A hosted service to run docker containers using FluentDocker. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /libs/hosting/src/Hosting.Docker/Extensions.Hosting.Docker.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/hosting/src/Hosting.Example/AdminWebAppHostedService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore; 2 | using Serilog; 3 | 4 | namespace Logicality.Extensions.Hosting.Example; 5 | 6 | public class AdminWebAppHostedService(HostedServiceContext context) : IHostedService 7 | { 8 | private IWebHost? _webHost; 9 | public const int Port = 5001; 10 | 11 | public Task StartAsync(CancellationToken cancellationToken) 12 | { 13 | var config = new ConfigurationBuilder() 14 | .AddInMemoryCollection(new Dictionary 15 | { 16 | { "SeqUri", context.Seq.SinkUri.ToString() } 17 | }) 18 | .Build(); 19 | 20 | _webHost = WebHost 21 | .CreateDefaultBuilder([]) 22 | .UseUrls($"http://+:{Port}") 23 | .UseConfiguration(config) 24 | .Build(); 25 | 26 | context.AdminWebApp = this; 27 | 28 | return _webHost.StartAsync(cancellationToken); 29 | } 30 | 31 | public Task StopAsync(CancellationToken cancellationToken) 32 | => _webHost!.StopAsync(cancellationToken); 33 | 34 | public class Startup(IConfiguration configuration) 35 | { 36 | public void ConfigureServices(IServiceCollection services) 37 | { 38 | services.AddLogging(logging => 39 | { 40 | var seqUri = configuration.GetValue("SeqUri")!; 41 | var logger = new LoggerConfiguration() 42 | .WriteTo.Seq(seqUri) 43 | .CreateLogger(); 44 | logging.AddSerilog(logger); 45 | }); 46 | } 47 | 48 | public void Configure(IApplicationBuilder app) 49 | { 50 | app.Run(context 51 | => context.Response.WriteAsync("Hello AdminWebApp")); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /libs/hosting/src/Hosting.Example/Extensions.Hosting.Example.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /libs/hosting/src/Hosting.Example/Extensions.Hosting.Example.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/hosting/src/Hosting.Example/HostedServiceContext.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.Extensions.Hosting.Example; 2 | 3 | /// 4 | /// This class holds references to hosted service. As hosted services start, they register themselves 5 | /// in this class. This is to facilitate using generated configuration values which may not be 6 | /// known until *after* a hosted service has started to be retrievable by other hosted services. 7 | /// 8 | /// For example, services that log to Seq will need to start after Seq has started and determined 9 | /// the port number it is listening on. 10 | /// 11 | public class HostedServiceContext 12 | { 13 | private readonly Dictionary _hostedServices = new(); 14 | 15 | public MySqlHostedService MySql 16 | { 17 | get => Get(nameof(MySql)); 18 | set => Add(nameof(MySqlHostedService), value); 19 | } 20 | 21 | public SeqHostedService Seq 22 | { 23 | get => Get(nameof(Seq)); 24 | set => Add(nameof(Seq), value); 25 | } 26 | 27 | public MainWebAppHostedService MainWebApp 28 | { 29 | get => Get(nameof(MainWebApp)); 30 | set => Add(nameof(MainWebAppHostedService), value); 31 | } 32 | 33 | public AdminWebAppHostedService AdminWebApp 34 | { 35 | get => Get(nameof(AdminWebApp)); 36 | set => Add(nameof(AdminWebAppHostedService), value); 37 | } 38 | 39 | private void Add(string name, IHostedService hostedService) 40 | { 41 | lock (_hostedServices) 42 | { 43 | _hostedServices.Add(name, hostedService); 44 | } 45 | } 46 | 47 | private T Get(string name) where T : IHostedService 48 | { 49 | lock (_hostedServices) 50 | { 51 | if (!_hostedServices.TryGetValue(name, out var value)) 52 | { 53 | throw new InvalidOperationException($"HostedService {name} was not found. Check the hosted services registration sequence."); 54 | } 55 | return (T)value; 56 | 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /libs/hosting/src/Hosting.Example/MainWebAppHostedService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore; 2 | using Serilog; 3 | 4 | namespace Logicality.Extensions.Hosting.Example; 5 | 6 | public class MainWebAppHostedService(HostedServiceContext context) : IHostedService 7 | { 8 | private IWebHost? _webHost; 9 | public const int Port = 5000; 10 | 11 | public Task StartAsync(CancellationToken cancellationToken) 12 | { 13 | var config = new ConfigurationBuilder() 14 | .AddInMemoryCollection(new Dictionary 15 | { 16 | { "SeqUri", context.Seq.SinkUri.ToString() } 17 | }) 18 | .Build(); 19 | 20 | _webHost = WebHost 21 | .CreateDefaultBuilder([]) 22 | .UseUrls($"http://+:{Port}") 23 | .UseConfiguration(config) 24 | .Build(); 25 | 26 | context.MainWebApp = this; 27 | 28 | return _webHost.StartAsync(cancellationToken); 29 | } 30 | 31 | public Task StopAsync(CancellationToken cancellationToken) 32 | => _webHost!.StopAsync(cancellationToken); 33 | 34 | public class Startup(IConfiguration configuration) 35 | { 36 | public void ConfigureServices(IServiceCollection services) 37 | { 38 | services.AddLogging(logging => 39 | { 40 | var seqUri = configuration.GetValue("SeqUri")!; 41 | var logger = new LoggerConfiguration() 42 | .WriteTo.Seq(seqUri) 43 | .CreateLogger(); 44 | logging.AddSerilog(logger); 45 | }); 46 | } 47 | 48 | public void Configure(IApplicationBuilder app) 49 | { 50 | app.Run(context 51 | => context.Response.WriteAsync("Hello MainWebApp")); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /libs/hosting/src/Hosting.Example/Program.cs: -------------------------------------------------------------------------------- 1 | using Serilog; 2 | using Serilog.Events; 3 | 4 | namespace Logicality.Extensions.Hosting.Example; 5 | 6 | internal class Program 7 | { 8 | private static async Task Main(string[] args) 9 | { 10 | await CreateHostBuilder(args) 11 | .Build() 12 | .RunAsync(); 13 | } 14 | 15 | private static IHostBuilder CreateHostBuilder(string[] args) 16 | { 17 | var loggerConfiguration = new LoggerConfiguration() 18 | .MinimumLevel.Debug() 19 | .MinimumLevel.Override("Microsoft", LogEventLevel.Error) 20 | .Enrich.FromLogContext() 21 | .WriteTo.Logger(l => 22 | { 23 | l.WriteHostedServiceMessagesToConsole(); 24 | }); 25 | 26 | var logger = loggerConfiguration.CreateLogger(); 27 | 28 | var context = new HostedServiceContext(); 29 | 30 | return new HostBuilder() 31 | .UseConsoleLifetime() 32 | .ConfigureServices(services => 33 | { 34 | services.AddSingleton(context); 35 | services.AddTransient(); 36 | 37 | services.AddSequentialHostedServices("root", r => r 38 | .Host() 39 | .Host() 40 | .HostParallel("web-apps", 41 | p => p 42 | .Host() 43 | .Host())); 44 | }) 45 | .UseSerilog(logger); 46 | } 47 | } -------------------------------------------------------------------------------- /libs/hosting/src/Hosting.Example/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "project": { 4 | "commandName": "Project" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /libs/hosting/src/Hosting.Example/SeqHostedService.cs: -------------------------------------------------------------------------------- 1 | using Ductus.FluentDocker.Builders; 2 | using Ductus.FluentDocker.Services; 3 | 4 | namespace Logicality.Extensions.Hosting.Example; 5 | 6 | public class SeqHostedService( 7 | HostedServiceContext context, 8 | ILogger logger) 9 | : DockerHostedService(logger) 10 | { 11 | private const int Port = 5010; 12 | private const int ContainerPort = 80; 13 | 14 | protected override string ContainerName => "extensions-seq"; 15 | 16 | public Uri SinkUri { get; } = new($"http://localhost:{Port}"); 17 | 18 | protected override IContainerService CreateContainerService() 19 | => new Builder() 20 | .UseContainer() 21 | .WithName(ContainerName) 22 | .UseImage("datalust/seq:latest") 23 | .ReuseIfExists() 24 | .ExposePort(Port, ContainerPort) 25 | .WithEnvironment("ACCEPT_EULA=Y") 26 | .WaitForPort($"{ContainerPort}/tcp", 5000, "127.0.0.1") 27 | .Build(); 28 | 29 | public override async Task StartAsync(CancellationToken cancellationToken) 30 | { 31 | await base.StartAsync(cancellationToken); 32 | context.Seq = this; 33 | } 34 | } -------------------------------------------------------------------------------- /libs/hosting/src/Hosting.SerilogConsoleLogging/Extensions.Hosting.SerilogConsoleLogging.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /libs/hosting/src/Hosting.SerilogConsoleLogging/Extensions.Hosting.SerilogConsoleLogging.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/hosting/src/Hosting.SerilogConsoleLogging/LoggerConfigurationExtensions.cs: -------------------------------------------------------------------------------- 1 | using Serilog; 2 | using Serilog.Filters; 3 | using Serilog.Sinks.SystemConsole.Themes; 4 | 5 | namespace Logicality.Extensions.Hosting; 6 | 7 | public static class LoggerConfigurationExtensions 8 | { 9 | /// 10 | /// Write HostedService log messages to console with in a nice format. Intended to be used by DevServers. 11 | /// 12 | /// 13 | /// The Serilog LoggerConfiguration 14 | /// 15 | /// 16 | /// The Serilog LoggerConfiguration 17 | /// 18 | public static LoggerConfiguration WriteHostedServiceMessagesToConsole(this LoggerConfiguration configuration) 19 | { 20 | configuration 21 | .Filter 22 | .ByIncludingOnly(e => Matching.FromSource(typeof(HostedServiceWrapper).FullName!)(e)) 23 | .WriteTo 24 | .Console( 25 | outputTemplate: 26 | "[{Timestamp:HH:mm:ss} {Level:u3}] [{HostedService}] {Message}{NewLine}{Exception}", 27 | theme: AnsiConsoleTheme.Code); 28 | return configuration; 29 | } 30 | } -------------------------------------------------------------------------------- /libs/hosting/src/Hosting/Extensions.Hosting.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | A collection of extensions over Microsoft.Extentions.Configuration 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /libs/hosting/src/Hosting/Extensions.Hosting.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/hosting/src/Hosting/HostedServiceBag.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Hosting; 2 | 3 | namespace Logicality.Extensions.Hosting; 4 | 5 | /// 6 | /// Represents a collection of hosted services keyed by name. Used in context 7 | /// that will prevent duplicate service being started and nice error messages if 8 | /// there is an attempt to access a hosted service that has not yet started. 9 | /// 10 | public class HostedServiceBag 11 | { 12 | private readonly Dictionary _hostedServices = new Dictionary(); 13 | 14 | /// 15 | /// Adds a hosted service 16 | /// 17 | /// 18 | /// 19 | public void Add(string name, IHostedService hostedService) 20 | { 21 | if (string.IsNullOrWhiteSpace(name)) 22 | { 23 | throw new ArgumentException("Value cannot be null or whitespace.", nameof(name)); 24 | } 25 | 26 | _ = hostedService ?? throw new ArgumentNullException(nameof(hostedService)); 27 | 28 | _hostedServices.Add(name, hostedService); 29 | } 30 | 31 | public T Get(string name) where T : IHostedService 32 | { 33 | if (!_hostedServices.TryGetValue(name, out var value)) 34 | { 35 | throw new InvalidOperationException($"HostedService {name} was not found. Perhaps it hasn't been started yet?"); 36 | } 37 | 38 | return (T)value; 39 | } 40 | } -------------------------------------------------------------------------------- /libs/hosting/src/Hosting/ParallelHostedServices.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Hosting; 2 | 3 | namespace Logicality.Extensions.Hosting; 4 | 5 | /// 6 | /// An that contains child hosted services and starts 7 | /// them in parallel. Useful for a set of hosted services that are not dependent on 8 | /// each other resulting in higher performing startup (on multi-core machines). 9 | /// 10 | public class ParallelHostedServices(params HostedServiceWrapper[] hostedServices) : IHostedService 11 | { 12 | private readonly IReadOnlyCollection _hostedServices = hostedServices ?? throw new ArgumentNullException(nameof(hostedServices)); 13 | 14 | internal void SetParent(HostedServiceWrapper parent) 15 | { 16 | foreach (var hostedService in _hostedServices) 17 | { 18 | hostedService.Parent = parent; 19 | } 20 | } 21 | 22 | public async Task StartAsync(CancellationToken cancellationToken) => 23 | await Task 24 | .WhenAll(_hostedServices 25 | .Select(hostedService => hostedService.StartAsync(cancellationToken))); 26 | 27 | public Task StopAsync(CancellationToken cancellationToken) 28 | { 29 | var tasks = _hostedServices 30 | .Select(hostedService => hostedService.StopAsync(cancellationToken)); 31 | return Task.WhenAll(tasks); 32 | } 33 | } -------------------------------------------------------------------------------- /libs/hosting/src/Hosting/SequentialHostedServices.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Hosting; 2 | 3 | namespace Logicality.Extensions.Hosting; 4 | 5 | /// 6 | /// An that contains child hosted services, starting them sequential 7 | /// in the order that they are supplied. This is important for services that may need to 8 | /// explicitly start before services. 9 | /// 10 | public class SequentialHostedServices(params HostedServiceWrapper[] hostedServices) : IHostedService 11 | { 12 | private readonly IReadOnlyCollection _hostedServices = hostedServices ?? throw new ArgumentNullException(nameof(hostedServices)); 13 | 14 | internal void SetParent(HostedServiceWrapper parent) 15 | { 16 | foreach (var hostedService in _hostedServices) 17 | { 18 | hostedService.Parent = parent; 19 | } 20 | } 21 | 22 | public async Task StartAsync(CancellationToken cancellationToken) 23 | { 24 | foreach (var hostedService in _hostedServices) 25 | { 26 | await hostedService.StartAsync(cancellationToken); 27 | } 28 | } 29 | 30 | public Task StopAsync(CancellationToken cancellationToken) 31 | { 32 | var tasks = _hostedServices 33 | .Select(hostedService => hostedService.StopAsync(cancellationToken)); 34 | return Task.WhenAll(tasks); 35 | } 36 | } -------------------------------------------------------------------------------- /libs/hosting/src/Hosting/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Logicality.Extensions.Hosting; 2 | 3 | // ReSharper disable once CheckNamespace 4 | namespace Microsoft.Extensions.DependencyInjection; 5 | 6 | public static class ServiceCollectionExtensions 7 | { 8 | /// 9 | /// Adds a set of hosted services that are started in the order they are registered. 10 | /// 11 | /// The service collection. 12 | /// A friendly name that represents the collection of hosted services. Used in log output. 13 | /// Configure the set of child hosted services. 14 | /// 15 | public static IServiceCollection AddSequentialHostedServices( 16 | this IServiceCollection services, 17 | string name, 18 | Action configure) 19 | { 20 | var builder = new SequentialHostedServiceBuilder(services, name); 21 | configure(builder); 22 | services.AddHostedService(sp => builder.Build(sp)); 23 | return services; 24 | } 25 | 26 | /// 27 | /// Adds a set of hosted services that are started in parallel. 28 | /// 29 | /// The service collection. 30 | /// A friendly name that represents the collection of hosted services. Used in log output. 31 | /// Configure the set of child hosted services. 32 | public static IServiceCollection AddParallelHostedServices( 33 | this IServiceCollection services, 34 | string name, 35 | Action configure) 36 | { 37 | var builder = new ParallelHostedServiceBuilder(services, name); 38 | configure(builder); 39 | services.AddHostedService(sp => builder.Build(sp)); 40 | return services; 41 | } 42 | } -------------------------------------------------------------------------------- /libs/hosting/tests/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /libs/hosting/tests/Hosting.Tests/ContainerHostedServiceTests.cs: -------------------------------------------------------------------------------- 1 | using Ductus.FluentDocker.Builders; 2 | using Ductus.FluentDocker.Services; 3 | using Logicality.SystemExtensions.Net.Sockets; 4 | using Microsoft.Extensions.Logging; 5 | using Xunit; 6 | using Xunit.Abstractions; 7 | 8 | namespace Logicality.Extensions.Hosting; 9 | 10 | public class ContainerHostedServiceTests(ITestOutputHelper outputHelper) 11 | { 12 | private readonly ILoggerFactory _loggerFactory = new LoggerFactory().AddXUnit(outputHelper); 13 | 14 | [Fact] 15 | public async Task Can_start_and_start_container() 16 | { 17 | var hostedService = new HelloWorldHostedService(_loggerFactory.CreateLogger()); 18 | 19 | await hostedService.StartAsync(CancellationToken.None); 20 | 21 | await hostedService.StopAsync(CancellationToken.None); 22 | } 23 | 24 | [Fact] 25 | public async Task Should_use_existing_container() 26 | { 27 | var hostedService1 = new HelloWorldHostedService(_loggerFactory.CreateLogger()); 28 | await hostedService1.StartAsync(CancellationToken.None); 29 | 30 | var hostedService2 = new HelloWorldHostedService(_loggerFactory.CreateLogger()); 31 | await hostedService2.StartAsync(CancellationToken.None); 32 | 33 | await hostedService1.StopAsync(CancellationToken.None); 34 | await hostedService2.StopAsync(CancellationToken.None); 35 | } 36 | 37 | public class HelloWorldHostedService(ILogger logger) : DockerHostedService(logger) 38 | { 39 | protected override string ContainerName { get; } = $"hello-world-{Guid.NewGuid():N}"; 40 | 41 | protected override IContainerService CreateContainerService() 42 | { 43 | var port = PortFinder.GetNext(); 44 | 45 | return new Builder() 46 | .UseContainer() 47 | .WithName(ContainerName) 48 | .UseImage("hello-world") 49 | .ReuseIfExists() 50 | .ExposePort(port, 80) 51 | .WithEnvironment("ACCEPT_EULA=Y") 52 | .Build(); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /libs/hosting/tests/Hosting.Tests/Context.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.Extensions.Hosting; 2 | 3 | public class Context 4 | { 5 | private int _counter; 6 | 7 | public int Counter => _counter; 8 | 9 | public void Increment() => Interlocked.Increment(ref _counter); 10 | } -------------------------------------------------------------------------------- /libs/hosting/tests/Hosting.Tests/ExampleHostedService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Hosting; 2 | 3 | namespace Logicality.Extensions.Hosting; 4 | 5 | public class ExampleHostedService(Context context) : IHostedService 6 | { 7 | public bool OnStartCalled { get; set; } 8 | 9 | public bool OnStopCalled { get; set; } 10 | 11 | public Task StartAsync(CancellationToken cancellationToken) 12 | { 13 | OnStartCalled = true; 14 | context.Increment(); 15 | return Task.CompletedTask; 16 | } 17 | 18 | public Task StopAsync(CancellationToken cancellationToken) 19 | { 20 | OnStopCalled = true; 21 | return Task.CompletedTask; 22 | } 23 | } -------------------------------------------------------------------------------- /libs/hosting/tests/Hosting.Tests/Extensions.Hosting.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Logicality.Extensions.Hosting.Tests 5 | Logicality.Extensions.Hosting 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /libs/hosting/tests/Hosting.Tests/Extensions.Hosting.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/hosting/tests/Hosting.Tests/SequentialAndParallelHostedServiceTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Hosting; 3 | using Microsoft.Extensions.Logging; 4 | using Shouldly; 5 | using Xunit; 6 | using Xunit.Abstractions; 7 | 8 | namespace Logicality.Extensions.Hosting; 9 | 10 | public class SequentialAndParallelHostedServiceTests(ITestOutputHelper outputHelper) 11 | { 12 | [Fact] 13 | public async Task Can_register_sequential_and_parallel_hosted_services() 14 | { 15 | var context = new Context(); 16 | 17 | var services = new ServiceCollection(); 18 | services.AddSingleton(context); 19 | services.AddLogging(configure => configure.AddXUnit(outputHelper)); 20 | // A bit convoluted but it covers all the registration paths. 21 | services.AddSequentialHostedServices("sequential", 22 | s => s 23 | .Host() 24 | .HostSequential("sequential-2", 25 | s2 => s2 26 | .Host() 27 | .Host()) 28 | .HostParallel("parallel", 29 | p => p 30 | .HostParallel("parallel-2", 31 | p2 => p2 32 | .Host()) 33 | .HostSequential("sequential-3", 34 | s3 => s3 35 | .Host() 36 | .Host()))); 37 | services.AddParallelHostedServices("parallel-4", 38 | p4 => p4.Host()); 39 | 40 | var serviceProvider = services.BuildServiceProvider(); 41 | 42 | var hostedServices = serviceProvider 43 | .GetServices() 44 | .ToArray(); 45 | 46 | foreach (var hostedService in hostedServices) 47 | { 48 | await hostedService.StartAsync(CancellationToken.None); 49 | } 50 | 51 | foreach (var hostedService in hostedServices) 52 | { 53 | await hostedService.StopAsync(CancellationToken.None); 54 | } 55 | 56 | context.Counter.ShouldBe(6); 57 | } 58 | } -------------------------------------------------------------------------------- /libs/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/logicality-io/libs/af5b790d0d01372d07b5823478eaa7a15a2afd0b/libs/icon.png -------------------------------------------------------------------------------- /libs/lambda/src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | lambda- 8 | 0.1 9 | Latest 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.ClientExtensions/Lambda.ClientExtensions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Extensions over IAmazonLambda client. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.ClientExtensions/Lambda.ClientExtensions.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.ClientExtensions/LambdaClientExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using Amazon.Lambda.Model; 3 | 4 | // ReSharper disable once CheckNamespace 5 | namespace Amazon.Lambda; 6 | 7 | public static class LambdaClientExtensions 8 | { 9 | /// 10 | /// Serializes a payloadObject object and invokes it against the supplied function name. 11 | /// 12 | /// The payloadObject object 13 | /// 14 | /// 15 | /// 16 | /// 17 | public static Task InvokeRequestAsync(this IAmazonLambda client, string functionName, T payloadObject) 18 | { 19 | var payload = JsonSerializer.Serialize(payloadObject); 20 | var invokeRequest = new InvokeRequest 21 | { 22 | FunctionName = functionName, 23 | InvocationType = InvocationType.RequestResponse, 24 | Payload = payload 25 | }; 26 | return client.InvokeAsync(invokeRequest); 27 | } 28 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.Example/ExampleAsynchronousInvokeFunction.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace Logicality.Lambda.Example; 6 | 7 | public class ExampleAsynchronousInvokeFunction : AsynchronousInvokeFunction 8 | { 9 | protected override void ConfigureConfiguration(IConfigurationBuilder configuration) 10 | { 11 | base.ConfigureConfiguration(configuration); 12 | } 13 | 14 | protected override void ConfigureLogging(ILoggingBuilder logging) 15 | { 16 | base.ConfigureLogging(logging); 17 | logging.SetMinimumLevel(LogLevel.Debug); 18 | } 19 | 20 | protected override void ConfigureServices(IServiceCollection services) 21 | { 22 | base.ConfigureServices(services); 23 | services.AddHttpClient(); 24 | } 25 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.Example/ExampleAsynchronousInvokeHandler.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | using Microsoft.Extensions.Options; 3 | 4 | namespace Logicality.Lambda.Example; 5 | 6 | public class ExampleAsynchronousInvokeHandler( 7 | IHttpClientFactory clientFactory, 8 | IOptionsSnapshot optionsSnapshot) 9 | : AsynchronousInvokeHandler(optionsSnapshot) 10 | { 11 | public override async Task Handle(Request request, ILambdaContext context) 12 | { 13 | var httpClient = clientFactory.CreateClient(); 14 | httpClient.Timeout = TimeSpan.FromSeconds(Options.Timeout); 15 | await httpClient.GetAsync(request.Url); 16 | } 17 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.Example/ExampleOptions.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | using Amazon.Lambda.Serialization.SystemTextJson; 3 | 4 | [assembly: LambdaSerializer(typeof(DefaultLambdaJsonSerializer))] 5 | 6 | namespace Logicality.Lambda.Example; 7 | 8 | public class ExampleOptions 9 | { 10 | public int Timeout { get; set; } = 30; 11 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.Example/ExampleSynchronousInvokeFunction.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace Logicality.Lambda.Example; 6 | 7 | public class ExampleSynchronousInvokeFunction: SynchronousInvokeFunction 8 | { 9 | protected override void ConfigureConfiguration(IConfigurationBuilder configuration) 10 | { 11 | base.ConfigureConfiguration(configuration); 12 | } 13 | 14 | protected override void ConfigureLogging(ILoggingBuilder logging) 15 | { 16 | base.ConfigureLogging(logging); 17 | logging.SetMinimumLevel(LogLevel.Debug); 18 | } 19 | 20 | protected override void ConfigureServices(IServiceCollection services) 21 | { 22 | base.ConfigureServices(services); 23 | services.AddHttpClient(); 24 | } 25 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.Example/ExampleSynchronousInvokeHandler.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | using Microsoft.Extensions.Options; 3 | 4 | namespace Logicality.Lambda.Example; 5 | 6 | public class ExampleSynchronousInvokeHandler( 7 | IHttpClientFactory clientFactory, 8 | IOptionsSnapshot optionsSnapshot) 9 | : SynchronousInvokeHandler(optionsSnapshot) 10 | { 11 | public override async Task Handle(Request request, ILambdaContext context) 12 | { 13 | var httpClient = clientFactory.CreateClient(); 14 | httpClient.Timeout = TimeSpan.FromSeconds(Options.Timeout); 15 | var response = await httpClient.GetAsync(request.Url); 16 | var body = await response.Content.ReadAsStringAsync(); 17 | return new Response(body); 18 | } 19 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.Example/Lambda.Example.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | Lambda 5 | false 6 | 7 | 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.Example/Lambda.Example.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.Example/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Mock Lambda Test Tool": { 4 | "commandName": "Executable", 5 | "commandLineArgs": "--port 5050", 6 | "workingDirectory": ".\\bin\\$(Configuration)\\net6.0", 7 | "executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.Example/Request.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.Lambda.Example; 2 | 3 | public record Request(string Url); -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.Example/Response.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.Lambda.Example; 2 | 3 | public record Response(string Body); -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.Example/aws-lambda-tools-defaults.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "Information" : [ 4 | "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", 5 | "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", 6 | "dotnet lambda help", 7 | "All the command line options for the Lambda command can be specified in this file." 8 | ], 9 | "profile" : "ops-admincli", 10 | "region" : "eu-west-1", 11 | "configuration" : "Release", 12 | "framework" : "netcoreapp3.1", 13 | "function-runtime" : "dotnetcore3.1", 14 | "function-memory-size" : 1024, 15 | "function-timeout" : 30, 16 | "function-handler" : "Logicality.Lambda.Example::Logicality.Lambda.Example.ExampleFunction::Handle", 17 | "function-name" : "lambda-example", 18 | "package-type" : "Zip", 19 | "function-role" : "arn:aws:iam::421818286845:role/lambda_exec_lambda-example", 20 | "tracing-mode" : "PassThrough", 21 | "environment-variables" : "", 22 | "image-tag" : "", 23 | "function-description" : "" 24 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.TestHost/DefaultLambdaFunctionActivator.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.Lambda.TestHost; 2 | 3 | /// 4 | /// Activates a LambdaFunction instance using default paramaterless constructor. 5 | /// 6 | public class DefaultLambdaFunctionActivator(Type type) : ILambdaFunctionActivator 7 | { 8 | public object Activate() => Activator.CreateInstance(type)!; 9 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.TestHost/DelegateLambdaFunctionActivator.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.Lambda.TestHost; 2 | 3 | internal class DelegateLambdaFunctionActivator(Func activate) : ILambdaFunctionActivator 4 | { 5 | public object Activate() => activate(); 6 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.TestHost/ILambdaFunctionActivator.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.Lambda.TestHost; 2 | 3 | public interface ILambdaFunctionActivator 4 | { 5 | object Activate(); 6 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.TestHost/ILambdaFunctionInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Amazon.Lambda.Core; 3 | 4 | namespace Logicality.Lambda.TestHost; 5 | 6 | public interface ILambdaFunctionInfo 7 | { 8 | MethodInfo HandlerMethod { get; } 9 | 10 | int ReservedConcurrency { get; } 11 | 12 | ILambdaFunctionActivator FunctionActivator { get; } 13 | 14 | ILambdaSerializer? Serializer { get; } 15 | 16 | string Name { get; } 17 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.TestHost/Lambda.TestHost.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | A mechanism to host and test one or more lambda functions behind an implementation of the InvokeApi 5 | 6 | 7 | 8 | 9 | <_Parameter1>$(AssemblyName).Tests 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.TestHost/Lambda.TestHost.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.TestHost/LambdaAccountPool.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.Lambda.TestHost; 2 | 3 | internal class LambdaAccountPool 4 | { 5 | private readonly uint _accountConcurrencyLimit; 6 | private int _counter; 7 | 8 | private readonly Dictionary _instancePools = new(StringComparer.OrdinalIgnoreCase); 9 | 10 | public LambdaAccountPool( 11 | uint accountConcurrencyLimit, 12 | IReadOnlyDictionary lambdaFunctionInfos) 13 | { 14 | _accountConcurrencyLimit = accountConcurrencyLimit; 15 | 16 | foreach (var lambdaFunctionInfo in lambdaFunctionInfos) 17 | { 18 | _instancePools.Add(lambdaFunctionInfo.Key, new LambdaInstancePool(lambdaFunctionInfo.Value)); 19 | } 20 | } 21 | 22 | public LambdaInstance? Get(string functionName) 23 | { 24 | var c = Interlocked.Increment(ref _counter); 25 | if (c > _accountConcurrencyLimit) 26 | { 27 | Interlocked.Decrement(ref _counter); 28 | return null; 29 | } 30 | 31 | return _instancePools.TryGetValue(functionName, out var item) 32 | ? item.Get() 33 | : null; 34 | } 35 | 36 | public void Return(LambdaInstance lambdaInstance) 37 | { 38 | Interlocked.Decrement(ref _counter); 39 | _instancePools[lambdaInstance.LambdaFunction.Name].Return(lambdaInstance); 40 | } 41 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.TestHost/LambdaInstance.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.Lambda.TestHost; 2 | 3 | internal class LambdaInstance(ILambdaFunctionInfo lambdaFunction) 4 | { 5 | public Guid InstanceId { get; } = Guid.NewGuid(); 6 | 7 | public ILambdaFunctionInfo LambdaFunction { get; } = lambdaFunction; 8 | 9 | public object FunctionInstance { get; } = lambdaFunction.FunctionActivator.Activate(); 10 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.TestHost/LambdaInstancePool.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | 3 | namespace Logicality.Lambda.TestHost; 4 | 5 | internal class LambdaInstancePool(ILambdaFunctionInfo lambdaFunctionInfo) 6 | { 7 | private readonly ConcurrentQueue _availableInstances = new(); 8 | private readonly Dictionary _usedInstances = new(); 9 | private int _counter; 10 | 11 | public LambdaInstance? Get() 12 | { 13 | lock (_availableInstances) 14 | { 15 | if (_availableInstances.TryDequeue(out var result)) 16 | { 17 | _usedInstances.Add(result.InstanceId, result); 18 | return result; 19 | } 20 | 21 | if (_counter >= lambdaFunctionInfo.ReservedConcurrency) 22 | { 23 | return null; 24 | } 25 | 26 | var instance = new LambdaInstance(lambdaFunctionInfo); 27 | _counter++; 28 | _usedInstances.Add(instance.InstanceId, result!); 29 | return instance; 30 | } 31 | } 32 | 33 | public void Return(LambdaInstance lambdaInstance) 34 | { 35 | lock (_availableInstances) 36 | { 37 | _usedInstances.Remove(lambdaInstance.InstanceId); 38 | _availableInstances.Enqueue(lambdaInstance); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.TestHost/LambdaTestHostSettings.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace Logicality.Lambda.TestHost; 5 | 6 | public class LambdaTestHostSettings(Func createContext) 7 | { 8 | private readonly Dictionary _functions = new(); 9 | 10 | /// 11 | /// The URL the lambda test host will listen on. Default value is http://127.0.0.1:0 12 | /// which will listen on a random free port. To get the URL to invoke lambdas, use 13 | /// LambdaTestHost.ServiceUrl. 14 | /// 15 | public string WebHostUrl { get; set; } = "http://*:0"; 16 | 17 | /// 18 | /// Gets or sets the maximum concurrency limit for all hosted lambdas. 19 | /// 20 | public uint AccountConcurrencyLimit { get; set; } = 1000; 21 | 22 | internal Func CreateContext { get; } = createContext; 23 | 24 | public Action ConfigureLogging { get; set; } = _ => { }; 25 | 26 | public IReadOnlyDictionary Functions => _functions; 27 | 28 | //Used in tests to signal the start of an invocation. 29 | internal AutoResetEvent InvocationOnStart => new(false); 30 | 31 | public void AddFunction(LambdaFunctionInfo lambdaFunctionInfo) 32 | { 33 | _functions.Add(lambdaFunctionInfo.Name, lambdaFunctionInfo); 34 | } 35 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.TestHost/TestLambdaContext.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | 3 | namespace Logicality.Lambda.TestHost; 4 | 5 | public class TestLambdaContext : ILambdaContext 6 | { 7 | public string? AwsRequestId { get; set; } 8 | 9 | public IClientContext? ClientContext { get; set; } 10 | 11 | public string? FunctionName { get; set; } 12 | 13 | public string? FunctionVersion { get; set; } 14 | 15 | public ICognitoIdentity? Identity { get; set; } 16 | 17 | public string? InvokedFunctionArn { get; set; } 18 | 19 | public ILambdaLogger? Logger { get; set; } 20 | 21 | public string? LogGroupName { get; set; } 22 | 23 | public string? LogStreamName { get; set; } 24 | 25 | public int MemoryLimitInMB { get; set; } 26 | 27 | public TimeSpan RemainingTime { get; set; } 28 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda.TestHost/TestLambdaLogger.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Amazon.Lambda.Core; 3 | 4 | namespace Logicality.Lambda.TestHost; 5 | 6 | public class TestLambdaLogger : ILambdaLogger 7 | { 8 | private readonly StringBuilder _logs = new(); 9 | 10 | public void Log(string message) => _logs.Append(message); 11 | 12 | public void LogLine(string message) => _logs.AppendLine(message); 13 | 14 | public string Logs => _logs.ToString(); 15 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda/AsynchronousInvokeFunction.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Logicality.Lambda 5 | { 6 | /// 7 | /// A base function that is invoked asynchronously (see https://docs.aws.amazon.com/lambda/latest/dg/invocation-async.html), 8 | /// that encapsulates some boiler plate code around configuration, logging and dependency injection. 9 | /// 10 | /// By default configuration is loaded from environment variables, appsettings.json and appsettings.{environment}.json 11 | /// 12 | /// The handler that will be activated to handle the request. 13 | /// The request type. 14 | /// The options type. 15 | public abstract class AsynchronousInvokeFunction : FunctionBase 16 | where THandler : class, IAsynchronousInvokeHandler where TOptions : class, new() 17 | { 18 | public Task Handle(TInput input, ILambdaContext context) 19 | { 20 | var serviceProvider = GetServiceProvider(); 21 | var handler = serviceProvider.GetRequiredService(); 22 | return handler.Handle(input, context); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda/AsynchronousInvokeHandler.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | using Microsoft.Extensions.Options; 3 | 4 | namespace Logicality.Lambda; 5 | 6 | /// 7 | /// A implementation of that has 8 | /// an injected. 9 | /// 10 | /// The type the handler will handle. 11 | /// The type of the options that will be injected via constructor. 12 | public abstract class AsynchronousInvokeHandler(IOptionsSnapshot optionsSnapshot) : 13 | IAsynchronousInvokeHandler 14 | where TOptions : class, new() 15 | { 16 | /// 17 | /// The options value. 18 | /// 19 | protected TOptions Options { get; } = optionsSnapshot.Value; 20 | 21 | /// 22 | /// The handler. 23 | /// 24 | /// 25 | /// 26 | /// 27 | public abstract Task Handle(TInput input, ILambdaContext context); 28 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda/IAsynchronousInvokeHandler.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | 3 | namespace Logicality.Lambda; 4 | 5 | /// 6 | /// Represents an lambda handler for that is asynchronously invoked (returns void). 7 | /// 8 | /// The request input type being handled. 9 | public interface IAsynchronousInvokeHandler 10 | { 11 | /// 12 | /// Handles the function request 13 | /// 14 | /// 15 | /// 16 | /// 17 | Task Handle(TInput input, ILambdaContext context); 18 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda/ISynchronousInvokeHandler.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | 3 | namespace Logicality.Lambda; 4 | 5 | /// 6 | /// Represents an lambda handler for that is synchronously invoked (returns a response object). 7 | /// 8 | /// 9 | /// 10 | public interface ISynchronousInvokeHandler 11 | { 12 | Task Handle(TInput input, ILambdaContext context); 13 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda/Lambda.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Micro-framework for AWS lambda functions that supports Dependecny Injection, Logging and Configuration 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /libs/lambda/src/Lambda/Lambda.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/lambda/src/Lambda/SynchronousInvokeFunction.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Logicality.Lambda; 5 | 6 | /// 7 | /// A base function the is invoked synchronously (see https://docs.aws.amazon.com/lambda/latest/dg/invocation-sync.html) 8 | /// that encapsulates some boiler plate code around configuration, logging and dependency injection. 9 | /// 10 | /// By default configuration is loaded from environment variables, appsettings.json and appsettings.{environment}.json 11 | /// 12 | /// The configuration object that configuration will be bound to. 13 | /// The handler that will be activated to handle the request. 14 | /// The input type. 15 | /// The response type. 16 | public abstract class SynchronousInvokeFunction: FunctionBase 17 | where TOptions : class, new() 18 | where THandler: class, ISynchronousInvokeHandler 19 | { 20 | public Task Handle(TInput input, ILambdaContext context) 21 | { 22 | var serviceProvider = GetServiceProvider(); 23 | var handler = serviceProvider.GetRequiredService(); 24 | return handler.Handle(input, context); 25 | } 26 | } -------------------------------------------------------------------------------- /libs/lambda/src/Lambda/SynchronousInvokeHandler.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | using Microsoft.Extensions.Options; 3 | 4 | namespace Logicality.Lambda 5 | { 6 | /// 7 | /// A implementation of that has 8 | /// an injected. 9 | /// 10 | public abstract class SynchronousInvokeHandler( 11 | IOptionsSnapshot optionsSnapshot) : ISynchronousInvokeHandler 12 | where TOptions : class, new() 13 | { 14 | protected TOptions Options { get; } = optionsSnapshot.Value; 15 | 16 | public abstract Task Handle(TInput input, ILambdaContext context); 17 | } 18 | } -------------------------------------------------------------------------------- /libs/lambda/tests/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /libs/lambda/tests/Lambda.TestHost.Tests/FixtureUtils.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Sockets; 3 | using Xunit.Abstractions; 4 | 5 | namespace Logicality.Lambda.TestHost; 6 | 7 | internal static class FixtureUtils 8 | { 9 | static FixtureUtils() 10 | { 11 | var env = Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER"); 12 | IsRunningInContainer = env != null && env.Equals("true", StringComparison.OrdinalIgnoreCase); 13 | } 14 | 15 | public static bool IsRunningInContainer { get; } 16 | 17 | public static string GetLocalIPAddress() 18 | { 19 | var host = Dns.GetHostEntry(Dns.GetHostName()); 20 | foreach (var ip in host.AddressList) 21 | { 22 | if (ip.AddressFamily == AddressFamily.InterNetwork) 23 | { 24 | return ip.ToString(); 25 | } 26 | } 27 | throw new Exception("No network adapters with an IPv4 address in the system!"); 28 | } 29 | 30 | public static string GetLambdaInvokeEndpoint(ITestOutputHelper outputHelper, LambdaTestHost lambdaTestHost) 31 | { 32 | var lambdaForwardUrlBuilder = new UriBuilder(lambdaTestHost.ServiceUrl); 33 | if (!IsRunningInContainer) 34 | { 35 | lambdaForwardUrlBuilder.Host = "host.docker.internal"; 36 | } 37 | 38 | var lambdaForwardUrl = lambdaForwardUrlBuilder.ToString(); 39 | // Remove trailing slash as localstack does string concatenation resulting in "//". 40 | lambdaForwardUrl = lambdaForwardUrl.Remove(lambdaForwardUrl.Length - 1); 41 | outputHelper.WriteLine($"Using LAMBDA_FALLBACK_URL={lambdaForwardUrl}"); 42 | return lambdaForwardUrl; 43 | } 44 | } -------------------------------------------------------------------------------- /libs/lambda/tests/Lambda.TestHost.Tests/Functions/APIGatewayFunction.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using Amazon.Lambda.APIGatewayEvents; 3 | using Amazon.Lambda.Core; 4 | using Amazon.Lambda.Serialization.SystemTextJson; 5 | 6 | namespace Logicality.Lambda.TestHost.Functions; 7 | 8 | public class APIGatewayFunction 9 | { 10 | [LambdaSerializer(typeof(DefaultLambdaJsonSerializer))] 11 | public APIGatewayProxyResponse Handle(APIGatewayProxyRequest apiGatewayProxyRequest, ILambdaContext context) 12 | { 13 | var response = new APIGatewayProxyResponse 14 | { 15 | StatusCode = (int)HttpStatusCode.OK, 16 | Body = $"Hello AWS Serverless{apiGatewayProxyRequest.Path}", 17 | Headers = new Dictionary 18 | { 19 | { "Content-Type", "text/plain" } 20 | } 21 | }; 22 | 23 | return response; 24 | } 25 | } -------------------------------------------------------------------------------- /libs/lambda/tests/Lambda.TestHost.Tests/Functions/BrokenFunction.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | using Amazon.Lambda.Serialization.SystemTextJson; 3 | 4 | namespace Logicality.Lambda.TestHost.Functions; 5 | 6 | public class BrokenFunction 7 | { 8 | [LambdaSerializer(typeof(DefaultLambdaJsonSerializer))] 9 | public void Handle(int sleep, ILambdaContext context) 10 | { 11 | throw new Exception(); 12 | } 13 | } -------------------------------------------------------------------------------- /libs/lambda/tests/Lambda.TestHost.Tests/Functions/ReverseStringFunction.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | using Amazon.Lambda.Serialization.SystemTextJson; 3 | 4 | namespace Logicality.Lambda.TestHost.Functions; 5 | 6 | public class ReverseStringFunction 7 | { 8 | [LambdaSerializer(typeof(DefaultLambdaJsonSerializer))] 9 | public string Reverse(string input, ILambdaContext context) 10 | { 11 | return new string(input.Reverse().ToArray()); 12 | } 13 | } -------------------------------------------------------------------------------- /libs/lambda/tests/Lambda.TestHost.Tests/Functions/SimpleLambdaFunction.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | using Amazon.Lambda.Serialization.SystemTextJson; 3 | 4 | namespace Logicality.Lambda.TestHost.Functions; 5 | 6 | public class SimpleLambdaFunction 7 | { 8 | [LambdaSerializer(typeof(DefaultLambdaJsonSerializer))] 9 | public SimpleResponse FunctionHandler(SimpleRequest request, ILambdaContext _) => new(request.Foo); 10 | 11 | public record SimpleRequest(string Foo); 12 | 13 | public record SimpleResponse(string Foo); 14 | } -------------------------------------------------------------------------------- /libs/lambda/tests/Lambda.TestHost.Tests/Functions/SleepFunction.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | using Amazon.Lambda.Serialization.SystemTextJson; 3 | 4 | namespace Logicality.Lambda.TestHost.Functions; 5 | 6 | public class SleepFunction 7 | { 8 | [LambdaSerializer(typeof(DefaultLambdaJsonSerializer))] 9 | public async Task Handle(int sleep, ILambdaContext context) 10 | { 11 | await Task.Delay(sleep); 12 | } 13 | } -------------------------------------------------------------------------------- /libs/lambda/tests/Lambda.TestHost.Tests/Lambda.TestHost.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Logicality.Lambda.TestHost.Tests 5 | Logicality.Lambda.TestHost 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /libs/lambda/tests/Lambda.TestHost.Tests/Lambda.TestHost.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/lambda/tests/Lambda.TestHost.Tests/LocalStack/SQSLambdaFunction.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | using Amazon.Lambda.Serialization.SystemTextJson; 3 | using Amazon.Lambda.SQSEvents; 4 | 5 | namespace Logicality.Lambda.TestHost.LocalStack; 6 | 7 | public class SQSLambdaFunction 8 | { 9 | [LambdaSerializer(typeof(DefaultLambdaJsonSerializer))] 10 | public Task FunctionHandler(SQSEvent request, ILambdaContext lambdaContext) 11 | { 12 | return Task.CompletedTask; 13 | } 14 | } -------------------------------------------------------------------------------- /libs/lambda/tests/Lambda.TestHost.Tests/SimpleLambdaFunction.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | using Amazon.Lambda.Serialization.SystemTextJson; 3 | 4 | namespace Logicality.Lambda.TestHost; 5 | 6 | public class SimpleLambdaFunction 7 | { 8 | [LambdaSerializer(typeof(DefaultLambdaJsonSerializer))] 9 | public SimpleResponse FunctionHandler(SimpleRequest request, ILambdaContext _) 10 | { 11 | var reverseData = new string(request.Data.Reverse().ToArray()); 12 | return new SimpleResponse(reverseData); 13 | } 14 | 15 | public record SimpleRequest(string Data); 16 | 17 | public record SimpleResponse(string Reverse); 18 | } -------------------------------------------------------------------------------- /libs/lambda/tests/Lambda.TestHost.Tests/XunitLambdaLogger.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.Core; 2 | using Xunit.Abstractions; 3 | 4 | namespace Logicality.Lambda.TestUtilities; 5 | 6 | public class XunitLambdaLogger(ITestOutputHelper outputHelper) : ILambdaLogger 7 | { 8 | public void Log(string message) => outputHelper.WriteLine(message); 9 | 10 | public void LogLine(string message) => outputHelper.WriteLine(message); 11 | } -------------------------------------------------------------------------------- /libs/lambda/tests/Lambda.Tests/AsynchronousInvokeFunctionTests.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.TestUtilities; 2 | using Logicality.Lambda.Example; 3 | using Xunit; 4 | 5 | namespace Logicality.Lambda 6 | { 7 | public class AsynchronousInvokeFunctionTests 8 | { 9 | [Fact] 10 | public async Task Can_activate_lambda() 11 | { 12 | var testFunction = new ExampleAsynchronousInvokeFunction(); 13 | 14 | await testFunction.Handle(new Request("http://example.com"), new TestLambdaContext()); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /libs/lambda/tests/Lambda.Tests/Lambda.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Logicality.Lambda.Tests 5 | Logicality.Lambda 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /libs/lambda/tests/Lambda.Tests/Lambda.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/lambda/tests/Lambda.Tests/SynchronousInvokeFunctionTests.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Lambda.TestUtilities; 2 | using Logicality.Lambda.Example; 3 | using Shouldly; 4 | using Xunit; 5 | 6 | namespace Logicality.Lambda; 7 | 8 | public class SynchronousInvokeFunctionTests 9 | { 10 | [Fact] 11 | public async Task Can_activate_lambda() 12 | { 13 | var testFunction = new ExampleSynchronousInvokeFunction(); 14 | 15 | var request = new Request("http://example.com"); 16 | var response = await testFunction.Handle(request, new TestLambdaContext()); 17 | 18 | response.Body.ShouldNotBeNullOrEmpty(); 19 | } 20 | } -------------------------------------------------------------------------------- /libs/pulumi/src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | pulumi- 8 | 0.1 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi.Automation/Aws/AwsConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Pulumi.Automation; 2 | 3 | namespace Logicality.Pulumi.Automation.Aws; 4 | 5 | public static class AwsConfiguration 6 | { 7 | /// 8 | /// Configuration the AWS provider to be usable for local deployment / development (i.e. 9 | /// dynamodb-local, localstack etc) 10 | /// 11 | /// 12 | public static void ConfigureForLocal(IDictionary configMap) 13 | { 14 | configMap.Add(AwsConfigurationKeys.Region, new ConfigValue("eu-west-1")); 15 | configMap.Add(AwsConfigurationKeys.AccessKey, new ConfigValue("ignored")); 16 | configMap.Add(AwsConfigurationKeys.SecretKey, new ConfigValue("ignored")); 17 | configMap.Add(AwsConfigurationKeys.SkipCredentialsValidation, new ConfigValue("true")); 18 | configMap.Add(AwsConfigurationKeys.SkipRequestingAccountId, new ConfigValue("true")); 19 | } 20 | } -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi.Automation/Aws/AwsConfigurationKeys.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.Pulumi.Automation.Aws; 2 | 3 | public static class AwsConfigurationKeys 4 | { 5 | public const string Region = "aws:region"; 6 | public const string AccessKey = "aws:accessKey"; 7 | public const string SecretKey = "aws:secretKey"; 8 | public const string SkipCredentialsValidation = "aws:skipCredentialsValidation"; 9 | public const string SkipRequestingAccountId = "aws:skipRequestingAccountId"; 10 | public const string Endpoints = "aws:endpoints"; 11 | } -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi.Automation/EnvironmentVariableKeys.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.Pulumi.Automation; 2 | 3 | public static class EnvironmentVariableKeys 4 | { 5 | public const string ConfigPassphrase = "PULUMI_CONFIG_PASSPHRASE"; 6 | public const string ConfigBackendUrl = "PULUMI_BACKEND_URL"; 7 | } -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi.Automation/LocalWorkspaceOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | using Pulumi.Automation; 2 | 3 | namespace Logicality.Pulumi.Automation; 4 | 5 | public static class LocalWorkspaceOptionsExtensions 6 | { 7 | /// 8 | /// Configures a for use with a local backend. Useful for portable 9 | /// integration testing. 10 | /// 11 | /// 12 | public static LocalWorkspaceOptions ConfigureForLocalBackend(this LocalWorkspaceOptions options) 13 | { 14 | options.EnvironmentVariables ??= new Dictionary(); 15 | options.EnvironmentVariables.Add(EnvironmentVariableKeys.ConfigPassphrase, "secret"); 16 | options.EnvironmentVariables.Add(EnvironmentVariableKeys.ConfigBackendUrl, "file://~"); 17 | return options; 18 | } 19 | } -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi.Automation/Pulumi.Automation.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi.Automation/Pulumi.Automation.net6.0.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | LostReference 5 | 6 | 7 | -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi.Automation/Pulumi.Automation.net7.0.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | LostReference 5 | 6 | 7 | -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi.Automation/Pulumi.Automation.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi.Automation/WorkspaceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using Pulumi; 3 | using Pulumi.Automation; 4 | 5 | namespace Logicality.Pulumi.Automation; 6 | 7 | public static class WorkspaceExtensions 8 | { 9 | /// 10 | /// Installs the correct version of the plug-in related to the TProvider type. 11 | /// 12 | /// The provider type. 13 | /// The workspace to install the plugin to. 14 | /// A cancellation token. 15 | public static async Task InstallPluginAsync(this Workspace workspace, CancellationToken cancellationToken = default) 16 | where TProvider: ProviderResource 17 | { 18 | var assembly = typeof(TProvider).Assembly; 19 | var fileVersionInfo = FileVersionInfo.GetVersionInfo(assembly.Location); 20 | var pluginName = assembly.GetName().Name!.Replace("Pulumi.", "").ToLower(); 21 | var pluginVersion = $"v{fileVersionInfo.FileMajorPart}.{fileVersionInfo.FileMinorPart}.{fileVersionInfo.FileBuildPart}"; 22 | 23 | await workspace.InstallPluginAsync(pluginName, pluginVersion, cancellationToken: cancellationToken); 24 | } 25 | } -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi.Aws/Ec2/StandardVpcArgs.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | 3 | namespace Logicality.Pulumi.Aws.Ec2; 4 | 5 | public class StandardVpcArgs 6 | { 7 | [Input("cidrBlockSegment")] 8 | public int CidrBlockSegment { get; set; } 9 | } -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi.Aws/Pulumi.Aws.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi.Aws/Pulumi.Aws.net6.0.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | LostReference 5 | 6 | 7 | -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi.Aws/Pulumi.Aws.net7.0.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | LostReference 5 | 6 | 7 | -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi.Aws/Pulumi.Aws.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi/Pulumi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi/Pulumi.net6.0.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | LostReference 5 | 6 | 7 | -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi/Pulumi.net7.0.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | LostReference 5 | 6 | 7 | -------------------------------------------------------------------------------- /libs/pulumi/src/Pulumi/Pulumi.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/pulumi/tests/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /libs/pulumi/tests/Pulumi.Aws.IntegrationTests/Pulumi.Aws.IntegrationTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | false 6 | Logicality.Pulumi.Aws.IntegrationTests 7 | Logicality.Pulumi.Aws 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /libs/pulumi/tests/Pulumi.Aws.IntegrationTests/Pulumi.Aws.IntegrationTests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/system-extensions/src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | system-extensions- 8 | 0.1 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /libs/system-extensions/src/SystemExtensions/AsyncDelegateDisposable.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.SystemExtensions; 2 | 3 | public class AsyncDelegateDisposable(Func onDispose) : IAsyncDisposable 4 | { 5 | public ValueTask DisposeAsync() => onDispose(); 6 | } -------------------------------------------------------------------------------- /libs/system-extensions/src/SystemExtensions/AsyncDisposableAction.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.SystemExtensions; 2 | 3 | /// 4 | /// An async disposable that invokes the supplied Action. 5 | /// 6 | public class AsyncDisposableAction(Func onDispose) : IAsyncDisposable 7 | { 8 | public ValueTask DisposeAsync() => onDispose(); 9 | } -------------------------------------------------------------------------------- /libs/system-extensions/src/SystemExtensions/DelegateDisposable.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.SystemExtensions; 2 | 3 | public class DelegateDisposable(Action onDispose) : IDisposable 4 | { 5 | public void Dispose() => onDispose(); 6 | } -------------------------------------------------------------------------------- /libs/system-extensions/src/SystemExtensions/DisposableAction.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.SystemExtensions; 2 | 3 | /// 4 | /// A disposable that invokes the supplied Action. 5 | /// 6 | public class DisposableAction(Action onDispose) : IDisposable 7 | { 8 | public void Dispose() => onDispose(); 9 | } -------------------------------------------------------------------------------- /libs/system-extensions/src/SystemExtensions/GetUtcNow.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.SystemExtensions; 2 | 3 | public delegate DateTimeOffset GetUtcNow(); 4 | -------------------------------------------------------------------------------- /libs/system-extensions/src/SystemExtensions/Net/Http/FileDownload.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.SystemExtensions.Net.Http; 2 | 3 | public static class FileDownload 4 | { 5 | public static async Task DownloadFile( 6 | this HttpMessageInvoker httpMessageInvoker, 7 | Uri source, 8 | string filePath, 9 | bool overwrite = true, 10 | CancellationToken cancellationToken = default) 11 | { 12 | if (File.Exists(filePath) && !overwrite) 13 | { 14 | return; 15 | } 16 | 17 | using var request = new HttpRequestMessage(HttpMethod.Get, source); 18 | var responseMessage = await httpMessageInvoker.SendAsync(request, cancellationToken); 19 | responseMessage.EnsureSuccessStatusCode(); 20 | await using var contentStream = await responseMessage.Content.ReadAsStreamAsync(cancellationToken); 21 | await using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None); 22 | await contentStream.CopyToAsync(fileStream, cancellationToken); 23 | } 24 | } -------------------------------------------------------------------------------- /libs/system-extensions/src/SystemExtensions/Net/Sockets/PortFinder.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Sockets; 3 | 4 | namespace Logicality.SystemExtensions.Net.Sockets; 5 | 6 | public static class PortFinder 7 | { 8 | /// 9 | /// Let the OS assign the next available port. The OS will always increment the port number 10 | /// when making these calls which mitigates against races in parallel invocations runs where a 11 | /// multiple consumers are trying to bind to a port. Note: this mitigates but does not totally 12 | /// eliminate port clashing. If it's possible to bind to port 0, then use that. 13 | /// 14 | /// 15 | public static int GetNext() 16 | { 17 | using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 18 | 19 | socket.Bind(new IPEndPoint(IPAddress.Loopback, 0)); 20 | return ((IPEndPoint)socket.LocalEndPoint!).Port; 21 | } 22 | } -------------------------------------------------------------------------------- /libs/system-extensions/src/SystemExtensions/SystemExtensions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | A collection of various extensions, helpers and utilities in the System.* namespace. 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /libs/system-extensions/src/SystemExtensions/SystemExtensions.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/system-extensions/src/SystemExtensions/Text/Json/SnakeCaseNamingPolicy.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace Logicality.SystemExtensions.Text.Json; 4 | 5 | public class SnakeCaseNamingPolicy : JsonNamingPolicy 6 | { 7 | public static SnakeCaseNamingPolicy Instance { get; } = new(); 8 | 9 | public override string ConvertName(string name) 10 | { 11 | return string.Concat(name.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x : x.ToString())).ToLower(); 12 | } 13 | } -------------------------------------------------------------------------------- /libs/system-extensions/src/SystemExtensions/Text/Json/StringUtils.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.SystemExtensions.Text.Json; 2 | 3 | internal static class StringUtils 4 | { 5 | public static string ToSnakeCase(this string str) 6 | { 7 | return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x : x.ToString())).ToLower(); 8 | } 9 | } -------------------------------------------------------------------------------- /libs/system-extensions/tests/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /libs/system-extensions/tests/SystemExtensions.Tests/DeterministicGuidFactoryTests.cs: -------------------------------------------------------------------------------- 1 | using Logicality.SystemExtensions; 2 | using Shouldly; 3 | using Xunit; 4 | 5 | namespace Logicality.System; 6 | 7 | public class DeterministicGuidFactoryTests 8 | { 9 | [Fact] 10 | public void When_same_input_and_same_namespace_then_should_create_same_guid() 11 | { 12 | var sut = new DeterministicGuidFactory(Guid.Parse("0E9C9913-06AF-4E24-A7CE-1AB5A06E35D0")); 13 | 14 | var guid1 = sut.Create("foo"); 15 | 16 | var guid2 = sut.Create("foo"); 17 | 18 | guid2.ShouldBe(guid1); 19 | } 20 | } -------------------------------------------------------------------------------- /libs/system-extensions/tests/SystemExtensions.Tests/Net/Http/FileDownloadTests.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using Xunit; 3 | 4 | namespace Logicality.SystemExtensions.Net.Http; 5 | 6 | public class DownloadFileTests 7 | { 8 | [Fact] 9 | public async Task Can_download_file() 10 | { 11 | var client = new HttpClient(); 12 | 13 | var sourceUri = new Uri("https://google.com/robots.txt"); 14 | var destinationPath = $"{Path.GetTempPath()}{Guid.NewGuid()}.txt"; 15 | await client.DownloadFile(sourceUri, destinationPath); 16 | 17 | File.Exists(destinationPath).ShouldBeTrue(); 18 | } 19 | } -------------------------------------------------------------------------------- /libs/system-extensions/tests/SystemExtensions.Tests/Net/Sockets/PortFinderTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using Logicality.SystemExtensions.Net.Sockets; 3 | using Shouldly; 4 | using Xunit; 5 | using Xunit.Abstractions; 6 | 7 | namespace Logicality.System.Net.Sockets; 8 | 9 | public class PortFinderTests(ITestOutputHelper outputHelper) 10 | { 11 | [Fact] 12 | public async Task Parallel_calls_should_all_get_different_ports() 13 | { 14 | var ports = new ConcurrentDictionary(); 15 | var tasks = new List(); 16 | for (var i = 0; i < 5; i++) 17 | { 18 | tasks.Add(Task.Run(() => 19 | { 20 | var port = PortFinder.GetNext(); 21 | ports.AddOrUpdate(port, _ => null!, (_, _) => null!); 22 | outputHelper.WriteLine(port.ToString()); 23 | })); 24 | } 25 | 26 | await Task.WhenAll(tasks); 27 | 28 | ports.Count.ShouldBe(5); 29 | } 30 | } -------------------------------------------------------------------------------- /libs/system-extensions/tests/SystemExtensions.Tests/SystemExtensions.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Logicality.System.Tests 5 | Logicality.System 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /libs/system-extensions/tests/SystemExtensions.Tests/SystemExtensions.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/test.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | $(NoWarn);1591 6 | latest 7 | full 8 | false 9 | true 10 | preview 11 | true 12 | enable 13 | 14 | 15 | 16 | 17 | all 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | 20 | 21 | 22 | 23 | 24 | 25 | all 26 | runtime; build; native; contentfiles; analyzers; buildtransitive 27 | 28 | 29 | 30 | 31 | 8.0.0 32 | 33 | 34 | -------------------------------------------------------------------------------- /libs/webhook-relay/WebhookRelay.slnf: -------------------------------------------------------------------------------- 1 | { 2 | "solution": { 3 | "path": "..\\..\\PlatformLibs.sln", 4 | "projects": [ 5 | "build\\Build.csproj", 6 | "libs\\webhook-relay\\src\\WebhookRelay\\WebhookRelay.csproj", 7 | "libs\\webhook-relay\\tests\\WebhookRelay.Tests\\WebhookRelay.Tests.csproj" 8 | ] 9 | } 10 | } -------------------------------------------------------------------------------- /libs/webhook-relay/readme.md: -------------------------------------------------------------------------------- 1 | # Webhook Relay Client 2 | 3 | A .NET client for the [Webhook Relay](https://webhookrelay.com) service. 4 | 5 | It creates a websocket connection, authenticates and subscribes to the specified buckets. 6 | 7 | Once the connection is established, it will start receiving messages and writing them to 8 | a channel. 9 | 10 | You can then read messages from the channel and process them however you like. 11 | 12 | If the connection is lost, the client will attempt to reconnect and resubscribe. 13 | 14 | When the client is disposed, it will close the connection and unsubscribe from the bucket(s). 15 | 16 | It is your responsibility to handle any remaining messages in the channel. 17 | 18 | ## Installation 19 | 20 | `dotnet add package Logicality.WebhookRelay.Client` 21 | 22 | ## Usage 23 | ```csharp 24 | 25 | ``` 26 | 27 | ## Development 28 | 29 | To run the tests you will need an account on [Webhook Relay](https://webhookrelay.com) and a bucket. Then 30 | you need to configure user secrets. 31 | 32 | ```bash 33 | cd 34 | dotnet user-secrets set "WebhookRelayTokenKey" "" --project test/WebhookRelay.Tests 35 | dotnet user-secrets set "WebhookRelayTokenSecret" "" --project test/WebhookRelay.Tests 36 | dotnet user-secrets set "WebhookUrl" "" --project test/WebhookRelay.Tests 37 | ``` -------------------------------------------------------------------------------- /libs/webhook-relay/src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | webhook-relay- 8 | 0.1 9 | 10 | 11 | -------------------------------------------------------------------------------- /libs/webhook-relay/src/WebhookRelay/ClientState.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.WebhookRelay; 2 | 3 | public enum ClientState 4 | { 5 | Disconnected, 6 | Connecting, 7 | Connected, 8 | Authenticating, 9 | Authenticated, 10 | Subscribing, 11 | Subscribed, 12 | HandlingMessages, 13 | Disposed, 14 | } -------------------------------------------------------------------------------- /libs/webhook-relay/src/WebhookRelay/ClientTrigger.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.WebhookRelay; 2 | 3 | internal enum ClientTrigger 4 | { 5 | Connect, 6 | ConnectCompleted, 7 | ConnectFailed, 8 | Authenticate, 9 | AuthenticateSucceeded, 10 | AuthenticationFailed, 11 | Subscribe, 12 | SubscribeCompleted, 13 | HandleMessages, 14 | ConnectionLost, 15 | Dispose 16 | } -------------------------------------------------------------------------------- /libs/webhook-relay/src/WebhookRelay/Metadata.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Logicality.WebhookRelay; 4 | 5 | public class Metadata 6 | { 7 | [JsonPropertyName("id")] 8 | public string Id { get; set; } = null!; 9 | 10 | [JsonPropertyName("bucket_id")] 11 | public string BucketId { get; set; } = null!; 12 | 13 | [JsonPropertyName("bucket_name")] 14 | public string BucketName { get; set; } = null!; 15 | 16 | [JsonPropertyName("input_id")] 17 | public string InputId { get; set; } = null!; 18 | 19 | [JsonPropertyName("input_name")] 20 | public string InputName { get; set; } = null!; 21 | 22 | [JsonPropertyName("output_name")] 23 | public string OutputName { get; set; } = null!; 24 | 25 | [JsonPropertyName("output_destination")] 26 | public string OutputDestination { get; set; } = null!; 27 | } -------------------------------------------------------------------------------- /libs/webhook-relay/src/WebhookRelay/WebhookMessage.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.WebhookRelay; 2 | 3 | public record WebhookMessage( 4 | Metadata Meta, 5 | Dictionary Headers, 6 | string Query, 7 | string Body, 8 | string Method); 9 | -------------------------------------------------------------------------------- /libs/webhook-relay/src/WebhookRelay/WebhookRelay.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | A .NET websocket client library for https://webhookrelay.io 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /libs/webhook-relay/src/WebhookRelay/WebhookRelay.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/webhook-relay/src/WebhookRelay/WebhookRelayClientConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.WebhookRelay; 2 | 3 | public class WebhookRelayClientConfiguration 4 | { 5 | public WebhookRelayClientConfiguration(string tokenKey, string tokenSecret, string[] buckets) 6 | { 7 | if (string.IsNullOrWhiteSpace(tokenKey)) 8 | { 9 | throw new ArgumentException("Value cannot be null or whitespace.", nameof(tokenKey)); 10 | } 11 | 12 | if (string.IsNullOrWhiteSpace(tokenSecret)) 13 | { 14 | throw new ArgumentException("Value cannot be null or whitespace.", nameof(tokenSecret)); 15 | } 16 | 17 | if (buckets.Length == 0) 18 | { 19 | throw new ArgumentException("Value cannot be an empty collection.", nameof(buckets)); 20 | } 21 | 22 | TokenKey = tokenKey; 23 | TokenSecret = tokenSecret; 24 | Buckets = buckets; 25 | } 26 | 27 | /// 28 | /// The token key to use for authentication. 29 | /// 30 | public string TokenKey { get; } 31 | 32 | /// 33 | /// The token secret to use for authentication. 34 | /// 35 | public string TokenSecret { get; } 36 | 37 | /// 38 | /// The buckets to subscribe to. 39 | /// 40 | public IReadOnlyCollection Buckets { get; } 41 | 42 | /// 43 | /// Uri of the Webhook Relay server. Defaults to https://my.webhookrelay.com/v1/socket 44 | /// 45 | public Uri Uri { get; } = new("wss://my.webhookrelay.com/v1/socket"); 46 | }; -------------------------------------------------------------------------------- /libs/webhook-relay/statemachine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/logicality-io/libs/af5b790d0d01372d07b5823478eaa7a15a2afd0b/libs/webhook-relay/statemachine.png -------------------------------------------------------------------------------- /libs/webhook-relay/tests/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /libs/webhook-relay/tests/WebhookRelay.Tests/Usings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; -------------------------------------------------------------------------------- /libs/webhook-relay/tests/WebhookRelay.Tests/WebhookRelay.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Logicality.WebhookRelay 5 | c627daf4-22f4-427d-818a-b86a57db54a2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /libs/webhook-relay/tests/WebhookRelay.Tests/WebhookRelay.Tests.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /libs/webhook-relay/tests/WebhookRelay.Tests/WebhookRelayClientExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Logicality.WebhookRelay; 2 | 3 | public static class WebhookRelayClientExtensions 4 | { 5 | public static async Task WaitForState(this WebhookRelayClient client, ClientState clientState, TimeSpan timeout) 6 | { 7 | var tcs = new TaskCompletionSource(); 8 | client.StateChanged += (_, state) => 9 | { 10 | if (state == ClientState.HandlingMessages) 11 | { 12 | tcs.SetResult(true); 13 | } 14 | }; 15 | 16 | if (await Task.WhenAny(tcs.Task, Task.Delay(timeout)) == tcs.Task) 17 | { 18 | return; 19 | } 20 | 21 | throw new TimeoutException(); 22 | } 23 | } -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Logicality Platform Libs 2 | 3 | A suite of .NET libraries used in a variety of Logicality applications and services. 4 | Managed in a mono-repo for convenience. Open source and shared under MIT for use, 5 | or better still, copying into your own projects. 6 | 7 | ## Libraries 8 | 9 | 1. [ASP.NET Core](/libs/aspnet-core): Extensions and helpers around ASP.NET Core. 10 | 1. [BullsEye](/libs/bullseye): Helpers functions for use in [BullsEye](https://github.com/adamralph/bullseye) 11 | based build programs. 12 | 1. [Configuration](/libs/configuration): Extensions to `Microsoft.Extensions.Configuration`. 13 | 1. [github](/libs/github): GitHub related utitlities including a fluent actions workflow builder. 14 | 1. [Hosting](/libs/hosting): Some utility Extensions to `Microsoft.Extensions.Hosting`. 15 | 1. [Lambda](/libs/lambda): Utilities, extensions and testing helpers for AWS lambda functions. 16 | 1. [Pulumi](/libs/pulumi): Custom component resources, extensions and helpers 17 | for `Pulumi` based infrastructure programs. 18 | 1. [System Extensions](/libs/system-extensions): Extensions to `System.*`. 19 | 1. [Testing](libs/testing/): Helpers, fixtures and other utilities to help with testing. 20 | -------------------------------------------------------------------------------- /statemachine.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: WebhookRelay Client 3 | --- 4 | stateDiagram-v2 5 | [*] --> Disconnected 6 | Disconnected --> Connecting : [T] Connect 7 | Connecting --> Connecting: Transient error, retry indefinitely (log warnings) 8 | Connecting --> ConnectingFailed: Non-transient-error. 9 | Connecting --> Disposed: DisposeAsync 10 | Connecting --> Connected 11 | Connected --> ConnectionLost 12 | ConnectionLost --> Connecting: Reconnect 13 | Connected --> Authenticating 14 | Authenticating --> Authenticated 15 | Authenticating --> AuthenticationFailed 16 | AuthenticationFailed --> [*] 17 | Authenticating --> ConnectionLost 18 | Authenticated --> HandlingMessages 19 | HandlingMessages --> ConnectionLost 20 | Connected --> Disposed : DisposeAsync 21 | Authenticating --> Disposed : DisposeAsync 22 | Authenticated --> Disposed : DisposeAsync 23 | HandlingMessages --> Disposed : DisposeAsync 24 | ConnectingFailed --> [*] 25 | Disposed --> [*] -------------------------------------------------------------------------------- /temp/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore --------------------------------------------------------------------------------