diff --git a/.dockerignore b/.dockerignore
index 415419ce..fe1152bd 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -21,4 +21,10 @@
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
-LICENSE
\ No newline at end of file
+LICENSE
+README.md
+!**/.gitignore
+!.git/HEAD
+!.git/config
+!.git/packed-refs
+!.git/refs/heads/**
\ No newline at end of file
diff --git a/.editorconfig b/.editorconfig
index c9a901a8..5b6c5012 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -98,4 +98,4 @@ csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
-file_header_template = Copyright © 2023-Present The Cloud Streams Authors\n\nLicensed under the Apache License, Version 2.0 (the "License"),\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an "AS IS" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.
\ No newline at end of file
+file_header_template = Copyright © 2024-Present The Cloud Streams Authors\n\nLicensed under the Apache License, Version 2.0 (the "License"),\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an "AS IS" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.
\ No newline at end of file
diff --git a/.github/workflows/build-dotnet.yml b/.github/workflows/build-dotnet.yml
index 3088574e..7019f886 100644
--- a/.github/workflows/build-dotnet.yml
+++ b/.github/workflows/build-dotnet.yml
@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- dotnet-version: ['7.0.x' ]
+ dotnet-version: ['8.0.x' ]
steps:
- name: Checkout
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 0098ecc4..52e0fc16 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -17,7 +17,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
- dotnet-version: 7.0.x
+ dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore "${{ env.SOLUTION }}"
- name: Build
@@ -25,8 +25,8 @@ jobs:
- name: Push1
run: dotnet nuget push "./src/*/*/bin/Release/*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
- publish-api-server-image:
- name: Publish API Server image
+ publish-api-image:
+ name: Publish API image
runs-on: ubuntu-latest
steps:
- name: Checkout repository
@@ -41,7 +41,7 @@ jobs:
id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
with:
- images: ${{ env.REGISTRY }}/${{ github.repository }}-api-server
+ images: ${{ env.REGISTRY }}/${{ github.repository }}-api
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
@@ -49,7 +49,7 @@ jobs:
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
with:
context: .
- file: './src/core/CloudStreams.Core.Api.Server/Dockerfile'
+ file: './src/core/CloudStreams.Core.Api/Dockerfile'
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
@@ -78,7 +78,7 @@ jobs:
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
with:
context: .
- file: './src/broker/CloudStreams.Broker.Api.Server/Dockerfile'
+ file: './src/broker/CloudStreams.Broker.Api/Dockerfile'
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
@@ -107,13 +107,13 @@ jobs:
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
with:
context: .
- file: './src/gateway/CloudStreams.Gateway.Api.Server/Dockerfile'
+ file: './src/gateway/CloudStreams.Gateway.Api/Dockerfile'
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- publish-api-server-bin:
- name: Publish API Server Binaries
+ publish-api-bin:
+ name: Publish API Binaries
strategy:
matrix:
kind: ['linux', 'windows', 'macOS']
@@ -134,7 +134,7 @@ jobs:
- name: Setup
uses: actions/setup-dotnet@v2
with:
- dotnet-version: 7.0.x
+ dotnet-version: 8.0.x
- name: Restore
run: dotnet restore
- name: Build
@@ -143,7 +143,7 @@ jobs:
tag=$(git describe --tags --abbrev=0)
release_name="cloud-streams-broker-${{ matrix.target }}"
# Publish
- dotnet publish src/core/CloudStreams.Core.Api.Server/CloudStreams.Core.Api.Server.csproj --runtime "${{ matrix.target }}" -c Release -o "$release_name"
+ dotnet publish src/core/CloudStreams.Core.Api/CloudStreams.Core.Api.csproj --runtime "${{ matrix.target }}" -c Release -o "$release_name"
# Pack
if [ "${{ matrix.target }}" == "win-x64" ]; then
# Pack for Windows
@@ -157,7 +157,7 @@ jobs:
- name: Publish
uses: softprops/action-gh-release@v1
with:
- files: "cloud-streams-api-server*"
+ files: "cloud-streams-api*"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -192,7 +192,7 @@ jobs:
tag=$(git describe --tags --abbrev=0)
release_name="cloud-streams-broker-${{ matrix.target }}"
# Publish
- dotnet publish src/broker/CloudStreams.Broker.Api.Server/CloudStreams.Broker.Api.Server.csproj --runtime "${{ matrix.target }}" -c Release -o "$release_name"
+ dotnet publish src/broker/CloudStreams.Broker.Api/CloudStreams.Broker.Api.csproj --runtime "${{ matrix.target }}" -c Release -o "$release_name"
# Pack
if [ "${{ matrix.target }}" == "win-x64" ]; then
# Pack for Windows
@@ -241,7 +241,7 @@ jobs:
tag=$(git describe --tags --abbrev=0)
release_name="cloud-streams-gateway-${{ matrix.target }}"
# Publish
- dotnet publish src/gateway/CloudStreams.Gateway.Api.Server/CloudStreams.Gateway.Api.Server.csproj --runtime "${{ matrix.target }}" -c Release -o "$release_name"
+ dotnet publish src/gateway/CloudStreams.Gateway.Api/CloudStreams.Gateway.Api.csproj --runtime "${{ matrix.target }}" -c Release -o "$release_name"
# Pack
if [ "${{ matrix.target }}" == "win-x64" ]; then
# Pack for Windows
diff --git a/.github/workflows/test-dotnet.yml b/.github/workflows/test-dotnet.yml
index e9a80c26..9f08d220 100644
--- a/.github/workflows/test-dotnet.yml
+++ b/.github/workflows/test-dotnet.yml
@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- dotnet-version: ['7.0.x' ]
+ dotnet-version: ['8.0.x' ]
steps:
- name: Checkout
diff --git a/.gitignore b/.gitignore
index 261c522e..8a30d258 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
-## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
@@ -23,6 +23,7 @@ mono_crash.*
[Rr]eleases/
x64/
x86/
+[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
@@ -61,6 +62,9 @@ project.lock.json
project.fragment.lock.json
artifacts/
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
# StyleCop
StyleCopReport.xml
@@ -86,6 +90,7 @@ StyleCopReport.xml
*.tmp_proj
*_wpftmp.csproj
*.log
+*.tlog
*.vspscc
*.vssscc
.builds
@@ -137,6 +142,11 @@ _TeamCity*
.axoCover/*
!.axoCover/settings.json
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
# Visual Studio code coverage results
*.coverage
*.coveragexml
@@ -284,6 +294,17 @@ node_modules/
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
+# Visual Studio 6 auto-generated project file (contains which files were open etc.)
+*.vbp
+
+# Visual Studio 6 workspace and project file (working project files containing files to include in project)
+*.dsw
+*.dsp
+
+# Visual Studio 6 technical files
+*.ncb
+*.aps
+
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
@@ -340,6 +361,9 @@ ASALocalRun/
# Local History for Visual Studio
.localhistory/
+# Visual Studio History (VSHistory) files
+.vshistory/
+
# BeatPulse healthcheck temp database
healthchecksdb
@@ -348,5 +372,27 @@ MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
-/deployments/docker-compose/docker-compose.yml
-/deployments/docker-compose/docker-compose.override.yml
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+
+# VS Code files for those working on multiple tools
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# Local History for Visual Studio Code
+.history/
+
+# Windows Installer files from build outputs
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# JetBrains Rider
+*.sln.iml
diff --git a/CloudStreams.sln b/CloudStreams.sln
index 61418739..37660a7a 100644
--- a/CloudStreams.sln
+++ b/CloudStreams.sln
@@ -1,178 +1,121 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
-VisualStudioVersion = 17.6.33815.320
+VisualStudioVersion = 17.9.34714.143
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{DC60318D-AFF7-4099-A3E2-AF78BDEE194A}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{FF66B57F-6960-4E8B-B890-5C1D4287DAE3}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{F474FF36-0A30-455E-9A10-A1615E3604D4}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{D14FE817-6B8B-4987-9203-A74B55BA23AF}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Core", "src\core\CloudStreams.Core\CloudStreams.Core.csproj", "{B0CC48AC-6E34-4D91-AF0C-F63FCDC17900}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gateway", "gateway", "{41B16F55-F5D9-4FC5-A955-A577AFBA6EAF}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Core.Infrastructure", "src\core\CloudStreams.Core.Infrastructure\CloudStreams.Core.Infrastructure.csproj", "{2F29487D-C4AD-4CD9-9969-6080D2F910B8}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "broker", "broker", "{EE7E5147-3DE2-41C1-A3C4-695CC41B2785}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Core.Infrastructure.EventSourcing.EventStore", "src\core\CloudStreams.Core.Infrastructure.EventSourcing.EventStore\CloudStreams.Core.Infrastructure.EventSourcing.EventStore.csproj", "{4C5192B7-2F5D-4513-B8BA-66A5A5FA6EF8}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Core", "src\core\CloudStreams.Core\CloudStreams.Core.csproj", "{177F238A-0173-49B9-925C-0B5432C75EC6}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Core.Api", "src\core\CloudStreams.Core.Api\CloudStreams.Core.Api.csproj", "{6DFD3670-466E-491E-B1D2-173621722A11}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Core.Application", "src\core\CloudStreams.Core.Application\CloudStreams.Core.Application.csproj", "{D776F8D4-04E8-4095-AD03-D71F5EBF8951}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Core.Api.Client", "src\core\CloudStreams.Core.Api.Client\CloudStreams.Core.Api.Client.csproj", "{19535702-F1D9-420B-BF3B-2A585663F40F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Core.Api.Client", "src\core\CloudStreams.Core.Api.Client\CloudStreams.Core.Api.Client.csproj", "{8F4260F3-8F1D-4C6B-9BC5-A7B8DD1E8254}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Core.Api.Server", "src\core\CloudStreams.Core.Api.Server\CloudStreams.Core.Api.Server.csproj", "{EFA9105F-F074-4257-8E8C-633B7DF7EBE8}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Core.Api", "src\core\CloudStreams.Core.Api\CloudStreams.Core.Api.csproj", "{55E026C9-D780-49E4-8F82-C6F303C10BE9}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Core.Application", "src\core\CloudStreams.Core.Application\CloudStreams.Core.Application.csproj", "{19671559-E4BD-49C3-AF13-AEAB2EA0D48B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Gateway.Api", "src\gateway\CloudStreams.Gateway.Api\CloudStreams.Gateway.Api.csproj", "{2F904542-163A-49A8-832A-3545A962234C}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gateway", "gateway", "{5E739A12-4175-4DB9-8094-8EB9BE7CA7F7}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Gateway.Application", "src\gateway\CloudStreams.Gateway.Application\CloudStreams.Gateway.Application.csproj", "{07217222-82B9-490E-B722-4C836A84A0C7}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dashboard", "dashboard", "{AB70869A-91DF-4285-BAB9-6836A68E5F7A}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Broker.Application", "src\broker\CloudStreams.Broker.Application\CloudStreams.Broker.Application.csproj", "{09481105-759E-4EC7-AF2E-1343ECF953B2}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Dashboard.StateManagement", "src\dashboard\CloudStreams.Dashboard.StateManagement\CloudStreams.Dashboard.StateManagement.csproj", "{932E98CC-50B8-4CA1-9080-B55627A07E97}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Broker.Api", "src\broker\CloudStreams.Broker.Api\CloudStreams.Broker.Api.csproj", "{92EDB067-DD65-4106-A0DA-5DC4B4386DFD}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Dashboard", "src\dashboard\CloudStreams.Dashboard\CloudStreams.Dashboard.csproj", "{47133552-BF81-44E4-B127-8256D97EA695}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dashboard", "dashboard", "{BDA26004-D077-442C-A4F7-F389D5AC9FE5}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{BE3EB57F-15FA-49E8-B21F-B648E78AC87B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Dashboard", "src\dashboard\CloudStreams.Dashboard\CloudStreams.Dashboard.csproj", "{DEF277AF-8024-49D8-BD80-338A79431F8C}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deployments", "deployments", "{43DFCA07-D86F-472B-AF59-B979DC9C948C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Dashboard.StateManagement", "src\dashboard\CloudStreams.Dashboard.StateManagement\CloudStreams.Dashboard.StateManagement.csproj", "{7BD5FCBA-1D5A-4DF2-BDC3-5672D3507C06}"
EndProject
-Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "deployments\docker-compose\docker-compose.dcproj", "{0050C106-97E3-40BD-8C2C-34503B5757DF}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Gateway.Application", "src\gateway\CloudStreams.Gateway.Application\CloudStreams.Gateway.Application.csproj", "{09853D11-38D0-4DCD-9138-56C1754EEECD}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Gateway.Api.Client", "src\gateway\CloudStreams.Gateway.Api.Client\CloudStreams.Gateway.Api.Client.csproj", "{F75AA225-432E-4A10-8017-75274673E80C}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Gateway.Api", "src\gateway\CloudStreams.Gateway.Api\CloudStreams.Gateway.Api.csproj", "{AD031F2B-498F-490B-AF1E-67DE43F75AC4}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Gateway.Api.Server", "src\gateway\CloudStreams.Gateway.Api.Server\CloudStreams.Gateway.Api.Server.csproj", "{5148840D-7E3E-4BC9-A098-EF46999750C1}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Core.Infrastructure.RuntimeExpressions.JavaScript", "src\core\CloudStreams.Core.Infrastructure.RuntimeExpressions.JavaScript\CloudStreams.Core.Infrastructure.RuntimeExpressions.JavaScript.csproj", "{8C91F090-0028-4A78-AD5E-523BBFD4A936}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Core.Infrastructure.RuntimeExpressions.JQ", "src\core\CloudStreams.Core.Infrastructure.RuntimeExpressions.JQ\CloudStreams.Core.Infrastructure.RuntimeExpressions.JQ.csproj", "{901F926B-6BA1-4D7F-97D5-F464BDF58113}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "broker", "broker", "{DB906BCE-DF5E-48A4-B2D0-78EBD504EE08}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Broker.Application", "src\broker\CloudStreams.Broker.Application\CloudStreams.Broker.Application.csproj", "{56C38938-070C-46CD-A9A2-812D0389A527}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Broker.Api", "src\broker\CloudStreams.Broker.Api\CloudStreams.Broker.Api.csproj", "{B8BD211C-D29B-4763-8780-B8C1AB7CA867}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudStreams.Broker.Api.Server", "src\broker\CloudStreams.Broker.Api.Server\CloudStreams.Broker.Api.Server.csproj", "{15686F8D-528D-4525-AB4C-920356A9FAF7}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "kubernetes", "kubernetes", "{D8AC6338-9A30-4FD5-A2EB-F46760F074C6}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{129B2507-0C27-4324-AFC9-2C802E25E51D}"
ProjectSection(SolutionItems) = preProject
- deployments\kubernetes\deployment.yaml = deployments\kubernetes\deployment.yaml
+ LICENSE = LICENSE
+ code-of-conduct.md = code-of-conduct.md
+ README.md = README.md
EndProjectSection
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deployments", "deployments", "{990DE5C0-9BDB-417C-91D9-B3883C5FCA3E}"
+EndProject
+Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "deployments\docker-compose\docker-compose.dcproj", "{0050C106-97E3-40BD-8C2C-34503B5757DF}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {B0CC48AC-6E34-4D91-AF0C-F63FCDC17900}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B0CC48AC-6E34-4D91-AF0C-F63FCDC17900}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B0CC48AC-6E34-4D91-AF0C-F63FCDC17900}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B0CC48AC-6E34-4D91-AF0C-F63FCDC17900}.Release|Any CPU.Build.0 = Release|Any CPU
- {2F29487D-C4AD-4CD9-9969-6080D2F910B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2F29487D-C4AD-4CD9-9969-6080D2F910B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2F29487D-C4AD-4CD9-9969-6080D2F910B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2F29487D-C4AD-4CD9-9969-6080D2F910B8}.Release|Any CPU.Build.0 = Release|Any CPU
- {4C5192B7-2F5D-4513-B8BA-66A5A5FA6EF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4C5192B7-2F5D-4513-B8BA-66A5A5FA6EF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4C5192B7-2F5D-4513-B8BA-66A5A5FA6EF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4C5192B7-2F5D-4513-B8BA-66A5A5FA6EF8}.Release|Any CPU.Build.0 = Release|Any CPU
- {6DFD3670-466E-491E-B1D2-173621722A11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6DFD3670-466E-491E-B1D2-173621722A11}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6DFD3670-466E-491E-B1D2-173621722A11}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6DFD3670-466E-491E-B1D2-173621722A11}.Release|Any CPU.Build.0 = Release|Any CPU
- {19535702-F1D9-420B-BF3B-2A585663F40F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {19535702-F1D9-420B-BF3B-2A585663F40F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {19535702-F1D9-420B-BF3B-2A585663F40F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {19535702-F1D9-420B-BF3B-2A585663F40F}.Release|Any CPU.Build.0 = Release|Any CPU
- {EFA9105F-F074-4257-8E8C-633B7DF7EBE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {EFA9105F-F074-4257-8E8C-633B7DF7EBE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {EFA9105F-F074-4257-8E8C-633B7DF7EBE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {EFA9105F-F074-4257-8E8C-633B7DF7EBE8}.Release|Any CPU.Build.0 = Release|Any CPU
- {19671559-E4BD-49C3-AF13-AEAB2EA0D48B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {19671559-E4BD-49C3-AF13-AEAB2EA0D48B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {19671559-E4BD-49C3-AF13-AEAB2EA0D48B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {19671559-E4BD-49C3-AF13-AEAB2EA0D48B}.Release|Any CPU.Build.0 = Release|Any CPU
- {932E98CC-50B8-4CA1-9080-B55627A07E97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {932E98CC-50B8-4CA1-9080-B55627A07E97}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {932E98CC-50B8-4CA1-9080-B55627A07E97}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {932E98CC-50B8-4CA1-9080-B55627A07E97}.Release|Any CPU.Build.0 = Release|Any CPU
- {47133552-BF81-44E4-B127-8256D97EA695}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {47133552-BF81-44E4-B127-8256D97EA695}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {47133552-BF81-44E4-B127-8256D97EA695}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {47133552-BF81-44E4-B127-8256D97EA695}.Release|Any CPU.Build.0 = Release|Any CPU
+ {177F238A-0173-49B9-925C-0B5432C75EC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {177F238A-0173-49B9-925C-0B5432C75EC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {177F238A-0173-49B9-925C-0B5432C75EC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {177F238A-0173-49B9-925C-0B5432C75EC6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D776F8D4-04E8-4095-AD03-D71F5EBF8951}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D776F8D4-04E8-4095-AD03-D71F5EBF8951}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D776F8D4-04E8-4095-AD03-D71F5EBF8951}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D776F8D4-04E8-4095-AD03-D71F5EBF8951}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8F4260F3-8F1D-4C6B-9BC5-A7B8DD1E8254}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8F4260F3-8F1D-4C6B-9BC5-A7B8DD1E8254}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8F4260F3-8F1D-4C6B-9BC5-A7B8DD1E8254}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8F4260F3-8F1D-4C6B-9BC5-A7B8DD1E8254}.Release|Any CPU.Build.0 = Release|Any CPU
+ {55E026C9-D780-49E4-8F82-C6F303C10BE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {55E026C9-D780-49E4-8F82-C6F303C10BE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {55E026C9-D780-49E4-8F82-C6F303C10BE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {55E026C9-D780-49E4-8F82-C6F303C10BE9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2F904542-163A-49A8-832A-3545A962234C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2F904542-163A-49A8-832A-3545A962234C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2F904542-163A-49A8-832A-3545A962234C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2F904542-163A-49A8-832A-3545A962234C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {07217222-82B9-490E-B722-4C836A84A0C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {07217222-82B9-490E-B722-4C836A84A0C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {07217222-82B9-490E-B722-4C836A84A0C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {07217222-82B9-490E-B722-4C836A84A0C7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {09481105-759E-4EC7-AF2E-1343ECF953B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {09481105-759E-4EC7-AF2E-1343ECF953B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {09481105-759E-4EC7-AF2E-1343ECF953B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {09481105-759E-4EC7-AF2E-1343ECF953B2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {92EDB067-DD65-4106-A0DA-5DC4B4386DFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {92EDB067-DD65-4106-A0DA-5DC4B4386DFD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {92EDB067-DD65-4106-A0DA-5DC4B4386DFD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {92EDB067-DD65-4106-A0DA-5DC4B4386DFD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DEF277AF-8024-49D8-BD80-338A79431F8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DEF277AF-8024-49D8-BD80-338A79431F8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DEF277AF-8024-49D8-BD80-338A79431F8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DEF277AF-8024-49D8-BD80-338A79431F8C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7BD5FCBA-1D5A-4DF2-BDC3-5672D3507C06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7BD5FCBA-1D5A-4DF2-BDC3-5672D3507C06}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7BD5FCBA-1D5A-4DF2-BDC3-5672D3507C06}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7BD5FCBA-1D5A-4DF2-BDC3-5672D3507C06}.Release|Any CPU.Build.0 = Release|Any CPU
{0050C106-97E3-40BD-8C2C-34503B5757DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0050C106-97E3-40BD-8C2C-34503B5757DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0050C106-97E3-40BD-8C2C-34503B5757DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0050C106-97E3-40BD-8C2C-34503B5757DF}.Release|Any CPU.Build.0 = Release|Any CPU
- {09853D11-38D0-4DCD-9138-56C1754EEECD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {09853D11-38D0-4DCD-9138-56C1754EEECD}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {09853D11-38D0-4DCD-9138-56C1754EEECD}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {09853D11-38D0-4DCD-9138-56C1754EEECD}.Release|Any CPU.Build.0 = Release|Any CPU
- {F75AA225-432E-4A10-8017-75274673E80C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F75AA225-432E-4A10-8017-75274673E80C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F75AA225-432E-4A10-8017-75274673E80C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F75AA225-432E-4A10-8017-75274673E80C}.Release|Any CPU.Build.0 = Release|Any CPU
- {AD031F2B-498F-490B-AF1E-67DE43F75AC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AD031F2B-498F-490B-AF1E-67DE43F75AC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AD031F2B-498F-490B-AF1E-67DE43F75AC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AD031F2B-498F-490B-AF1E-67DE43F75AC4}.Release|Any CPU.Build.0 = Release|Any CPU
- {5148840D-7E3E-4BC9-A098-EF46999750C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {5148840D-7E3E-4BC9-A098-EF46999750C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {5148840D-7E3E-4BC9-A098-EF46999750C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {5148840D-7E3E-4BC9-A098-EF46999750C1}.Release|Any CPU.Build.0 = Release|Any CPU
- {8C91F090-0028-4A78-AD5E-523BBFD4A936}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {8C91F090-0028-4A78-AD5E-523BBFD4A936}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {8C91F090-0028-4A78-AD5E-523BBFD4A936}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {8C91F090-0028-4A78-AD5E-523BBFD4A936}.Release|Any CPU.Build.0 = Release|Any CPU
- {901F926B-6BA1-4D7F-97D5-F464BDF58113}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {901F926B-6BA1-4D7F-97D5-F464BDF58113}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {901F926B-6BA1-4D7F-97D5-F464BDF58113}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {901F926B-6BA1-4D7F-97D5-F464BDF58113}.Release|Any CPU.Build.0 = Release|Any CPU
- {56C38938-070C-46CD-A9A2-812D0389A527}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {56C38938-070C-46CD-A9A2-812D0389A527}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {56C38938-070C-46CD-A9A2-812D0389A527}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {56C38938-070C-46CD-A9A2-812D0389A527}.Release|Any CPU.Build.0 = Release|Any CPU
- {B8BD211C-D29B-4763-8780-B8C1AB7CA867}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B8BD211C-D29B-4763-8780-B8C1AB7CA867}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B8BD211C-D29B-4763-8780-B8C1AB7CA867}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B8BD211C-D29B-4763-8780-B8C1AB7CA867}.Release|Any CPU.Build.0 = Release|Any CPU
- {15686F8D-528D-4525-AB4C-920356A9FAF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {15686F8D-528D-4525-AB4C-920356A9FAF7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {15686F8D-528D-4525-AB4C-920356A9FAF7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {15686F8D-528D-4525-AB4C-920356A9FAF7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
- {F474FF36-0A30-455E-9A10-A1615E3604D4} = {DC60318D-AFF7-4099-A3E2-AF78BDEE194A}
- {B0CC48AC-6E34-4D91-AF0C-F63FCDC17900} = {F474FF36-0A30-455E-9A10-A1615E3604D4}
- {2F29487D-C4AD-4CD9-9969-6080D2F910B8} = {F474FF36-0A30-455E-9A10-A1615E3604D4}
- {4C5192B7-2F5D-4513-B8BA-66A5A5FA6EF8} = {F474FF36-0A30-455E-9A10-A1615E3604D4}
- {6DFD3670-466E-491E-B1D2-173621722A11} = {F474FF36-0A30-455E-9A10-A1615E3604D4}
- {19535702-F1D9-420B-BF3B-2A585663F40F} = {F474FF36-0A30-455E-9A10-A1615E3604D4}
- {EFA9105F-F074-4257-8E8C-633B7DF7EBE8} = {F474FF36-0A30-455E-9A10-A1615E3604D4}
- {19671559-E4BD-49C3-AF13-AEAB2EA0D48B} = {F474FF36-0A30-455E-9A10-A1615E3604D4}
- {5E739A12-4175-4DB9-8094-8EB9BE7CA7F7} = {DC60318D-AFF7-4099-A3E2-AF78BDEE194A}
- {AB70869A-91DF-4285-BAB9-6836A68E5F7A} = {DC60318D-AFF7-4099-A3E2-AF78BDEE194A}
- {932E98CC-50B8-4CA1-9080-B55627A07E97} = {AB70869A-91DF-4285-BAB9-6836A68E5F7A}
- {47133552-BF81-44E4-B127-8256D97EA695} = {AB70869A-91DF-4285-BAB9-6836A68E5F7A}
- {0050C106-97E3-40BD-8C2C-34503B5757DF} = {43DFCA07-D86F-472B-AF59-B979DC9C948C}
- {09853D11-38D0-4DCD-9138-56C1754EEECD} = {5E739A12-4175-4DB9-8094-8EB9BE7CA7F7}
- {F75AA225-432E-4A10-8017-75274673E80C} = {5E739A12-4175-4DB9-8094-8EB9BE7CA7F7}
- {AD031F2B-498F-490B-AF1E-67DE43F75AC4} = {5E739A12-4175-4DB9-8094-8EB9BE7CA7F7}
- {5148840D-7E3E-4BC9-A098-EF46999750C1} = {5E739A12-4175-4DB9-8094-8EB9BE7CA7F7}
- {8C91F090-0028-4A78-AD5E-523BBFD4A936} = {F474FF36-0A30-455E-9A10-A1615E3604D4}
- {901F926B-6BA1-4D7F-97D5-F464BDF58113} = {F474FF36-0A30-455E-9A10-A1615E3604D4}
- {DB906BCE-DF5E-48A4-B2D0-78EBD504EE08} = {DC60318D-AFF7-4099-A3E2-AF78BDEE194A}
- {56C38938-070C-46CD-A9A2-812D0389A527} = {DB906BCE-DF5E-48A4-B2D0-78EBD504EE08}
- {B8BD211C-D29B-4763-8780-B8C1AB7CA867} = {DB906BCE-DF5E-48A4-B2D0-78EBD504EE08}
- {15686F8D-528D-4525-AB4C-920356A9FAF7} = {DB906BCE-DF5E-48A4-B2D0-78EBD504EE08}
- {D8AC6338-9A30-4FD5-A2EB-F46760F074C6} = {43DFCA07-D86F-472B-AF59-B979DC9C948C}
+ {D14FE817-6B8B-4987-9203-A74B55BA23AF} = {FF66B57F-6960-4E8B-B890-5C1D4287DAE3}
+ {41B16F55-F5D9-4FC5-A955-A577AFBA6EAF} = {FF66B57F-6960-4E8B-B890-5C1D4287DAE3}
+ {EE7E5147-3DE2-41C1-A3C4-695CC41B2785} = {FF66B57F-6960-4E8B-B890-5C1D4287DAE3}
+ {177F238A-0173-49B9-925C-0B5432C75EC6} = {D14FE817-6B8B-4987-9203-A74B55BA23AF}
+ {D776F8D4-04E8-4095-AD03-D71F5EBF8951} = {D14FE817-6B8B-4987-9203-A74B55BA23AF}
+ {8F4260F3-8F1D-4C6B-9BC5-A7B8DD1E8254} = {D14FE817-6B8B-4987-9203-A74B55BA23AF}
+ {55E026C9-D780-49E4-8F82-C6F303C10BE9} = {D14FE817-6B8B-4987-9203-A74B55BA23AF}
+ {2F904542-163A-49A8-832A-3545A962234C} = {41B16F55-F5D9-4FC5-A955-A577AFBA6EAF}
+ {07217222-82B9-490E-B722-4C836A84A0C7} = {41B16F55-F5D9-4FC5-A955-A577AFBA6EAF}
+ {09481105-759E-4EC7-AF2E-1343ECF953B2} = {EE7E5147-3DE2-41C1-A3C4-695CC41B2785}
+ {92EDB067-DD65-4106-A0DA-5DC4B4386DFD} = {EE7E5147-3DE2-41C1-A3C4-695CC41B2785}
+ {BDA26004-D077-442C-A4F7-F389D5AC9FE5} = {FF66B57F-6960-4E8B-B890-5C1D4287DAE3}
+ {DEF277AF-8024-49D8-BD80-338A79431F8C} = {BDA26004-D077-442C-A4F7-F389D5AC9FE5}
+ {7BD5FCBA-1D5A-4DF2-BDC3-5672D3507C06} = {BDA26004-D077-442C-A4F7-F389D5AC9FE5}
+ {0050C106-97E3-40BD-8C2C-34503B5757DF} = {990DE5C0-9BDB-417C-91D9-B3883C5FCA3E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {A39D1D72-5614-4CCF-B7AD-923E4DC3F452}
+ SolutionGuid = {5E10D9D7-D5B5-4C0B-B7AC-AC10FF33DA3F}
EndGlobalSection
EndGlobal
diff --git a/README.md b/README.md
index d052f71e..67db5ea5 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,30 @@
-
+
+## Contents
+
+- [About](#about)
+ + [Features](#features)
+ + [Benefits](#benefits)
+- [Documentation](#documentation)
+ + [Fundamentals](#fundamentals)
+ + [Getting started](#getting-started)
+ + [API Reference](#api-reference)
+- [Contributing](#contributing)
+ + [How to contribute](#how-to-contribute)
+ + [Types of contributions](#types-of-contributions)
+ + [Code of conduct](#code-of-conduct)
+ + [Get in touch](#get-in-touch)
+ + [Let's build together](#lets-build-together)
+## About
+
Cloud Streams is a cloud-native tool that empowers users to capture and process [cloud events](https://cloudevents.io/) in real-time, enabling event-driven architectures that are both scalable and efficient. With Cloud Streams, you can customize how you stream, filter, partition, and mutate the [cloud events](https://cloudevents.io/) you consume using declarative rules, giving you full control over how your data is processed and consumed.
As event-driven architectures have become more popular, so too have the challenges associated with building and managing them, especially when dealing with large volumes of data. This is where Cloud Streams comes in. This powerful and straightforward tool provides a simple and effective way to capture, process, and route cloud events in real-time, allowing you to create event-driven architectures that are both flexible and reliable.
-## Features
+### Features
- **Streaming**: Cloud Streams allows users to stream cloud events in real-time, ensuring that all relevant data is captured and processed as soon as it becomes available. This feature is especially useful for applications that require real-time data processing, such as financial trading or IoT applications.
- **Filtering**: Cloud Streams provides powerful filtering capabilities, allowing users to filter cloud events based on declarative rules. This ensures that only relevant data is captured and processed, reducing processing time and improving overall system performance.
@@ -17,7 +34,7 @@ As event-driven architectures have become more popular, so too have the challeng
- **Fault Tolerance**: Cloud Streams provides fault tolerance by implementing atomic, configurable retry policies that enable brokers to resend missed events to a consumer that was previously unavailable or that responded with a non-success status code. This ensures that all missed events are eventually received by the consumer in the order they were originally posted. The retry policy enables potential errors to be fixed at the consumer level, without the risk of losing data.
- **Playback**: Cloud Streams provides playback capabilities, allowing users to play back previously captured cloud events. This can be useful for debugging, testing, or catastrophic recovery purposes. With this feature, users can recover from system failures or data corruption by replaying previously captured events.
-## Benefits
+### Benefits
- **Scalability**: Cloud Streams is designed to be highly scalable, allowing users to process large volumes of data with ease. This makes it an ideal solution for applications with rapidly changing data requirements.
- **Flexibility**: Cloud Streams provides flexible filtering and routing capabilities, allowing users to customize the way their data is processed and consumed. This ensures that users can adapt to changing data requirements quickly and easily.
@@ -26,6 +43,138 @@ As event-driven architectures have become more popular, so too have the challeng
- **Historical Data Access**: Cloud Streams provides historical data access capabilities, allowing users to access previously captured cloud events. This can be useful for analyzing historical data, as well as for replaying events for debugging, testing, or catastrophic recovery purposes. With this feature, users can recover from system failures or data corruption by accessing previously captured events.
- **Easy to Integrate**: Cloud Streams is extremely easy to integrate in existing solutions. As a matter of fact, producers can publish events to Cloud Streams simply by POSTing Cloud Events, using the [structured content mode](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/bindings/http-protocol-binding.md#32-structured-content-mode), to a specific gateway. Consumers, on the other hand, only need to have an endpoint that can accept cloud events sent over HTTP using the [structured content mode](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/bindings/http-protocol-binding.md#32-structured-content-mode). That's it. All the configuration, and control, is offloaded to Cloud Streams resources such as subscriptions.
-## Solution Overview
+### Want to know more?
+
+Then take a look at our [wiki](https://github.com/neuroglia-io/cloud-streams/wiki)📚, or start a new discussion/issue!
+
+
+
+*Solution overview:*
+
+## Documentation
+
+### Fundamentals
+
+CloudStreams is a platform that implements [resource-oriented architecture (ROA)](https://github.com/neuroglia-io/cloud-streams/wiki/Fundamentals#resources) to allow users to customize the behavior and configuration of event processing. Similar to Kubernetes Custom Resources, CloudStreams resources enable users to define specific configurations without modifying the underlying system. These resources serve as templates for various aspects of event handling within the CloudStreams ecosystem, offering flexibility to tailor event processing to specific architectural needs.
+
+[Cloud Events](https://github.com/neuroglia-io/cloud-streams/wiki/Fundamentals#cloud-events) are a standardized format used by CloudStreams to represent events in cloud environments. They encapsulate essential information about an event, such as its type, source, and data, fostering interoperability and seamless integration across different cloud services.
+
+[Streams](https://github.com/neuroglia-io/cloud-streams/wiki/Fundamentals#streams) in CloudStreams are ordered, append-only collections of events, providing comprehensive auditing and enabling efficient event handling. Events are automatically [partitioned](https://github.com/neuroglia-io/cloud-streams/wiki/Fundamentals#partitions) based on attributes like source, type, and subject, allowing for scalable and responsive architectures.
+
+CloudStreams [subscriptions](https://github.com/neuroglia-io/cloud-streams/wiki/Fundamentals#subscriptions) empower users to configure event dispatching based on partitions, filters, and mutations. This fine-grained control optimizes event-driven applications by ensuring that relevant events are efficiently processed and delivered to consumers based on specific criteria.
+
+*Please view the [wiki](https://github.com/neuroglia-io/cloud-streams/wiki/Fundamentals)📚 for additional information.*
+
+### Getting started
+
+The easiest way to start the solution is using ``docker-compose``.
+
+To to this, clone the repository:
+
+```shell
+git clone https://github.com/neuroglia-io/cloud-streams.git
+```
+
+Navigate to the resulting `/cloud-streams/` directory, then run the following command:
+
+```shell
+docker-compose -f "deployments/docker-compose/docker-compose.yml" up
+```
+
+Now that everything is up and running, you can start publishing cloud events using POST requests on the gateway's ingestion endpoint, at `http://localhost:8080/api/events/pub`:
+
+```shell
+curl -X 'POST' \
+ 'http://localhost:8080/api/events/pub' \
+ -H 'accept: */*' \
+ -H 'Content-Type: application/cloudevents+json' \
+ -d '{
+ "id": "1234567890",
+ "specversion": "1.0",
+ "source": "https://foo.bar.com",
+ "type": "bar.foo",
+ "subject": "foobar",
+ "dataschema": "string",
+ "data": {
+ "foo": "bar"
+ }
+}'
+```
+
+You can start consuming cloud events by creating a new subscription, which can be done via [Dashboard](http://localhost:8080/subscriptions), or by executing a simple POST request:
+
+```shell
+curl -X 'POST' \
+ 'http://localhost:8080/api/resources/v1/subscriptions?dryRun=false' \
+ -H 'accept: text/plain' \
+ -H 'Content-Type: text/json' \
+ -d '{
+ "metadata": {
+ "name": "my-subscription"
+ },
+ "spec": {
+ "subscriber": {
+ "uri": "https://webhook.site/00000000-0000-0000-0000-000000000000"
+ },
+ "stream":{
+ "offset": 0
+ }
+ }
+}'
+```
+
+The preceeding sample creates a new subscription to all events (no partition has been defined and no filter has been set). Because we have defined the stream offset at '0', when will start receiving all events ever published, even the ones published before the creation of the subscription.
+
+Note that, by default, subscriptions will only process cloud events published **after** their creation.
+
+*For more information on subscriptions, please check the [wiki](https://github.com/neuroglia-io/cloud-streams/wiki/Fundamentals#subscriptions)📚.*
+
+
+
+The `Dashboard UI` is served by the `gateway` and is reachable at [http://localhost:8080](http://localhost:8080/).
+
+The `Swagger UI`, also served by the `gateway`, is reachable at [http://localhost:8080/api/doc](http://localhost:8080/api/doc).
+
+### API Reference
+
+*Please refer to the [wiki](https://github.com/neuroglia-io/cloud-streams/wiki/API-Reference)📚.*
+
+## Contributing
+
+👋 **Welcome Contributors!**
+
+We're thrilled that you're interested in contributing to our project on GitHub. Whether you're a seasoned developer or new to open source, there are many ways you can contribute and make a difference.
+
+### How to Contribute
+
+1. **Fork the Repository**: Start by forking our repository to your GitHub account.
+2. **Clone the Repository**: Clone the forked repository to your local machine using `git clone`.
+3. **Create a Branch**: Create a new branch for your contribution (`git checkout -b my-feature`).
+4. **Make Changes**: Make your changes and additions to the codebase.
+5. **Commit Changes**: Commit your changes with descriptive commit messages.
+6. **Push Changes**: Push your changes to your forked repository (`git push origin my-feature`).
+7. **Submit a Pull Request**: Open a pull request from your forked repository to our main repository.
+
+### Types of Contributions
+
+- **Bug Fixes**: Help us squash those pesky bugs.
+- **Feature Additions**: Introduce new features or enhancements.
+- **Documentation**: Improve our documentation to make it more comprehensive.
+- **Code Optimization**: Help optimize existing code for better performance.
+- **Issue Reporting**: Report bugs, suggest features, or ask questions by opening issues.
+
+### Code of Conduct
+
+Please review and adhere to our [Code of Conduct](code-of-conduct.md) while participating in this project.
+
+### Get in Touch
+
+If you have any questions or need assistance, feel free to reach out via GitHub issues or discussions.
+
+### Let's Build Together!
+
+Every contribution counts, no matter how big or small. We appreciate your time and effort in making our project better. Thank you for being part of our open source community!
+
+Happy Coding! 🚀
diff --git a/code-of-conduct.md b/code-of-conduct.md
new file mode 100644
index 00000000..462518aa
--- /dev/null
+++ b/code-of-conduct.md
@@ -0,0 +1,41 @@
+# Cloud Streams Code of Conduct
+
+Cloud Streams is dedicated to providing a welcoming and inclusive environment for all participants, regardless of age, gender identity and expression, sexual orientation, disability, physical appearance, race, ethnicity, religion (or lack thereof), or technology choices. We do not tolerate harassment of participants in any form.
+
+This Code of Conduct applies to all Cloud Streams spaces, including but not limited to GitHub repositories, issue trackers, Slack channels, email communications, social media, and events.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment include:
+
+- Using welcoming and inclusive language
+- Being respectful of differing viewpoints and experiences
+- Gracefully accepting constructive criticism
+- Focusing on what is best for the community
+- Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+- The use of sexualized language or imagery and unwelcome sexual attention or advances
+- Trolling, insulting/derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or electronic address, without explicit permission
+- Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned with this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
+
+## Reporting Violations
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [info@neuroglia.io](mailto:info@neuroglia.io). All complaints will be reviewed and investigated promptly and fairly.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html)
+
+For answers to common questions about this code of conduct, see [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq)
+
+[homepage]: [https://www.contributor-covenant.org](https://www.contributor-covenant.org)
diff --git a/deployments/docker-compose/docker-compose.dcproj b/deployments/docker-compose/docker-compose.dcproj
index 87c2e947..e896ab7e 100644
--- a/deployments/docker-compose/docker-compose.dcproj
+++ b/deployments/docker-compose/docker-compose.dcproj
@@ -12,6 +12,5 @@
-
\ No newline at end of file
diff --git a/deployments/docker-compose/docker-compose.yml b/deployments/docker-compose/docker-compose.yml
index ad228962..2227c0d7 100644
--- a/deployments/docker-compose/docker-compose.yml
+++ b/deployments/docker-compose/docker-compose.yml
@@ -6,7 +6,6 @@ services:
image: eventstore/eventstore:latest
environment:
- EVENTSTORE_INSECURE=true
- - EVENTSTORE_EXT_TCP_PORT=1113
- EVENTSTORE_HTTP_PORT=2113
- EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP=true
- EVENTSTORE_RUN_PROJECTIONS=all
@@ -14,48 +13,38 @@ services:
ports:
- "1113:1113"
- "2113:2113"
- volumes:
- - type: volume
- source: eventstore-data
- target: /var/lib/eventstore
- - type: volume
- source: eventstore-logs
- target: /var/log/eventstore
redis:
image: redis:latest
- api-server:
- image: ${DOCKER_REGISTRY-}api-server
+ gateway:
+ image: ${DOCKER_REGISTRY-}gateway
build:
context: ../../
- dockerfile: ./src/core/CloudStreams.Core.Api.Server/Dockerfile
+ dockerfile: ./src/gateway/CloudStreams.Gateway.Api/Dockerfile
environment:
ASPNETCORE_ENVIRONMENT: Development
+ ASPNETCORE_HTTP_PORTS: 80
CONNECTIONSTRINGS__REDIS: ${REDIS_URI}
CONNECTIONSTRINGS__EVENTSTORE: ${EVENTSTORE_URI}
- volumes:
- - ./plugins:/app/bin/Debug/net7.0/plugins
+ CLOUDSTREAMS_GATEWAY_NAME: gateway-1
+ ports:
+ - 8080:80
depends_on:
- redis
- eventstore
- gateway:
- image: ${DOCKER_REGISTRY-}gateway
+ broker:
+ image: ${DOCKER_REGISTRY-}broker
build:
context: ../../
- dockerfile: ./src/gateway/CloudStreams.Gateway.Api.Server/Dockerfile
+ dockerfile: ./src/broker/CloudStreams.Broker.Api/Dockerfile
environment:
ASPNETCORE_ENVIRONMENT: Development
+ ASPNETCORE_HTTP_PORTS: 80
CONNECTIONSTRINGS__REDIS: ${REDIS_URI}
CONNECTIONSTRINGS__EVENTSTORE: ${EVENTSTORE_URI}
- CLOUDSTREAMS_GATEWAY_NAME: gateway-1
- volumes:
- - ./plugins:/app/bin/Debug/net7.0/plugins
+ CLOUDSTREAMS_BROKER_NAME: broker-1
depends_on:
- redis
- - eventstore
-
-volumes:
- eventstore-data:
- eventstore-logs:
\ No newline at end of file
+ - eventstore
\ No newline at end of file
diff --git a/deployments/docker-compose/plugins/event-sourcing.plugin.json b/deployments/docker-compose/plugins/event-sourcing.plugin.json
deleted file mode 100644
index 5c603d69..00000000
--- a/deployments/docker-compose/plugins/event-sourcing.plugin.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "assemblyFilePath": "/app/bin/Debug/net7.0/plugins/event-sourcing/CloudStreams.Core.Infrastructure.EventSourcing.EventStore.dll",
- "typeName": "CloudStreams.Core.Infrastructure.Services.ESEventStoreProvider"
-}
\ No newline at end of file
diff --git a/deployments/docker-compose/plugins/resource-database.plugin.json b/deployments/docker-compose/plugins/resource-database.plugin.json
deleted file mode 100644
index 2aebc8e5..00000000
--- a/deployments/docker-compose/plugins/resource-database.plugin.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "assemblyFilePath": "Hylo.Providers.Redis.dll",
- "typeName": "Hylo.Providers.FileSystem.Services.RedisDatabaseProvider",
- "nugetPackage": "Hylo.Providers.Redis"
-}
\ No newline at end of file
diff --git a/deployments/kubernetes/deployment.yaml b/deployments/kubernetes/deployment.yaml
deleted file mode 100644
index 436646f6..00000000
--- a/deployments/kubernetes/deployment.yaml
+++ /dev/null
@@ -1,167 +0,0 @@
-apiVersion: v1
-kind: Namespace
-metadata:
- name: cloud-streams
- labels:
- name: cloud-streams
-
----
-
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: api-server
- namespace: cloud-streams
- labels:
- app: api-server
-spec:
- replicas: 1
- selector:
- matchLabels:
- app: api-server
- template:
- metadata:
- labels:
- app: api-server
- spec:
- containers:
- - name: api-server
- image: ghcr.io/neuroglia-io/cloud-streams-api-server:latest
- ports:
- - containerPort: 80
- env:
- - name: CONNECTIONSTRINGS__REDIS
- value: ${REDIS_URI}
- - name: CONNECTIONSTRINGS__EVENTSTORE
- value: ${EVENTSTORE_URI}
- volumeMounts:
- - name: cloud-streams-plugins
- mountPath: /app/plugins/
- volumes:
- - name: cloud-streams-plugins
- hostPath:
- path: /run/desktop/mnt/host/c/Users/User/.cloud-streams/plugins
----
-
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: gateway
- namespace: cloud-streams
- labels:
- app: gateway
-spec:
- replicas: 1
- selector:
- matchLabels:
- app: gateway
- template:
- metadata:
- labels:
- app: gateway
- spec:
- containers:
- - name: gateway
- image: ghcr.io/neuroglia-io/cloud-streams-gateway:latest
- ports:
- - containerPort: 80
- env:
- - name: CONNECTIONSTRINGS__REDIS
- value: ${REDIS_URI}
- - name: CONNECTIONSTRINGS__EVENTSTORE
- value: ${EVENTSTORE_URI}
- - name: CLOUDSTREAMS_GATEWAY_NAME
- valueFrom:
- fieldRef:
- fieldPath: metadata.name
- volumeMounts:
- - name: cloud-streams-plugins
- mountPath: /app/plugins/
- volumes:
- - name: cloud-streams-plugins
- hostPath:
- path: /run/desktop/mnt/host/c/Users/User/.cloud-streams/plugins
----
-
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: broker
- namespace: cloud-streams
- labels:
- app: broker
-spec:
- replicas: 1
- selector:
- matchLabels:
- app: broker
- template:
- metadata:
- labels:
- app: broker
- spec:
- containers:
- - name: broker
- image: ghcr.io/neuroglia-io/cloud-streams-broker:latest
- ports:
- - containerPort: 80
- env:
- - name: CONNECTIONSTRINGS__REDIS
- value: ${REDIS_URI}
- - name: CONNECTIONSTRINGS__EVENTSTORE
- value: ${EVENTSTORE_URI}
- - name: CLOUDSTREAMS_BROKER_NAME
- valueFrom:
- fieldRef:
- fieldPath: metadata.name
- volumeMounts:
- - name: cloud-streams-plugins
- mountPath: /app/plugins/
- volumes:
- - name: cloud-streams-plugins
- hostPath:
- path: /run/desktop/mnt/host/c/Users/User/.cloud-streams/plugins
----
-
-apiVersion: v1
-kind: Service
-metadata:
- name: api-server
-spec:
- selector:
- app: api-server
- ports:
- - name: http
- protocol: TCP
- port: 80
- targetPort: 80
-
----
-
-apiVersion: v1
-kind: Service
-metadata:
- name: gateway
-spec:
- selector:
- app: gateway
- ports:
- - name: http
- protocol: TCP
- port: 80
- targetPort: 80
-
----
-
-apiVersion: v1
-kind: Service
-metadata:
- name: broker
-spec:
- selector:
- app: broker
- ports:
- - name: http
- protocol: TCP
- port: 80
- targetPort: 80
\ No newline at end of file
diff --git a/launchSettings.json b/launchSettings.json
deleted file mode 100644
index 45328c71..00000000
--- a/launchSettings.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "profiles": {
- "Docker Compose": {
- "commandName": "DockerCompose",
- "commandVersion": "1.0",
- "serviceActions": {
- "cloudstreams.core.api.server": "StartDebugging"
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/broker/CloudStreams.Broker.Api.Server/CloudStreams.Broker.Api.Server.csproj b/src/broker/CloudStreams.Broker.Api.Server/CloudStreams.Broker.Api.Server.csproj
deleted file mode 100644
index 153f9fe0..00000000
--- a/src/broker/CloudStreams.Broker.Api.Server/CloudStreams.Broker.Api.Server.csproj
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
- net7.0
- enable
- enable
- True
- 0.14.0
- $(VersionPrefix)
- $(VersionPrefix)
- en
- Apache-2.0
- Copyright © 2023-Present The Cloud Streams Authors. All rights reserved.
- https://github.com/neuroglia-io/cloud-streams
- README.md
- https://github.com/neuroglia-io/cloud-streams
- git
- cloud-streams-broker
- Linux
- ..\..\..
- af3f83b7-cab9-412b-9a4b-21e1921c5b0c
-
-
-
-
- \
- True
-
-
- \
- True
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/broker/CloudStreams.Broker.Api.Server/Dockerfile b/src/broker/CloudStreams.Broker.Api.Server/Dockerfile
deleted file mode 100644
index b460cb59..00000000
--- a/src/broker/CloudStreams.Broker.Api.Server/Dockerfile
+++ /dev/null
@@ -1,29 +0,0 @@
-#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
-
-FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
-WORKDIR /app
-EXPOSE 80
-EXPOSE 443
-RUN apt-get update
-RUN apt-get install -y jq
-
-FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
-WORKDIR /src
-COPY ["src/broker/CloudStreams.Broker.Api.Server/CloudStreams.Broker.Api.Server.csproj", "src/broker/CloudStreams.Broker.Api.Server/"]
-COPY ["src/broker/CloudStreams.Broker.Api/CloudStreams.Broker.Api.csproj", "src/broker/CloudStreams.Broker.Api/"]
-COPY ["src/broker/CloudStreams.Broker.Application/CloudStreams.Broker.Application.csproj", "src/broker/CloudStreams.Broker.Application/"]
-COPY ["src/core/CloudStreams.Core.Application/CloudStreams.Core.Application.csproj", "src/core/CloudStreams.Core.Application/"]
-COPY ["src/core/CloudStreams.Core.Infrastructure/CloudStreams.Core.Infrastructure.csproj", "src/core/CloudStreams.Core.Infrastructure/"]
-COPY ["src/core/CloudStreams.Core/CloudStreams.Core.csproj", "src/core/CloudStreams.Core/"]
-RUN dotnet restore "src/broker/CloudStreams.Broker.Api.Server/CloudStreams.Broker.Api.Server.csproj"
-COPY . .
-WORKDIR "/src/src/broker/CloudStreams.Broker.Api.Server"
-RUN dotnet build "CloudStreams.Broker.Api.Server.csproj" -c Release -o /app/build
-
-FROM build AS publish
-RUN dotnet publish "CloudStreams.Broker.Api.Server.csproj" -c Release -o /app/publish /p:UseAppHost=false
-
-FROM base AS final
-WORKDIR /app
-COPY --from=publish /app/publish .
-ENTRYPOINT ["dotnet", "CloudStreams.Broker.Api.Server.dll"]
\ No newline at end of file
diff --git a/src/broker/CloudStreams.Broker.Api.Server/Program.cs b/src/broker/CloudStreams.Broker.Api.Server/Program.cs
deleted file mode 100644
index 64df6481..00000000
--- a/src/broker/CloudStreams.Broker.Api.Server/Program.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright © 2023-Present The Cloud Streams Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"),
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-using CloudStreams.Broker.Api.Configuration;
-using CloudStreams.Core.Application.Configuration;
-using HealthChecks.UI.Client;
-using Microsoft.AspNetCore.Diagnostics.HealthChecks;
-using Swashbuckle.AspNetCore.SwaggerUI;
-
-var builder = WebApplication.CreateBuilder(args);
-builder.UseCloudStreams(builder =>
-{
- builder.UseBrokerApi();
-});
-
-var app = builder.Build();
-
-if (app.Environment.IsDevelopment()) app.UseWebAssemblyDebugging();
-else app.UseExceptionHandler("/error");
-
-app.UseResponseCompression();
-app.UseBlazorFrameworkFiles();
-app.UseStaticFiles();
-app.UseRouting();
-app.UseAuthorization();
-app.UseHealthChecks("/healthz", new HealthCheckOptions()
-{
- ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
-});
-app.UseSwagger(builder =>
-{
- builder.RouteTemplate = "api/{documentName}/doc/oas.{json|yaml}";
-});
-app.UseSwaggerUI(builder =>
-{
- builder.DocExpansion(DocExpansion.None);
- builder.SwaggerEndpoint("/api/v1/doc/oas.json", "Cloud Streams Broker API v1");
- builder.RoutePrefix = "api/doc";
- builder.DisplayOperationId();
-});
-
-app.MapControllers();
-app.MapFallbackToFile("index.html");
-
-await app.RunAsync();
diff --git a/src/broker/CloudStreams.Broker.Api.Server/Properties/launchSettings.json b/src/broker/CloudStreams.Broker.Api.Server/Properties/launchSettings.json
deleted file mode 100644
index f4c4c2bc..00000000
--- a/src/broker/CloudStreams.Broker.Api.Server/Properties/launchSettings.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "profiles": {
- "CloudStreams.Broker.Api.Server": {
- "commandName": "Project",
- "launchBrowser": true,
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development",
- "CLOUDSTREAMS_BROKER_NAME": "broker-1",
- "CONNECTIONSTRINGS__EVENTSTORE": "esdb://localhost:2113?tls=false",
- "CONNECTIONSTRINGS__REDIS": "localhost"
- },
- "applicationUrl": "https://localhost:52660;http://localhost:52661"
- },
- "Docker": {
- "commandName": "Docker",
- "launchBrowser": true,
- "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
- "publishAllPorts": true,
- "useSSL": true
- }
- }
-}
\ No newline at end of file
diff --git a/src/broker/CloudStreams.Broker.Api.Server/appsettings.Development.json b/src/broker/CloudStreams.Broker.Api.Server/appsettings.Development.json
deleted file mode 100644
index a6e86ace..00000000
--- a/src/broker/CloudStreams.Broker.Api.Server/appsettings.Development.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "Logging": {
- "LogLevel": {
- "Default": "Debug",
- "Microsoft.AspNetCore": "Warning"
- }
- }
-}
diff --git a/src/broker/CloudStreams.Broker.Api.Server/appsettings.json b/src/broker/CloudStreams.Broker.Api.Server/appsettings.json
deleted file mode 100644
index 10f68b8c..00000000
--- a/src/broker/CloudStreams.Broker.Api.Server/appsettings.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft.AspNetCore": "Warning"
- }
- },
- "AllowedHosts": "*"
-}
diff --git a/src/broker/CloudStreams.Broker.Api/CloudStreams.Broker.Api.csproj b/src/broker/CloudStreams.Broker.Api/CloudStreams.Broker.Api.csproj
index 9a654c81..8980af3c 100644
--- a/src/broker/CloudStreams.Broker.Api/CloudStreams.Broker.Api.csproj
+++ b/src/broker/CloudStreams.Broker.Api/CloudStreams.Broker.Api.csproj
@@ -1,40 +1,52 @@
- net7.0
- enable
+ net8.0
enable
- True
- Library
+ enable
0.14.0
$(VersionPrefix)
$(VersionPrefix)
en
+ true
+ True
Apache-2.0
Copyright © 2023-Present The Cloud Streams Authors. All rights reserved.
https://github.com/neuroglia-io/cloud-streams
- README.md
https://github.com/neuroglia-io/cloud-streams
git
+ true
+ cloud-streams/broker
+ Linux
+ ..\..\..
-
- \
- True
-
-
- \
- True
-
+
+
+
+
+
+
+
-
+
+
-
+
+ PreserveNewest
+ true
+ PreserveNewest
+
+
+ PreserveNewest
+ true
+ PreserveNewest
+
diff --git a/src/broker/CloudStreams.Broker.Api/Configuration/ICloudStreamsApiBuilderExtensions.cs b/src/broker/CloudStreams.Broker.Api/Configuration/ICloudStreamsApiBuilderExtensions.cs
deleted file mode 100644
index 778a4e9a..00000000
--- a/src/broker/CloudStreams.Broker.Api/Configuration/ICloudStreamsApiBuilderExtensions.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright © 2023-Present The Cloud Streams Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"),
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-using CloudStreams.Broker.Application.Configuration;
-using CloudStreams.Broker.Application.Services;
-using CloudStreams.Core.Infrastructure.Configuration;
-using Hylo.Infrastructure;
-using Hylo.Infrastructure.Services;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-
-namespace CloudStreams.Broker.Api.Configuration;
-
-///
-/// Defines extensions for s
-///
-public static class ICloudStreamsApiBuilderExtensions
-{
-
- ///
- /// Configures CloudStreams to use the Broker API
- ///
- /// The to configure
- /// The configured
- public static ICloudStreamsApplicationBuilder UseBrokerApi(this ICloudStreamsApplicationBuilder builder)
- {
- var options = new BrokerOptions();
- builder.Configuration.AddEnvironmentVariables(BrokerOptions.EnvironmentVariablePrefix);
- builder.Configuration.Bind(options);
-
- builder.WithServiceName(options.Name);
- builder.Services.Configure(builder.Configuration);
- builder.Services.TryAddSingleton();
- builder.Services.AddSingleton>(provider => provider.GetRequiredService());
- builder.Services.AddHostedService(provider => provider.GetRequiredService());
-
- return builder;
- }
-
-}
diff --git a/src/broker/CloudStreams.Broker.Api/Dockerfile b/src/broker/CloudStreams.Broker.Api/Dockerfile
new file mode 100644
index 00000000..e492b4d4
--- /dev/null
+++ b/src/broker/CloudStreams.Broker.Api/Dockerfile
@@ -0,0 +1,25 @@
+FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
+USER app
+WORKDIR /app
+EXPOSE 80
+USER root
+RUN apt-get update
+RUN apt-get install -y jq
+
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
+ARG BUILD_CONFIGURATION=Release
+WORKDIR /src
+COPY ["src/broker/CloudStreams.Broker.Api/CloudStreams.Broker.Api.csproj", "src/broker/CloudStreams.Broker.Api/"]
+RUN dotnet restore "./src/broker/CloudStreams.Broker.Api/CloudStreams.Broker.Api.csproj"
+COPY . .
+WORKDIR "/src/src/broker/CloudStreams.Broker.Api"
+RUN dotnet build "./CloudStreams.Broker.Api.csproj" -c $BUILD_CONFIGURATION -o /app/build
+
+FROM build AS publish
+ARG BUILD_CONFIGURATION=Release
+RUN dotnet publish "./CloudStreams.Broker.Api.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app/publish .
+ENTRYPOINT ["dotnet", "CloudStreams.Broker.Api.dll"]
\ No newline at end of file
diff --git a/src/broker/CloudStreams.Broker.Api/Program.cs b/src/broker/CloudStreams.Broker.Api/Program.cs
new file mode 100644
index 00000000..083ae4d8
--- /dev/null
+++ b/src/broker/CloudStreams.Broker.Api/Program.cs
@@ -0,0 +1,36 @@
+// Copyright © 2024-Present The Cloud Streams Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"),
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using CloudStreams.Broker.Application.Configuration;
+using CloudStreams.Broker.Application.Services;
+using CloudStreams.Core;
+using CloudStreams.Core.Api;
+using CloudStreams.Core.Resources;
+using Neuroglia.Data.Infrastructure.ResourceOriented.Services;
+
+CloudStreamsDefaults.Telemetry.ActivitySource = new("Cloud Streams Broker");
+
+var builder = WebApplication.CreateBuilder(args);
+builder.Configuration.AddEnvironmentVariables(BrokerOptions.EnvironmentVariablePrefix);
+builder.UseCloudStreams(builder => { });
+
+builder.Services.Configure(builder.Configuration);
+builder.Services.AddSingleton();
+builder.Services.AddSingleton>(provider => provider.GetRequiredService());
+builder.Services.AddHostedService(provider => provider.GetRequiredService());
+
+using var app = builder.Build();
+
+app.UseResponseCompression();
+
+await app.RunAsync();
\ No newline at end of file
diff --git a/src/broker/CloudStreams.Broker.Api/Properties/launchSettings.json b/src/broker/CloudStreams.Broker.Api/Properties/launchSettings.json
index 5f50bbed..d03e269e 100644
--- a/src/broker/CloudStreams.Broker.Api/Properties/launchSettings.json
+++ b/src/broker/CloudStreams.Broker.Api/Properties/launchSettings.json
@@ -1,19 +1,39 @@
{
"profiles": {
- "CloudStreams.Broker.Api": {
+ "http": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "CLOUDSTREAMS_BROKER_NAME": "broker-1"
},
- "applicationUrl": "https://localhost:52552;http://localhost:52553"
+ "dotnetRunMessages": true,
+ "applicationUrl": "http://localhost:5225"
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
},
- "Docker": {
+ "Container (Dockerfile)": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
- "publishAllPorts": true,
- "useSSL": true
+ "environmentVariables": {
+ "ASPNETCORE_HTTP_PORTS": "8080"
+ },
+ "publishAllPorts": true
+ }
+ },
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:19215",
+ "sslPort": 0
}
}
}
\ No newline at end of file
diff --git a/src/broker/CloudStreams.Broker.Api/appsettings.Development.json b/src/broker/CloudStreams.Broker.Api/appsettings.Development.json
new file mode 100644
index 00000000..6b06496b
--- /dev/null
+++ b/src/broker/CloudStreams.Broker.Api/appsettings.Development.json
@@ -0,0 +1,12 @@
+{
+ "ConnectionStrings": {
+ "eventstore": "esdb://localhost:2113?tls=false&tlsVerifyCert=false",
+ "redis": "localhost:6379"
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/src/broker/CloudStreams.Broker.Api/appsettings.json b/src/broker/CloudStreams.Broker.Api/appsettings.json
new file mode 100644
index 00000000..7b9ce52f
--- /dev/null
+++ b/src/broker/CloudStreams.Broker.Api/appsettings.json
@@ -0,0 +1,44 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "Plugins": {
+ "Sources": [
+ {
+ "name": "event-store",
+ "type": "nuget",
+ "properties": {
+ "packageId": "Neuroglia.Data.Infrastructure.EventSourcing.EventStore"
+ },
+ "filter": {
+ "criteria": [
+ {
+ "implements": "Neuroglia.Data.Infrastructure.EventSourcing.Services.IEventStore, Neuroglia.Data.Infrastructure.EventSourcing.Abstractions"
+ },
+ {
+ "implements": "Neuroglia.Data.Infrastructure.EventSourcing.Services.IProjectionManager, Neuroglia.Data.Infrastructure.EventSourcing.Abstractions"
+ }
+ ]
+ }
+ },
+ {
+ "name": "resource-database",
+ "type": "nuget",
+ "properties": {
+ "packageId": "Neuroglia.Data.Infrastructure.ResourceOriented.Redis"
+ },
+ "filter": {
+ "criteria": [
+ {
+ "implements": "Neuroglia.Data.Infrastructure.ResourceOriented.Services.IDatabase, Neuroglia.Data.Infrastructure.ResourceOriented.Abstractions"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "AllowedHosts": "*"
+}
diff --git a/src/broker/CloudStreams.Broker.Application/CloudStreams.Broker.Application.csproj b/src/broker/CloudStreams.Broker.Application/CloudStreams.Broker.Application.csproj
index fceb5e83..4cfea77a 100644
--- a/src/broker/CloudStreams.Broker.Application/CloudStreams.Broker.Application.csproj
+++ b/src/broker/CloudStreams.Broker.Application/CloudStreams.Broker.Application.csproj
@@ -1,43 +1,28 @@
-
+
- net7.0
+ net8.0
enable
enable
- True
- Library
0.14.0
$(VersionPrefix)
$(VersionPrefix)
en
+ true
+ True
Apache-2.0
Copyright © 2023-Present The Cloud Streams Authors. All rights reserved.
https://github.com/neuroglia-io/cloud-streams
- README.md
https://github.com/neuroglia-io/cloud-streams
git
+ true
-
-
-
-
-
-
-
-
- \
- True
-
-
- \
- True
-
-
-
-
-
+
+
+
+
diff --git a/src/broker/CloudStreams.Broker.Application/Configuration/BrokerOptions.cs b/src/broker/CloudStreams.Broker.Application/Configuration/BrokerOptions.cs
index 7fb916f1..616e8acc 100644
--- a/src/broker/CloudStreams.Broker.Application/Configuration/BrokerOptions.cs
+++ b/src/broker/CloudStreams.Broker.Application/Configuration/BrokerOptions.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
namespace CloudStreams.Broker.Application.Configuration;
///
-/// Represents the options used to configure a Cloud Streams cloud event gateway
+/// Represents the options used to configure a Cloud Streams cloud event broker
///
public class BrokerOptions
{
@@ -25,12 +25,12 @@ public class BrokerOptions
public const string EnvironmentVariablePrefix = "CLOUDSTREAMS_BROKER_";
///
- /// Gets/sets the gateway's name
+ /// Gets/sets the broker's name
///
public virtual string Name { get; set; } = null!;
///
- /// Gets/sets the gateway's namespace
+ /// Gets/sets the broker's namespace
///
public virtual string? Namespace { get; set; } = null!;
diff --git a/src/broker/CloudStreams.Broker.Application/Properties/launchSettings.json b/src/broker/CloudStreams.Broker.Application/Properties/launchSettings.json
deleted file mode 100644
index 43f002b9..00000000
--- a/src/broker/CloudStreams.Broker.Application/Properties/launchSettings.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "profiles": {
- "CloudStreamsBroker.Application": {
- "commandName": "Project",
- "launchBrowser": true,
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- },
- "applicationUrl": "https://localhost:55562;http://localhost:55563"
- }
- }
-}
\ No newline at end of file
diff --git a/src/broker/CloudStreams.Broker.Application/Services/SubscriptionHandler.cs b/src/broker/CloudStreams.Broker.Application/Services/SubscriptionHandler.cs
index 9ce200b1..5380eda6 100644
--- a/src/broker/CloudStreams.Broker.Application/Services/SubscriptionHandler.cs
+++ b/src/broker/CloudStreams.Broker.Application/Services/SubscriptionHandler.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -11,57 +11,50 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using CloudStreams.Core.Data;
-using CloudStreams.Core.Infrastructure;
-using CloudStreams.Core.Infrastructure.Services;
using FluentValidation;
using Grpc.Core;
-using Hylo;
-using Hylo.Infrastructure.Services;
-using Json.Patch;
using Polly;
using Polly.CircuitBreaker;
-using System.Net;
-using System.Net.Mime;
-using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
using System.Text.RegularExpressions;
namespace CloudStreams.Broker.Application.Services;
///
-/// Represents a service used to handle a
+/// Represents a service used to handle a
///
public class SubscriptionHandler
: IAsyncDisposable
{
- private IDisposable? _Subscription;
- private bool _Disposed;
+ IDisposable? _Subscription;
+ bool _Disposed;
///
/// Initializes a new
///
/// The service used to create s
/// The service used to manage the application's lifetime
- /// The service used to store s
+ /// The service used to serialize/deserialize data to/from JSON
+ /// The service used to store s
/// The service used to manage s
- /// The service used to control resources
- /// The service used to monitor the current
- /// The service used to evaluate runtime expressions
+ /// The service used to control resources
+ /// The service used to monitor the current
+ /// The service used to evaluate runtime expressions
/// An containing registered s
/// The service used to perform HTTP requests
- /// The to dispatch s to
- public SubscriptionHandler(ILoggerFactory loggerFactory, IHostApplicationLifetime hostApplicationLifetime, IEventStoreProvider eventStoreProvider, IRepository resourceRepository, IResourceController subscriptionController,
- IResourceMonitor broker, IExpressionEvaluatorProvider expressionEvaluatorProvider, IEnumerable> cloudEventValidators, HttpClient httpClient, Subscription subscription)
+ /// The to dispatch s to
+ public SubscriptionHandler(ILoggerFactory loggerFactory, IHostApplicationLifetime hostApplicationLifetime, IJsonSerializer serializer, ICloudEventStore cloudEventStore, IRepository resourceRepository, IResourceController subscriptionController,
+ IResourceMonitor broker, IExpressionEvaluator expressionEvaluator, IEnumerable> cloudEventValidators, HttpClient httpClient, Subscription subscription)
{
this.Logger = loggerFactory.CreateLogger(this.GetType());
this.HostApplicationLifetime = hostApplicationLifetime;
- this.EventStoreProvider = eventStoreProvider;
+ this.Serializer = serializer;
+ this.EventStore = cloudEventStore;
this.ResourceRepository = resourceRepository;
this.SubscriptionController = subscriptionController;
this.Broker = broker;
- this.ExpressionEvaluatorProvider = expressionEvaluatorProvider;
+ this.ExpressionEvaluator = expressionEvaluator;
this.CloudEventValidators = cloudEventValidators;
this.HttpClient = httpClient;
this.Subscription = subscription;
@@ -80,9 +73,14 @@ public SubscriptionHandler(ILoggerFactory loggerFactory, IHostApplicationLifetim
protected IHostApplicationLifetime HostApplicationLifetime { get; }
///
- /// Gets the service used to store s
+ /// Gets the service used to serialize/deserialize data to/from JSON
///
- protected IEventStoreProvider EventStoreProvider { get; }
+ protected IJsonSerializer Serializer { get; }
+
+ ///
+ /// Gets the service used to stream s
+ ///
+ protected ICloudEventStore EventStore { get; }
///
/// Gets the service used to manage s
@@ -90,19 +88,19 @@ public SubscriptionHandler(ILoggerFactory loggerFactory, IHostApplicationLifetim
protected IRepository ResourceRepository { get; }
///
- /// Gets the service used to control resources
+ /// Gets the service used to control resources
///
protected IResourceController SubscriptionController { get; }
///
- /// Gets the service used to monitor the current
+ /// Gets the service used to monitor the current
///
- protected IResourceMonitor Broker { get; }
+ protected IResourceMonitor Broker { get; }
///
/// Gets the service used to evaluate runtime expressions
///
- protected IExpressionEvaluatorProvider ExpressionEvaluatorProvider { get; }
+ protected IExpressionEvaluator ExpressionEvaluator { get; }
///
/// Gets an containing registered s
@@ -115,7 +113,7 @@ public SubscriptionHandler(ILoggerFactory loggerFactory, IHostApplicationLifetim
protected HttpClient HttpClient { get; }
///
- /// Gets the to dispatch s to
+ /// Gets the to dispatch s to
///
protected Subscription Subscription { get; private set; }
@@ -202,7 +200,7 @@ public virtual async Task InitializeAsync(CancellationToken cancellationToken)
}
///
- /// Initializes the 's
+ /// Initializes the 's
///
/// A new awaitable
protected virtual async Task InitializeCloudEventStreamAsync()
@@ -223,33 +221,58 @@ protected virtual async Task InitializeCloudEventStreamAsync()
{
try
{
- this.StreamOffset = (await this.EventStoreProvider.GetEventStore().GetStreamMetadataAsync(this.StreamInitializationCancellationTokenSource.Token).ConfigureAwait(false)).Length;
+ this.StreamOffset = (await this.EventStore.GetStreamMetadataAsync(this.StreamInitializationCancellationTokenSource.Token).ConfigureAwait(false)).Length;
}
- catch (HyloException ex) when (ex.Problem.Status == (int)HttpStatusCode.NotFound)
+ catch (ProblemDetailsException ex) when (ex.Problem.Status == (int)HttpStatusCode.NotFound)
{
this.StreamOffset = 0;
}
if (offset >= 0 && (ulong)offset == this.StreamOffset) offset = -1;
- this.CloudEventStream = await this.EventStoreProvider.GetEventStore().SubscribeAsync(offset, this.StreamInitializationCancellationTokenSource.Token).ConfigureAwait(false);
+ while (true)
+ {
+ try
+ {
+ this.CloudEventStream = await this.EventStore.ObserveAsync(offset, this.StreamInitializationCancellationTokenSource.Token).ConfigureAwait(false);
+ break;
+ }
+ catch (StreamNotFoundException)
+ {
+ var delay = 5000;
+ this.Logger.LogDebug("Failed to observe the cloud event stream because the first cloud event is yet to be published. Retrying in {delay} milliseconds...", delay);
+ await Task.Delay(delay).ConfigureAwait(false);
+ }
+ }
}
else
{
try
{
- this.StreamOffset = (await this.EventStoreProvider.GetEventStore().GetPartitionMetadataAsync(this.Subscription.Spec.Partition, this.StreamInitializationCancellationTokenSource.Token).ConfigureAwait(false)).Length;
+ this.StreamOffset = (await this.EventStore.GetPartitionMetadataAsync(this.Subscription.Spec.Partition, this.StreamInitializationCancellationTokenSource.Token).ConfigureAwait(false)).Length;
}
- catch (HyloException ex) when (ex.Problem.Status == (int)HttpStatusCode.NotFound)
+ catch (ProblemDetailsException ex) when (ex.Problem.Status == (int)HttpStatusCode.NotFound)
{
this.StreamOffset = 0;
}
if (offset >= 0 && (ulong)offset == this.StreamOffset) offset = -1;
- this.CloudEventStream = await this.EventStoreProvider.GetEventStore().SubscribeToPartitionAsync(this.Subscription.Spec.Partition, offset, this.StreamInitializationCancellationTokenSource.Token).ConfigureAwait(false);
-
+ while (true)
+ {
+ try
+ {
+ this.CloudEventStream = await this.EventStore.ObservePartitionAsync(this.Subscription.Spec.Partition, offset, this.StreamInitializationCancellationTokenSource.Token).ConfigureAwait(false);
+ break;
+ }
+ catch (StreamNotFoundException)
+ {
+ var delay = 5000;
+ this.Logger.LogDebug("Failed to observe the cloud event stream because the first cloud event is yet to be published. Retrying in {delay} milliseconds...", delay);
+ await Task.Delay(delay).ConfigureAwait(false);
+ }
+ }
}
- this._Subscription = this.CloudEventStream.Where(this.Filters).SubscribeAsync(this.OnCloudEventAsync, onErrorAsync: this.OnSubscriptionErrorAsync, null);
+ this._Subscription = this.CloudEventStream.ToAsyncEnumerable().WhereAwait(this.FiltersAsync).ToObservable().SubscribeAsync(this.OnCloudEventAsync, onErrorAsync: this.OnSubscriptionErrorAsync, null);
if (offset != StreamPosition.EndOfStream && (ulong)offset < this.StreamOffset) _ = this.CatchUpAsync().ConfigureAwait(false);
}
- catch (Exception ex) when (ex is ObjectDisposedException || ex is TaskCanceledException || ex is OperationCanceledException || (ex is RpcException rpcex && rpcex.StatusCode == StatusCode.Cancelled))
+ catch (Exception ex) when (ex is ObjectDisposedException || ex is TaskCanceledException || ex is OperationCanceledException || (ex is RpcException rpcException && rpcException.StatusCode == StatusCode.Cancelled))
{
if (this.StreamInitializationTaskCompletionSource?.Task.IsCompleted == false && this.StreamInitializationCancellationTokenSource != null && !this.StreamInitializationCancellationTokenSource.IsCancellationRequested) this.StreamInitializationCancellationTokenSource.Cancel();
}
@@ -265,54 +288,54 @@ protected virtual async Task InitializeCloudEventStreamAsync()
}
///
- /// Determines whether or not the filters the specified
+ /// Determines whether or not the filters the specified
///
/// The to filter
- /// A boolean indicating whether or not the filters the specified
- protected virtual bool Filters(CloudEventRecord e)
+ /// A boolean indicating whether or not the filters the specified
+ protected virtual ValueTask FiltersAsync(CloudEventRecord e)
{
- if(e == null) throw new ArgumentNullException(nameof(e));
- if (this.Subscription.Spec.Filter == null) return true;
+ ArgumentNullException.ThrowIfNull(e);
+ if (this.Subscription.Spec.Filter == null) return ValueTask.FromResult(true);
return this.Subscription.Spec.Filter.Type switch
{
- CloudEventFilterType.Attributes => this.Filters(e, this.Subscription.Spec.Filter.Attributes!),
- CloudEventFilterType.Expression => this.Filters(e, this.Subscription.Spec.Filter.Expression!),
+ CloudEventFilterType.Attributes => this.FiltersAsync(e, this.Subscription.Spec.Filter.Attributes!),
+ CloudEventFilterType.Expression => this.FiltersAsync(e, this.Subscription.Spec.Filter.Expression!),
_ => throw new NotSupportedException($"The specified {nameof(CloudEventFilterType)} '{EnumHelper.Stringify(this.Subscription.Spec.Filter.Type)}' is not supported")
};
}
///
- /// Determines whether or not the filters the specified
+ /// Determines whether or not the filters the specified
///
/// The to filter
/// An containing the key/value mappings of the attributes to filter the specified by
- /// A boolean indicating whether or not the filters the specified
- protected virtual bool Filters(CloudEventRecord e, IDictionary attributeFilters)
+ /// A boolean indicating whether or not the filters the specified
+ protected virtual async ValueTask FiltersAsync(CloudEventRecord e, IDictionary attributeFilters)
{
- if (e == null) throw new ArgumentNullException(nameof(e));
- if (attributeFilters == null) throw new ArgumentNullException(nameof(attributeFilters));
+ ArgumentNullException.ThrowIfNull(e);
+ ArgumentNullException.ThrowIfNull(attributeFilters);
var attributes = e.Metadata.ContextAttributes.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToString());
foreach (var attributeFilter in attributeFilters)
{
if (!attributes.TryGetValue(attributeFilter.Key, out var attributeValue) || string.IsNullOrWhiteSpace(attributeValue)) return false;
if (string.IsNullOrWhiteSpace(attributeFilter.Value)) continue;
- if (attributeValue.IsRuntimeExpression() && !this.ExpressionEvaluatorProvider.GetExpressionEvaluator().EvaluateCondition(attributeFilter.Value, attributeValue)) return false;
+ if (attributeValue.IsRuntimeExpression() && !await this.ExpressionEvaluator.EvaluateConditionAsync(attributeFilter.Value, attributeValue, cancellationToken: this.CancellationToken).ConfigureAwait(false)) return false;
else if (!Regex.IsMatch(attributeValue, attributeFilter.Value)) return false;
}
return true;
}
///
- /// Determines whether or not the filters the specified
+ /// Determines whether or not the filters the specified
///
/// The to filter
/// A runtime expression used to determine whether or not the specified should be dispatched to subscribers
- /// A boolean indicating whether or not the filters the specified
- protected virtual bool Filters(CloudEventRecord e, string expression)
+ /// A boolean indicating whether or not the filters the specified
+ protected virtual async ValueTask FiltersAsync(CloudEventRecord e, string expression)
{
- if (e == null) throw new ArgumentNullException(nameof(e));
+ ArgumentNullException.ThrowIfNull(e);
if (string.IsNullOrWhiteSpace(expression)) throw new ArgumentNullException(nameof(expression));
- return this.ExpressionEvaluatorProvider.GetExpressionEvaluator().EvaluateCondition(expression, e);
+ return await this.ExpressionEvaluator.EvaluateConditionAsync(expression, e, cancellationToken: this.CancellationToken).ConfigureAwait(false);
}
///
@@ -322,7 +345,7 @@ protected virtual bool Filters(CloudEventRecord e, string expression)
/// The mutated
protected virtual async Task MutateAsync(CloudEvent e)
{
- if (e == null) throw new ArgumentNullException(nameof(e));
+ ArgumentNullException.ThrowIfNull(e);
if(this.Subscription.Spec.Mutation == null) return e.Clone()!;
var mutated = this.Subscription.Spec.Mutation.Type switch
{
@@ -340,12 +363,12 @@ protected virtual async Task MutateAsync(CloudEvent e)
/// The to mutate
/// An object used to configure the mutation to perform
/// The mutated
- protected virtual Task MutateAsync(CloudEvent e, object mutation)
+ protected virtual async Task MutateAsync(CloudEvent e, object mutation)
{
- if (e == null) throw new ArgumentNullException(nameof(e));
- if (mutation == null) throw new ArgumentNullException(nameof(mutation));
- if (mutation is string expression) return Task.FromResult(this.ExpressionEvaluatorProvider.GetExpressionEvaluator().Evaluate(expression, e));
- else return Task.FromResult(this.ExpressionEvaluatorProvider.GetExpressionEvaluator().Mutate(mutation, e));
+ ArgumentNullException.ThrowIfNull(e);
+ ArgumentNullException.ThrowIfNull(mutation);
+ if (mutation is string expression) return await this.ExpressionEvaluator.EvaluateAsync(expression, e);
+ else return await this.ExpressionEvaluator.EvaluateAsync(mutation, e);
}
///
@@ -356,8 +379,8 @@ protected virtual async Task MutateAsync(CloudEvent e)
/// The mutated
protected virtual async Task MutateAsync(CloudEvent e, Webhook webhook)
{
- if (e == null) throw new ArgumentNullException(nameof(e));
- if (webhook == null) throw new ArgumentNullException(nameof(webhook));
+ ArgumentNullException.ThrowIfNull(e);
+ ArgumentNullException.ThrowIfNull(webhook);
using var requestContent = e.ToHttpContent();
using var request = new HttpRequestMessage(HttpMethod.Post, webhook.ServiceUri) { Content = requestContent };
request.Headers.Accept.Add(new(MediaTypeNames.Application.Json));
@@ -365,7 +388,7 @@ protected virtual async Task MutateAsync(CloudEvent e)
var responseContent = await response.Content.ReadAsStringAsync(this.CancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
if (response.Content.Headers.ContentType?.MediaType == MediaTypeNames.Application.Json) throw new Exception($"Unexpected HTTP response's content type: {response.Content.Headers.ContentType?.MediaType}"); //todo: better feedback
- return Serializer.Json.Deserialize(responseContent);
+ return this.Serializer.Deserialize(responseContent);
}
///
@@ -375,10 +398,10 @@ protected virtual async Task MutateAsync(CloudEvent e)
/// A new awaitable
protected virtual async Task ValidateAsync(CloudEvent e)
{
- if(e == null) throw new ArgumentNullException(nameof(e));
+ ArgumentNullException.ThrowIfNull(e);
var validationTasks = this.CloudEventValidators.Select(v => v.ValidateAsync(e, this.CancellationToken));
await Task.WhenAll(validationTasks).ConfigureAwait(false);
- if (validationTasks.Any(t => !t.Result.IsValid)) throw new FormatException("Failed to validate the specified cloud event"); //todo: better feeback
+ if (validationTasks.Any(t => !t.Result.IsValid)) throw new FormatException("Failed to validate the specified cloud event"); //todo: better feedback
}
///
@@ -390,9 +413,9 @@ protected virtual async Task ValidateAsync(CloudEvent e)
/// A new awaitable
protected virtual async Task DispatchAsync(CloudEventRecord e, bool retryOnError, bool catchUpWhenAvailable)
{
- if (e == null) throw new ArgumentNullException(nameof(e));
+ ArgumentNullException.ThrowIfNull(e);
var cloudEvent = e.ToCloudEvent(this.Broker.Resource.Spec.Dispatch?.Sequencing);
- if (!this.Filters(e)) return;
+ if (!await this.FiltersAsync(e).ConfigureAwait(false)) return;
cloudEvent = await this.MutateAsync(cloudEvent).ConfigureAwait(false);
await this.DispatchAsync(cloudEvent, e.Sequence, retryOnError, catchUpWhenAvailable).ConfigureAwait(false);
}
@@ -436,16 +459,16 @@ protected virtual async Task RetryDispatchAsync(CloudEvent e, ulong offset, bool
this.SubscriberAvailable = false;
this.SubscriptionOutOfSync = true;
var policyConfiguration = this.Subscription.Spec.Subscriber.RetryPolicy ?? this.DefaultRetryPolicy;
- var exceptionPreficate = (HttpRequestException ex) => policyConfiguration.StatusCodes == null || !policyConfiguration.StatusCodes.Any() || (ex.StatusCode.HasValue && ex.StatusCode.HasValue && policyConfiguration.StatusCodes.Contains((int)ex.StatusCode.Value));
+ var exceptionPredicate = (HttpRequestException ex) => policyConfiguration.StatusCodes == null || policyConfiguration.StatusCodes.Count == 0 || (ex.StatusCode.HasValue && ex.StatusCode.HasValue && policyConfiguration.StatusCodes.Contains((int)ex.StatusCode.Value));
- AsyncCircuitBreakerPolicy? circuitBreakerPolicy = policyConfiguration.CircuitBreaker == null ? null : Policy.Handle(exceptionPreficate)
- .CircuitBreakerAsync(policyConfiguration.CircuitBreaker.BreakAfter, policyConfiguration.CircuitBreaker.BreakDuration);
+ AsyncCircuitBreakerPolicy? circuitBreakerPolicy = policyConfiguration.CircuitBreaker == null ? null : Policy.Handle(exceptionPredicate)
+ .CircuitBreakerAsync(policyConfiguration.CircuitBreaker.BreakAfter, policyConfiguration.CircuitBreaker.BreakDuration.ToTimeSpan());
AsyncPolicy retryPolicy = policyConfiguration.MaxAttempts.HasValue ?
- Policy.Handle(exceptionPreficate)
- .WaitAndRetryAsync(policyConfiguration.MaxAttempts.Value, policyConfiguration.BackoffDuration.ForAttempt)
- : Policy.Handle(exceptionPreficate)
- .WaitAndRetryForeverAsync(policyConfiguration.BackoffDuration.ForAttempt);
+ Policy.Handle(exceptionPredicate)
+ .WaitAndRetryAsync(policyConfiguration.MaxAttempts.Value, attempt => policyConfiguration.BackoffDuration == null ? TimeSpan.FromSeconds(3) : policyConfiguration.BackoffDuration.ForAttempt(attempt))
+ : Policy.Handle(exceptionPredicate)
+ .WaitAndRetryForeverAsync(attempt => policyConfiguration.BackoffDuration == null ? TimeSpan.FromSeconds(3) : policyConfiguration.BackoffDuration.ForAttempt(attempt));
retryPolicy = circuitBreakerPolicy == null ? retryPolicy : retryPolicy.WrapAsync(circuitBreakerPolicy);
await retryPolicy.ExecuteAsync(async _ => await this.DispatchAsync(e, offset, false, catchUpWhenAvailable), this.CancellationToken).ConfigureAwait(false);
@@ -470,15 +493,14 @@ protected virtual async Task CatchUpAsync()
this.SubscriptionOutOfSync = true;
this.StreamInitializationTaskCompletionSource ??= new();
var currentOffset = this.Subscription.GetOffset();
- var eventStore = this.EventStoreProvider.GetEventStore();
if (currentOffset == StreamPosition.EndOfStream) currentOffset = this.Subscription.Spec.Partition == null ?
- (long)(await eventStore.ReadOneAsync(StreamReadDirection.Backwards, StreamPosition.EndOfStream, this.StreamInitializationCancellationTokenSource!.Token).ConfigureAwait(false))!.Sequence
- : (long)(await eventStore.ReadPartitionAsync(this.Subscription.Spec.Partition, StreamReadDirection.Backwards, StreamPosition.EndOfStream, 1, this.StreamInitializationCancellationTokenSource!.Token).SingleAsync(this.StreamInitializationCancellationTokenSource!.Token).ConfigureAwait(false))!.Sequence;
+ (long)(await this.EventStore.ReadOneAsync(StreamReadDirection.Backwards, StreamPosition.EndOfStream, this.StreamInitializationCancellationTokenSource!.Token).ConfigureAwait(false))!.Sequence
+ : (long)(await this.EventStore.ReadPartitionAsync(this.Subscription.Spec.Partition, StreamReadDirection.Backwards, StreamPosition.EndOfStream, 1, this.StreamInitializationCancellationTokenSource!.Token).SingleAsync(this.StreamInitializationCancellationTokenSource!.Token).ConfigureAwait(false))!.Sequence;
do
{
var record = this.Subscription.Spec.Partition == null ?
- await eventStore.ReadOneAsync(StreamReadDirection.Forwards, currentOffset!, this.StreamInitializationCancellationTokenSource!.Token).ConfigureAwait(false)
- : await eventStore.ReadPartitionAsync(this.Subscription.Spec.Partition, StreamReadDirection.Forwards, currentOffset, 1, this.StreamInitializationCancellationTokenSource!.Token).SingleOrDefaultAsync(this.StreamInitializationCancellationTokenSource!.Token).ConfigureAwait(false);
+ await this.EventStore.ReadOneAsync(StreamReadDirection.Forwards, currentOffset!, this.StreamInitializationCancellationTokenSource!.Token).ConfigureAwait(false)
+ : await this.EventStore.ReadPartitionAsync(this.Subscription.Spec.Partition, StreamReadDirection.Forwards, currentOffset, 1, this.StreamInitializationCancellationTokenSource!.Token).SingleOrDefaultAsync(this.StreamInitializationCancellationTokenSource!.Token).ConfigureAwait(false);
if (record == null)
{
await Task.Delay(50);
@@ -490,11 +512,8 @@ await eventStore.ReadOneAsync(StreamReadDirection.Forwards, currentOffset!, this
while (!this.StreamInitializationCancellationTokenSource.Token.IsCancellationRequested && (ulong)currentOffset <= this.StreamOffset);
this.SubscriptionOutOfSync = false;
}
- catch (Exception ex) when (ex is ObjectDisposedException || ex is TaskCanceledException || ex is OperationCanceledException || (ex is RpcException rpcex && rpcex.StatusCode == StatusCode.Cancelled)) { }
- finally
- {
- this.StreamInitializationTaskCompletionSource?.SetResult();
- }
+ catch (Exception ex) when (ex is ObjectDisposedException || ex is TaskCanceledException || ex is OperationCanceledException || (ex is RpcException rpcException && rpcException.StatusCode == StatusCode.Cancelled)) { }
+ finally { this.StreamInitializationTaskCompletionSource?.SetResult(); }
}
///
@@ -509,15 +528,15 @@ protected virtual async Task CommitOffsetAsync(ulong? offset)
if (resource.Status.Stream == null) resource.Status.Stream = new();
resource.Status.Stream.AckedOffset = offset;
resource.Status.ObservedGeneration = this.Subscription.Metadata.Generation;
- var patch = JsonPatchHelper.CreateJsonPatchFromDiff(this.Subscription, resource);
+ var patch = JsonPatchUtility.CreateJsonPatchFromDiff(this.Subscription, resource);
if (!patch.Operations.Any()) return;
await this.ResourceRepository.PatchStatusAsync(new Patch(PatchType.JsonPatch, patch), resource.GetName(), resource.GetNamespace(), false, this.CancellationToken).ConfigureAwait(false);
}
///
- /// Sets the 's status phase
+ /// Sets the 's status phase
///
- /// The 's status phase
+ /// The 's status phase
/// A new awaitable
protected virtual async Task SetStatusPhaseAsync(SubscriptionStatusPhase phase)
{
@@ -525,7 +544,7 @@ protected virtual async Task SetStatusPhaseAsync(SubscriptionStatusPhase phase)
if (resource.Status == null) resource.Status = new();
else if (resource.Status.Phase == phase) return;
resource.Status.Phase = phase;
- var patch = JsonPatchHelper.CreateJsonPatchFromDiff(this.Subscription, resource);
+ var patch = JsonPatchUtility.CreateJsonPatchFromDiff(this.Subscription, resource);
if (!patch.Operations.Any()) return;
await this.ResourceRepository.PatchStatusAsync(new Patch(PatchType.JsonPatch, patch), resource.GetName(), resource.GetNamespace(), false, this.CancellationToken).ConfigureAwait(false);
}
@@ -552,7 +571,7 @@ protected virtual void OnDefaultRetryPolicyChanged(HttpClientRetryPolicy retryPo
}
///
- /// Handles changes to the 's offset
+ /// Handles changes to the 's offset
///
/// The desired offset
/// A new awaitable
@@ -566,7 +585,7 @@ protected virtual async Task OnSubscriptionOffsetChangedAsync(long? offset)
if (resource.Status == null) resource.Status = new() { ObservedGeneration = this.Subscription.Metadata.Generation };
if (resource.Status.Stream == null) resource.Status.Stream = new();
resource.Status.Stream.Fault = null;
- var patch = JsonPatchHelper.CreateJsonPatchFromDiff(this.Subscription, resource);
+ var patch = JsonPatchUtility.CreateJsonPatchFromDiff(this.Subscription, resource);
await this.ResourceRepository.PatchStatusAsync(new Patch(PatchType.JsonPatch, patch), resource.GetName(), resource.GetNamespace(), false, this.CancellationToken).ConfigureAwait(false);
return;
}
@@ -624,13 +643,13 @@ protected virtual async Task OnCloudEventAsync(CloudEventRecord e)
protected virtual async Task OnSubscriptionErrorAsync(Exception ex)
{
this.StreamInitializationCancellationTokenSource?.Cancel();
- this.Logger.LogError("An error occured while streaming cloud events for subscription '{subscription}': {ex}", this.Subscription, ex);
+ this.Logger.LogError("An error occurred while streaming cloud events for subscription '{subscription}': {ex}", this.Subscription, ex);
var resource = this.Subscription.Clone()!;
if (resource.Spec.Stream == null) return;
if (resource.Status == null) resource.Status = new() { ObservedGeneration = this.Subscription.Metadata.Generation };
if (resource.Status.Stream == null) resource.Status.Stream = new();
resource.Status.Stream.Fault = ex.ToProblemDetails();
- var patch = JsonPatchHelper.CreateJsonPatchFromDiff(this.Subscription, resource);
+ var patch = JsonPatchUtility.CreateJsonPatchFromDiff(this.Subscription, resource);
await this.ResourceRepository.PatchStatusAsync(new Patch(PatchType.JsonPatch, patch), resource.GetName(), resource.GetNamespace(), false, this.CancellationToken).ConfigureAwait(false);
}
diff --git a/src/broker/CloudStreams.Broker.Application/Services/SubscriptionManager.cs b/src/broker/CloudStreams.Broker.Application/Services/SubscriptionManager.cs
index 1dce9c45..604e02b3 100644
--- a/src/broker/CloudStreams.Broker.Application/Services/SubscriptionManager.cs
+++ b/src/broker/CloudStreams.Broker.Application/Services/SubscriptionManager.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -12,47 +12,39 @@
// limitations under the License.
using CloudStreams.Broker.Application.Configuration;
-using Hylo;
-using Hylo.Infrastructure.Configuration;
-using Hylo.Infrastructure.Services;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
+using Neuroglia.Data.Infrastructure.ResourceOriented.Configuration;
using System.Collections.Concurrent;
-using System.Net;
-using System.Reactive.Linq;
namespace CloudStreams.Broker.Application.Services;
///
/// Represents a service used to manage s
///
-public class SubscriptionManager
- : ResourceController
+///
+/// Initializes a new
+///
+/// The current
+/// The service used to create s
+/// The service used to access the current
+/// The service used to manage s
+/// The service used to access the current
+public class SubscriptionManager(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IOptions> controllerOptions, IRepository repository, IOptions brokerOptions)
+ : ResourceController(loggerFactory, controllerOptions, repository)
{
- ///
- /// Initializes a new
- ///
- /// The current
- /// The service used to create s
- /// The service used to access the current
- /// The service used to manage s
- /// The service used to access the current
- public SubscriptionManager(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IOptions> controllerOptions, IRepository repository, IOptions brokerOptions)
- : base(loggerFactory, controllerOptions, repository)
- {
- this.ServiceProvider = serviceProvider;
- this.BrokerOptions = brokerOptions.Value;
- }
+ readonly List _lockedKeys = [];
///
/// Gets the current
///
- protected IServiceProvider ServiceProvider { get; }
+ protected IServiceProvider ServiceProvider { get; } = serviceProvider;
///
- /// Gets the running 's options
+ /// Gets the running 's options
///
- protected BrokerOptions BrokerOptions { get; }
+ protected BrokerOptions BrokerOptions { get; } = brokerOptions.Value;
///
/// Gets a containing the key/value mappings of handled s
@@ -60,9 +52,9 @@ public SubscriptionManager(IServiceProvider serviceProvider, ILoggerFactory logg
protected ConcurrentDictionary Subscriptions { get; } = new();
///
- /// Gets the service used to monitor the current
+ /// Gets the service used to monitor the current
///
- protected IResourceMonitor? Broker { get; private set; }
+ protected IResourceMonitor? Broker { get; private set; }
///
/// Gets the 's
@@ -72,17 +64,17 @@ public SubscriptionManager(IServiceProvider serviceProvider, ILoggerFactory logg
///
public override async Task StartAsync(CancellationToken cancellationToken)
{
- Core.Data.Broker? broker = null;
+ Core.Resources.Broker? broker = null;
try
{
- broker = await this.Repository.GetAsync(this.BrokerOptions.Name, this.BrokerOptions.Namespace, cancellationToken).ConfigureAwait(false);
+ broker = await this.Repository.GetAsync(this.BrokerOptions.Name, this.BrokerOptions.Namespace, cancellationToken).ConfigureAwait(false);
}
- catch (HyloException ex) when (ex.Problem.Status == (int)HttpStatusCode.NotFound) { }
+ catch (ProblemDetailsException ex) when (ex.Problem.Status == (int)HttpStatusCode.NotFound) { }
finally
{
if (broker == null)
{
- broker = new Core.Data.Broker(new ResourceMetadata(this.BrokerOptions.Name, this.BrokerOptions.Namespace), new BrokerSpec()
+ broker = new Core.Resources.Broker(new ResourceMetadata(this.BrokerOptions.Name, this.BrokerOptions.Namespace), new BrokerSpec()
{
Dispatch = new()
{
@@ -91,7 +83,7 @@ public override async Task StartAsync(CancellationToken cancellationToken)
});
broker = await this.Repository.AddAsync(broker, false, cancellationToken).ConfigureAwait(false);
}
- this.Broker = await this.Repository.MonitorAsync(this.BrokerOptions.Name, this.BrokerOptions.Namespace, false, cancellationToken).ConfigureAwait(false);
+ this.Broker = await this.Repository.MonitorAsync(this.BrokerOptions.Name, this.BrokerOptions.Namespace, false, cancellationToken).ConfigureAwait(false);
}
await base.StartAsync(cancellationToken).ConfigureAwait(false);
foreach (var subscription in this.Resources.Values.ToList())
@@ -136,6 +128,7 @@ protected virtual async Task OnSubscriptionLabelChangedAsync(Subscription subscr
{
if (this.Broker == null) return;
var key = this.GetSubscriptionHandlerCacheKey(subscription.GetName(), subscription.GetNamespace());
+ if (this._lockedKeys.Contains(key)) return;
if (this.Options.LabelSelectors == null || this.Options.LabelSelectors.All(s => s.Selects(subscription)) == true)
{
if (this.Subscriptions.TryGetValue(key, out _)) return;
@@ -164,9 +157,11 @@ protected override async Task OnResourceDeletedAsync(Subscription subscription,
protected virtual async Task OnSubscriptionCreatedAsync(Subscription subscription)
{
var key = this.GetSubscriptionHandlerCacheKey(subscription.GetName(), subscription.GetNamespace());
+ this._lockedKeys.Add(key);
var handler = ActivatorUtilities.CreateInstance(this.ServiceProvider, subscription, this.Broker!);
await handler.InitializeAsync(this.CancellationToken).ConfigureAwait(false);
this.Subscriptions.AddOrUpdate(key, handler, (_, _) => handler);
+ this._lockedKeys.Remove(key);
}
///
diff --git a/src/broker/CloudStreams.Broker.Application/Usings.cs b/src/broker/CloudStreams.Broker.Application/Usings.cs
index fd1fa627..f06a5256 100644
--- a/src/broker/CloudStreams.Broker.Application/Usings.cs
+++ b/src/broker/CloudStreams.Broker.Application/Usings.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -12,4 +12,21 @@
// limitations under the License.
global using CloudStreams.Core;
-global using CloudStreams.Core.Data;
+global using CloudStreams.Core.Application;
+global using CloudStreams.Core.Application.Services;
+global using CloudStreams.Core.Resources;
+global using Microsoft.Extensions.Hosting;
+global using Microsoft.Extensions.Logging;
+global using Neuroglia;
+global using Neuroglia.Data;
+global using Neuroglia.Data.Expressions;
+global using Neuroglia.Data.Expressions.Services;
+global using Neuroglia.Data.Infrastructure.EventSourcing;
+global using Neuroglia.Data.Infrastructure.ResourceOriented;
+global using Neuroglia.Data.Infrastructure.ResourceOriented.Services;
+global using Neuroglia.Eventing.CloudEvents;
+global using Neuroglia.Reactive;
+global using Neuroglia.Serialization;
+global using System.Net;
+global using System.Net.Mime;
+global using System.Reactive.Linq;
\ No newline at end of file
diff --git a/src/core/CloudStreams.Core.Api.Client/CloudStreams.Core.Api.Client.csproj b/src/core/CloudStreams.Core.Api.Client/CloudStreams.Core.Api.Client.csproj
index ac902892..8b6f43ec 100644
--- a/src/core/CloudStreams.Core.Api.Client/CloudStreams.Core.Api.Client.csproj
+++ b/src/core/CloudStreams.Core.Api.Client/CloudStreams.Core.Api.Client.csproj
@@ -1,47 +1,28 @@
-
+
- net7.0
+ net8.0
enable
enable
- True
0.14.0
$(VersionPrefix)
$(VersionPrefix)
en
+ true
+ True
+ Apache-2.0
+ Copyright © 2023-Present The Cloud Streams Authors. All rights reserved.
https://github.com/neuroglia-io/cloud-streams
- README.md
https://github.com/neuroglia-io/cloud-streams
git
- Copyright © 2023 - Present The Cloud Streams Authors. All rights reserverd
- cloudstreams;core;api;client
- Contains the client implementation of the CloudStreams Core API
- logo.png
- Apache-2.0
- True
- True
+ true
-
- \
- True
-
-
- \
- True
-
+
+
-
-
-
-
-
-
-
-
-
diff --git a/src/core/CloudStreams.Core.Api.Client/Configuration/CoreApiClientOptions.cs b/src/core/CloudStreams.Core.Api.Client/Configuration/CoreApiClientOptions.cs
index 083c90d8..b802542e 100644
--- a/src/core/CloudStreams.Core.Api.Client/Configuration/CoreApiClientOptions.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Configuration/CoreApiClientOptions.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
diff --git a/src/core/CloudStreams.Core.Api.Client/Extensions/ICloudStreamsResourceManagementApiClientExtensions.cs b/src/core/CloudStreams.Core.Api.Client/Extensions/ICloudStreamsResourceManagementApiClientExtensions.cs
index 26099745..e88f1c3d 100644
--- a/src/core/CloudStreams.Core.Api.Client/Extensions/ICloudStreamsResourceManagementApiClientExtensions.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Extensions/ICloudStreamsResourceManagementApiClientExtensions.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
diff --git a/src/core/CloudStreams.Core.Api.Client/Extensions/IServiceCollectionExtensions.cs b/src/core/CloudStreams.Core.Api.Client/Extensions/IServiceCollectionExtensions.cs
index 0847c6d5..1e7d9f5e 100644
--- a/src/core/CloudStreams.Core.Api.Client/Extensions/IServiceCollectionExtensions.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Extensions/IServiceCollectionExtensions.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -40,7 +40,7 @@ public static IServiceCollection AddCloudStreamsCoreApiClient(this IServiceColle
{
var options = provider.GetRequiredService>().Value;
var connection = new HubConnectionBuilder()
- .WithUrl($"{options.BaseAddress}api/resource-management/v1/ws/watch")
+ .WithUrl($"{options.BaseAddress}api/ws/resources/watch")
.WithAutomaticReconnect()
.Build();
return new ResourceWatchEventHubClient(connection);
diff --git a/src/core/CloudStreams.Core.Api.Client/Services/CloudStreamsCoreApiClient.CloudEvents.cs b/src/core/CloudStreams.Core.Api.Client/Services/CloudStreamsCoreApiClient.CloudEvents.cs
index 8ac73fc0..aadbfef0 100644
--- a/src/core/CloudStreams.Core.Api.Client/Services/CloudStreamsCoreApiClient.CloudEvents.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Services/CloudStreamsCoreApiClient.CloudEvents.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -11,7 +11,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using CloudStreams.Core.Data;
+using Neuroglia;
+using Neuroglia.Eventing.CloudEvents;
+using Neuroglia.Serialization;
namespace CloudStreams.Core.Api.Client.Services;
@@ -36,7 +38,7 @@ public partial class CloudStreamsCoreApiClient
var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, $"{CloudEventPartitionsApiPath}{type}"), cancellationToken).ConfigureAwait(false);
var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- return Serializer.Json.DeserializeAsyncEnumerable(responseStream, cancellationToken: cancellationToken);
+ return this.Serializer.DeserializeAsyncEnumerable(responseStream, cancellationToken: cancellationToken);
}
///
@@ -46,7 +48,7 @@ public partial class CloudStreamsCoreApiClient
using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, $"{CloudEventPartitionsApiPath}{type}/{id}"), cancellationToken).ConfigureAwait(false);
using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
- return Serializer.Json.Deserialize(json);
+ return this.Serializer.Deserialize(json);
}
///
@@ -55,7 +57,7 @@ public virtual async Task GetStreamMetadataAsync(CancellationTok
using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, CloudEventStreamApiPath), cancellationToken).ConfigureAwait(false);
using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
- return Serializer.Json.Deserialize(json)!;
+ return this.Serializer.Deserialize(json)!;
}
///
@@ -75,7 +77,7 @@ public virtual async Task GetStreamMetadataAsync(CancellationTok
var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, uri), cancellationToken).ConfigureAwait(false);
var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- return Serializer.Json.DeserializeAsyncEnumerable(responseStream, cancellationToken: cancellationToken);
+ return this.Serializer.DeserializeAsyncEnumerable(responseStream, cancellationToken: cancellationToken);
}
}
diff --git a/src/core/CloudStreams.Core.Api.Client/Services/CloudStreamsCoreApiClient.ResourceManagement.cs b/src/core/CloudStreams.Core.Api.Client/Services/CloudStreamsCoreApiClient.ResourceManagement.cs
index db461431..acf18889 100644
--- a/src/core/CloudStreams.Core.Api.Client/Services/CloudStreamsCoreApiClient.ResourceManagement.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Services/CloudStreamsCoreApiClient.ResourceManagement.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -11,8 +11,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using CloudStreams.Core.Data;
-
namespace CloudStreams.Core.Api.Client.Services;
public partial class CloudStreamsCoreApiClient
diff --git a/src/core/CloudStreams.Core.Api.Client/Services/CloudStreamsCoreApiClient.cs b/src/core/CloudStreams.Core.Api.Client/Services/CloudStreamsCoreApiClient.cs
index bf92f92b..0314f683 100644
--- a/src/core/CloudStreams.Core.Api.Client/Services/CloudStreamsCoreApiClient.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Services/CloudStreamsCoreApiClient.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -13,6 +13,8 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
+using Neuroglia;
+using Neuroglia.Serialization;
namespace CloudStreams.Core.Api.Client.Services;
@@ -30,11 +32,13 @@ public partial class CloudStreamsCoreApiClient
///
/// The current
/// The service used to create s
+ /// The service used to serialize/deserialize objects to/from JSON
/// The service used to perform http requests
- public CloudStreamsCoreApiClient(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, HttpClient httpClient)
+ public CloudStreamsCoreApiClient(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IJsonSerializer serializer, HttpClient httpClient)
{
this.ServiceProvider = serviceProvider;
this.Logger = loggerFactory.CreateLogger(this.GetType());
+ this.Serializer = serializer;
this.HttpClient = httpClient;
foreach(var apiProperty in this.GetType().GetProperties().Where(p => p.CanRead && p.PropertyType.GetGenericType(typeof(IResourceManagementApi<>)) != null))
{
@@ -55,6 +59,11 @@ public CloudStreamsCoreApiClient(IServiceProvider serviceProvider, ILoggerFactor
///
protected ILogger Logger { get; }
+ ///
+ /// Gets the service used to serialize/deserialize objects to/from JSON
+ ///
+ protected IJsonSerializer Serializer { get; }
+
///
/// Gets the service used to perform http requests
///
@@ -74,7 +83,7 @@ public CloudStreamsCoreApiClient(IServiceProvider serviceProvider, ILoggerFactor
/// The processed
protected virtual Task ProcessRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken = default)
{
- if(request == null) throw new ArgumentNullException(nameof(request));
+ ArgumentNullException.ThrowIfNull(request);
return Task.FromResult(request);
}
@@ -86,7 +95,7 @@ protected virtual Task ProcessRequestAsync(HttpRequestMessag
/// The processed
protected virtual async Task ProcessResponseAsync(HttpResponseMessage response, CancellationToken cancellationToken = default)
{
- if (response == null) throw new ArgumentNullException(nameof(response));
+ ArgumentNullException.ThrowIfNull(response);
if (response.IsSuccessStatusCode) return response;
var content = string.Empty;
if (response.Content != null) content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
@@ -94,7 +103,7 @@ protected virtual async Task ProcessResponseAsync(HttpRespo
if (!response.IsSuccessStatusCode)
{
if (string.IsNullOrWhiteSpace(content)) response.EnsureSuccessStatusCode();
- else throw new CloudStreamsException(Serializer.Json.Deserialize(content));
+ else throw new ProblemDetailsException(this.Serializer.Deserialize(content)!);
}
return response;
}
diff --git a/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/ICloudEventPartitionsApi.cs b/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/ICloudEventPartitionsApi.cs
index cae46196..3f6b013e 100644
--- a/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/ICloudEventPartitionsApi.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/ICloudEventPartitionsApi.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using CloudStreams.Core.Data;
+using Neuroglia.Eventing.CloudEvents;
namespace CloudStreams.Core.Api.Client.Services;
@@ -20,7 +20,7 @@ namespace CloudStreams.Core.Api.Client.Services;
///
public interface ICloudEventPartitionsApi
{
-
+
///
/// Gets the metadata used to describe the specified partition
///
diff --git a/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/ICloudEventStreamApi.cs b/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/ICloudEventStreamApi.cs
index 87121b2a..b1a39c84 100644
--- a/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/ICloudEventStreamApi.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/ICloudEventStreamApi.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using CloudStreams.Core.Data;
+using Neuroglia.Eventing.CloudEvents;
namespace CloudStreams.Core.Api.Client.Services;
diff --git a/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/ICloudEventsApi.cs b/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/ICloudEventsApi.cs
index 412217ab..34b57fd0 100644
--- a/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/ICloudEventsApi.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/ICloudEventsApi.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using CloudStreams.Core.Data;
+using Neuroglia.Eventing.CloudEvents;
namespace CloudStreams.Core.Api.Client.Services;
@@ -20,7 +20,7 @@ namespace CloudStreams.Core.Api.Client.Services;
///
public interface ICloudEventsApi
{
-
+
///
/// Gets the API used to manage partitions
///
diff --git a/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/ICloudStreamsCoreApiClient.cs b/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/ICloudStreamsCoreApiClient.cs
index 2a2ad0e2..3644f365 100644
--- a/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/ICloudStreamsCoreApiClient.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/ICloudStreamsCoreApiClient.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
diff --git a/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/IResourceEventWatchHub.cs b/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/IResourceEventWatchHub.cs
index 13095c03..8a1caab3 100644
--- a/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/IResourceEventWatchHub.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/IResourceEventWatchHub.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
diff --git a/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/IResourceEventWatchHubClient.cs b/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/IResourceEventWatchHubClient.cs
index 2db976a2..706bea14 100644
--- a/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/IResourceEventWatchHubClient.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/IResourceEventWatchHubClient.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
diff --git a/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/IResourceManagementApi.cs b/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/IResourceManagementApi.cs
index 79c212db..0994e9f9 100644
--- a/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/IResourceManagementApi.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/IResourceManagementApi.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -11,6 +11,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using Neuroglia.Data;
+
namespace CloudStreams.Core.Api.Client.Services;
///
diff --git a/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/IResourceManagementApiClient.cs b/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/IResourceManagementApiClient.cs
index 25b2c57f..6fefec40 100644
--- a/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/IResourceManagementApiClient.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Services/Interfaces/IResourceManagementApiClient.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -11,8 +11,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using CloudStreams.Core.Data;
-
namespace CloudStreams.Core.Api.Client.Services;
///
diff --git a/src/core/CloudStreams.Core.Api.Client/Services/ResourceManagementApi.cs b/src/core/CloudStreams.Core.Api.Client/Services/ResourceManagementApi.cs
index 8385ecf3..ca96f1e1 100644
--- a/src/core/CloudStreams.Core.Api.Client/Services/ResourceManagementApi.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Services/ResourceManagementApi.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -12,6 +12,9 @@
// limitations under the License.
using Microsoft.Extensions.Logging;
+using Neuroglia;
+using Neuroglia.Data;
+using Neuroglia.Serialization;
using System.Net.Mime;
using System.Text;
@@ -21,49 +24,45 @@ namespace CloudStreams.Core.Api.Client.Services;
/// Represents the default implementation of the
///
/// The type of s to manage
-public class ResourceManagementApi
+/// The service used to perform logging
+/// The service used to serialize/deserialize objects from/to JSON
+/// The service used to perform HTTP requests
+/// The path to the API used to manage s of the specified type
+public class ResourceManagementApi(ILogger> logger, IJsonSerializer serializer, HttpClient httpClient, string path)
: IResourceManagementApi
where TResource : IResource, new()
{
///
- /// Initializes a new
+ /// Gets the service used to perform logging
///
- /// The service used to create s
- /// The service used to perform HTTP requests
- /// The path to the API used to manage s of the specified type
- public ResourceManagementApi(ILoggerFactory loggerFactory, HttpClient httpClient, string path)
- {
- this.Logger = loggerFactory.CreateLogger(this.GetType());
- this.HttpClient = httpClient;
- this.Path = CloudStreamsCoreApiClient.ResourceManagementApiPath + $"{new TResource().GetVersion()}/{path}";
- }
+ protected ILogger Logger { get; } = logger;
///
- /// Gets the service used to perform logging
+ /// Gets the service used to serialize/deserialize objects from/to JSON
///
- protected ILogger Logger { get; }
+ protected IJsonSerializer Serializer { get; } = serializer;
///
/// Gets the service used to perform HTTP requests
///
- protected HttpClient HttpClient { get; }
+ protected HttpClient HttpClient { get; } = httpClient;
///
/// Gets the path to the API used to manage s of the specified type
///
- protected string Path { get; }
+ protected string Path { get; } = CloudStreamsCoreApiClient.ResourceManagementApiPath + $"{new TResource().GetVersion()}/{path}";
///
public virtual async Task CreateAsync(TResource resource, CancellationToken cancellationToken = default)
{
if (resource == null) throw new ArgumentNullException(nameof(resource));
- var json = Serializer.Json.Serialize(resource);
+ var json = this.Serializer.SerializeToText(resource);
using var content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json);
using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Post, this.Path) { Content = content }, cancellationToken).ConfigureAwait(false);
using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
- return Serializer.Json.Deserialize(json)!;
+ return this.Serializer.Deserialize(json)!;
}
///
@@ -72,7 +71,7 @@ public virtual async Task GetDefinitionAsync(CancellationTok
using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, $"{this.Path}/definition"), cancellationToken).ConfigureAwait(false);
using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
- return Serializer.Json.Deserialize(json)!;
+ return this.Serializer.Deserialize(json)!;
}
///
@@ -83,7 +82,7 @@ public virtual async Task GetAsync(string name, string? @namespace =
using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, uri), cancellationToken).ConfigureAwait(false);
using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
- return Serializer.Json.Deserialize(json)!;
+ return this.Serializer.Deserialize(json)!;
}
///
@@ -93,48 +92,48 @@ public virtual async Task> ListAsync(string? @namesp
var queryStringArguments = new Dictionary();
if (!string.IsNullOrWhiteSpace(@namespace)) queryStringArguments.Add("namespace", @namespace!);
if (labelSelectors?.Any() == true) queryStringArguments.Add(nameof(labelSelectors), labelSelectors.Select(s => s.ToString()).Join(','));
- if (queryStringArguments.Any()) uri += $"?{queryStringArguments.Select(kvp => $"{kvp.Key}={kvp.Value}").Join('&')}";
+ if (queryStringArguments.Count != 0) uri += $"?{queryStringArguments.Select(kvp => $"{kvp.Key}={kvp.Value}").Join('&')}";
var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, uri), cancellationToken).ConfigureAwait(false);
var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- return Serializer.Json.DeserializeAsyncEnumerable(responseStream, cancellationToken: cancellationToken)!;
+ return this.Serializer.DeserializeAsyncEnumerable(responseStream, cancellationToken: cancellationToken)!;
}
///
public virtual async Task UpdateAsync(TResource resource, CancellationToken cancellationToken = default)
{
if (resource == null) throw new ArgumentNullException(nameof(resource));
- var json = Serializer.Json.Serialize(resource);
+ var json = this.Serializer.SerializeToText(resource);
using var content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json);
using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Put, this.Path) { Content = content }, cancellationToken).ConfigureAwait(false);
using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
- return Serializer.Json.Deserialize(json)!;
+ return this.Serializer.Deserialize(json)!;
}
///
public virtual async Task PatchAsync(Patch patch, string name, string? @namespace = null, CancellationToken cancellationToken = default)
{
- if (patch == null) throw new ArgumentNullException(nameof(patch));
+ ArgumentNullException.ThrowIfNull(patch);
var uri = string.IsNullOrWhiteSpace(@namespace) ? $"{this.Path}/{name}" : $"{this.Path}/namespace/{@namespace}/{name}";
- var json = Serializer.Json.Serialize(patch);
+ var json = this.Serializer.SerializeToText(patch);
using var content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json);
using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Patch, uri) { Content = content }, cancellationToken).ConfigureAwait(false);
using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
- return Serializer.Json.Deserialize(json)!;
+ return this.Serializer.Deserialize(json)!;
}
///
public virtual async Task PatchStatusAsync(Patch patch, string name, string? @namespace = null, CancellationToken cancellationToken = default)
{
- if (patch == null) throw new ArgumentNullException(nameof(patch));
- var json = Serializer.Json.Serialize(patch);
+ ArgumentNullException.ThrowIfNull(patch);
+ var json = this.Serializer.SerializeToText(patch);
using var content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json);
using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Patch, $"{this.Path}/status") { Content = content }, cancellationToken).ConfigureAwait(false);
using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
- return Serializer.Json.Deserialize(json)!;
+ return this.Serializer.Deserialize(json)!;
}
///
@@ -154,7 +153,7 @@ public virtual async Task DeleteAsync(string name, string? @namespace = null, Ca
/// The processed
protected virtual Task ProcessRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken = default)
{
- if (request == null) throw new ArgumentNullException(nameof(request));
+ ArgumentNullException.ThrowIfNull(request);
return Task.FromResult(request);
}
@@ -166,7 +165,7 @@ protected virtual Task ProcessRequestAsync(HttpRequestMessag
/// The processed
protected virtual async Task ProcessResponseAsync(HttpResponseMessage response, CancellationToken cancellationToken = default)
{
- if (response == null) throw new ArgumentNullException(nameof(response));
+ ArgumentNullException.ThrowIfNull(response);
if (response.IsSuccessStatusCode) return response;
var content = string.Empty;
if (response.Content != null) content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
@@ -174,7 +173,7 @@ protected virtual async Task ProcessResponseAsync(HttpRespo
if (!response.IsSuccessStatusCode)
{
if (string.IsNullOrWhiteSpace(content)) response.EnsureSuccessStatusCode();
- else throw new CloudStreamsException(Serializer.Json.Deserialize(content));
+ else throw new ProblemDetailsException(this.Serializer.Deserialize(content)!);
}
return response;
}
diff --git a/src/core/CloudStreams.Core.Api.Client/Services/ResourceWatch.cs b/src/core/CloudStreams.Core.Api.Client/Services/ResourceWatch.cs
index ae8a15bb..d0092487 100644
--- a/src/core/CloudStreams.Core.Api.Client/Services/ResourceWatch.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Services/ResourceWatch.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -11,48 +11,39 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using CloudStreams.Core.Data;
-
namespace CloudStreams.Core.Api.Client.Services;
///
/// Represents the service used to handle a resource watch event subscription
///
/// The type of resources to watch
-public class ResourceWatch
+///
+/// Initializes a new
+///
+/// The service used to interact with the
+/// The namespace watched resources belong to, if any
+/// The used to watch resources of the specified type
+public class ResourceWatch(ResourceWatchEventHubClient resourceWatchEventHub, string? resourceNamespace, IObservable> stream)
: IObservable>, IAsyncDisposable
where TResource : class, IResource, new()
{
private bool _Disposed;
- ///
- /// Initializes a new
- ///
- /// The service used to interact with the
- /// The namespace watched resources belong to, if any
- /// The used to watch resources of the specified type
- public ResourceWatch(ResourceWatchEventHubClient resourceWatchEventHub, string? resourceNamespace, IObservable> stream)
- {
- this.ResourceWatchEventHub = resourceWatchEventHub ?? throw new ArgumentNullException(nameof(resourceWatchEventHub));
- this.ResourceNamespace = resourceNamespace;
- this.Stream = stream;
- }
-
///
/// Gets the service used to interact with the
///
- protected ResourceWatchEventHubClient ResourceWatchEventHub { get; }
+ protected ResourceWatchEventHubClient ResourceWatchEventHub { get; } = resourceWatchEventHub ?? throw new ArgumentNullException(nameof(resourceWatchEventHub));
///
/// Gets the namespace watched resources belong to, if any
///
- protected virtual string? ResourceNamespace { get; }
+ protected virtual string? ResourceNamespace { get; } = resourceNamespace;
///
/// Gets the used to watch resources of the specified type
///
- protected IObservable> Stream { get; }
+ protected IObservable> Stream { get; } = stream;
///
public IDisposable Subscribe(IObserver> observer) => this.Stream.Subscribe(observer);
diff --git a/src/core/CloudStreams.Core.Api.Client/Services/ResourceWatchEventHubClient.cs b/src/core/CloudStreams.Core.Api.Client/Services/ResourceWatchEventHubClient.cs
index f02c3d84..923f9b55 100644
--- a/src/core/CloudStreams.Core.Api.Client/Services/ResourceWatchEventHubClient.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Services/ResourceWatchEventHubClient.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -11,9 +11,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using CloudStreams.Core.Data;
-using Hylo;
using Microsoft.AspNetCore.SignalR.Client;
+using Neuroglia.Eventing.CloudEvents;
using System.Reactive.Linq;
using System.Reactive.Subjects;
@@ -25,8 +24,8 @@ namespace CloudStreams.Core.Api.Client.Services;
public class ResourceWatchEventHubClient
: IAsyncDisposable
{
-
- private bool _Disposed;
+
+ bool _Disposed;
///
/// Initializes a new
diff --git a/src/core/CloudStreams.Core.Api.Client/Usings.cs b/src/core/CloudStreams.Core.Api.Client/Usings.cs
index 807e0d8b..1b998df8 100644
--- a/src/core/CloudStreams.Core.Api.Client/Usings.cs
+++ b/src/core/CloudStreams.Core.Api.Client/Usings.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -11,5 +11,5 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-global using CloudStreams.Core;
-global using Hylo;
\ No newline at end of file
+global using CloudStreams.Core.Resources;
+global using Neuroglia.Data.Infrastructure.ResourceOriented;
\ No newline at end of file
diff --git a/src/core/CloudStreams.Core.Api.Server/CloudStreams.Core.Api.Server.csproj b/src/core/CloudStreams.Core.Api.Server/CloudStreams.Core.Api.Server.csproj
deleted file mode 100644
index d3abcb64..00000000
--- a/src/core/CloudStreams.Core.Api.Server/CloudStreams.Core.Api.Server.csproj
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
- net7.0
- enable
- enable
- 0.14.0
- Linux
- ..\..\..
- ..\..\..\docker-compose.dcproj
- cloudstreams-api-server
-
-
-
-
- \
- True
-
-
- \
- True
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/core/CloudStreams.Core.Api.Server/Dockerfile b/src/core/CloudStreams.Core.Api.Server/Dockerfile
deleted file mode 100644
index 5d7d54a6..00000000
--- a/src/core/CloudStreams.Core.Api.Server/Dockerfile
+++ /dev/null
@@ -1,30 +0,0 @@
-#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
-
-FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
-WORKDIR /app
-EXPOSE 80
-RUN apt-get update
-RUN apt-get install -y jq
-
-FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
-WORKDIR /src
-COPY ["src/core/CloudStreams.Core.Api.Server/CloudStreams.Core.Api.Server.csproj", "src/core/CloudStreams.Core.Api.Server/"]
-COPY ["src/dashboard/CloudStreams.Dashboard/CloudStreams.Dashboard.csproj", "src/dashboard/CloudStreams.Dashboard/"]
-COPY ["src/core/CloudStreams.Core.Api.Client/CloudStreams.Core.Api.Client.csproj", "src/core/CloudStreams.Core.Api.Client/"]
-COPY ["src/core/CloudStreams.Core/CloudStreams.Core.csproj", "src/core/CloudStreams.Core/"]
-COPY ["src/dashboard/CloudStreams.Dashboard.StateManagement/CloudStreams.Dashboard.StateManagement.csproj", "src/dashboard/CloudStreams.Dashboard.StateManagement/"]
-COPY ["src/core/CloudStreams.Core.Api/CloudStreams.Core.Api.csproj", "src/core/CloudStreams.Core.Api/"]
-COPY ["src/core/CloudStreams.Core.Application/CloudStreams.Core.Application.csproj", "src/core/CloudStreams.Core.Application/"]
-COPY ["src/core/CloudStreams.Core.Infrastructure/CloudStreams.Core.Infrastructure.csproj", "src/core/CloudStreams.Core.Infrastructure/"]
-RUN dotnet restore "src/core/CloudStreams.Core.Api.Server/CloudStreams.Core.Api.Server.csproj"
-COPY . .
-WORKDIR "/src/src/core/CloudStreams.Core.Api.Server"
-RUN dotnet build "CloudStreams.Core.Api.Server.csproj" -c Release -o /app/build
-
-FROM build AS publish
-RUN dotnet publish "CloudStreams.Core.Api.Server.csproj" -c Release -o /app/publish /p:UseAppHost=false
-
-FROM base AS final
-WORKDIR /app
-COPY --from=publish /app/publish .
-ENTRYPOINT ["dotnet", "CloudStreams.Core.Api.Server.dll"]
\ No newline at end of file
diff --git a/src/core/CloudStreams.Core.Api.Server/Properties/launchSettings.json b/src/core/CloudStreams.Core.Api.Server/Properties/launchSettings.json
deleted file mode 100644
index db4354ee..00000000
--- a/src/core/CloudStreams.Core.Api.Server/Properties/launchSettings.json
+++ /dev/null
@@ -1,39 +0,0 @@
-{
- "profiles": {
- "http": {
- "commandName": "Project",
- "launchBrowser": true,
- "launchUrl": "api/doc",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development",
- "CONNECTIONSTRINGS__EVENTSTORE": "esdb://localhost:2113?tls=false",
- "CONNECTIONSTRINGS__REDIS": "localhost"
- },
- "dotnetRunMessages": true,
- "applicationUrl": "http://localhost:5094"
- },
- "IIS Express": {
- "commandName": "IISExpress",
- "launchBrowser": true,
- "launchUrl": "api/doc",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- }
- },
- "Docker": {
- "commandName": "Docker",
- "launchBrowser": true,
- "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/api/doc",
- "publishAllPorts": true
- }
- },
- "$schema": "https://json.schemastore.org/launchsettings.json",
- "iisSettings": {
- "windowsAuthentication": false,
- "anonymousAuthentication": true,
- "iisExpress": {
- "applicationUrl": "http://localhost:36955",
- "sslPort": 0
- }
- }
-}
\ No newline at end of file
diff --git a/src/core/CloudStreams.Core.Api.Server/appsettings.Development.json b/src/core/CloudStreams.Core.Api.Server/appsettings.Development.json
deleted file mode 100644
index a6e86ace..00000000
--- a/src/core/CloudStreams.Core.Api.Server/appsettings.Development.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "Logging": {
- "LogLevel": {
- "Default": "Debug",
- "Microsoft.AspNetCore": "Warning"
- }
- }
-}
diff --git a/src/core/CloudStreams.Core.Api.Server/appsettings.json b/src/core/CloudStreams.Core.Api.Server/appsettings.json
deleted file mode 100644
index 10f68b8c..00000000
--- a/src/core/CloudStreams.Core.Api.Server/appsettings.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft.AspNetCore": "Warning"
- }
- },
- "AllowedHosts": "*"
-}
diff --git a/src/gateway/CloudStreams.Gateway.Api.Client/Services/Interfaces/ICloudStreamsGatewayApiClient.cs b/src/core/CloudStreams.Core.Api/ApiController.cs
similarity index 56%
rename from src/gateway/CloudStreams.Gateway.Api.Client/Services/Interfaces/ICloudStreamsGatewayApiClient.cs
rename to src/core/CloudStreams.Core.Api/ApiController.cs
index a755b8fb..342d1414 100644
--- a/src/gateway/CloudStreams.Gateway.Api.Client/Services/Interfaces/ICloudStreamsGatewayApiClient.cs
+++ b/src/core/CloudStreams.Core.Api/ApiController.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -11,19 +11,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using CloudStreams.Core.Data;
-
-namespace CloudStreams.Gateway.Api.Client.Services;
+namespace CloudStreams.Core.Api;
///
-/// Defines the fundamentals of a service used to interact with a Cloud Streams gateway's API
+/// Represents the base class for all s
///
-public interface ICloudStreamsGatewayApiClient
+///
+/// Initializes a new
+///
+/// The service used to mediate calls
+public abstract class ApiController(IMediator mediator)
+ : ControllerBase
{
///
- /// Gets the API used to manage s
+ /// Gets the service used to mediate calls
///
- ICloudEventsApi CloudEvents { get; }
+ protected IMediator Mediator { get; } = mediator;
}
\ No newline at end of file
diff --git a/src/core/CloudStreams.Core.Api/CloudStreams - Backup.Core.Api.csproj b/src/core/CloudStreams.Core.Api/CloudStreams - Backup.Core.Api.csproj
new file mode 100644
index 00000000..5b181042
--- /dev/null
+++ b/src/core/CloudStreams.Core.Api/CloudStreams - Backup.Core.Api.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net8.0
+ enable
+ enable
+ Library
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/core/CloudStreams.Core.Api/CloudStreams.Core.Api.csproj b/src/core/CloudStreams.Core.Api/CloudStreams.Core.Api.csproj
index 8fa1bffb..c9b313af 100644
--- a/src/core/CloudStreams.Core.Api/CloudStreams.Core.Api.csproj
+++ b/src/core/CloudStreams.Core.Api/CloudStreams.Core.Api.csproj
@@ -1,44 +1,43 @@
-
+
- net7.0
- Library
- enable
+ net8.0
enable
- True
+ enable
0.14.0
$(VersionPrefix)
$(VersionPrefix)
en
+ true
+ True
+ Apache-2.0
+ Copyright © 2023-Present The Cloud Streams Authors. All rights reserved.
https://github.com/neuroglia-io/cloud-streams
- README.md
https://github.com/neuroglia-io/cloud-streams
git
- Copyright © 2023 - Present The Cloud Streams Authors. All rights reserverd
- cloudstreams;core;api;
- Contains the Cloud Streams Core API controllers and services
- logo.png
- Apache-2.0
- True
- True
- True
+ true
+ cloud-streams/broker
+ Linux
+ ..\..\..
-
- \
- True
-
-
- \
- True
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
diff --git a/src/core/CloudStreams.Core.Api/ClusterResourceApiController.cs b/src/core/CloudStreams.Core.Api/ClusterResourceApiController.cs
new file mode 100644
index 00000000..74c878fb
--- /dev/null
+++ b/src/core/CloudStreams.Core.Api/ClusterResourceApiController.cs
@@ -0,0 +1,76 @@
+// Copyright © 2024-Present The Cloud Streams Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"),
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using CloudStreams.Core.Application.Commands.Resources.Generic;
+using CloudStreams.Core.Application.Queries.Resources.Generic;
+
+namespace CloudStreams.Core.Api;
+
+///
+/// Represents a used to manage cluster resources
+///
+/// The type of cluster to manage
+///
+public abstract class ClusterResourceApiController(IMediator mediator)
+ : ResourceApiController(mediator)
+ where TResource : class, IResource, new()
+{
+
+ ///
+ /// Gets the specified cluster resourced
+ ///
+ /// The name of the resource to get
+ /// A
+ /// A new
+ [HttpGet("{name}")]
+ [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)]
+ [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))]
+ public virtual async Task GetClusterResource(string name, CancellationToken cancellationToken = default)
+ {
+ return this.Process(await this.Mediator.ExecuteAsync(new GetResourceQuery(name, null), cancellationToken).ConfigureAwait(false));
+ }
+
+ ///
+ /// Patches the specified resource
+ ///
+ /// The patch to apply
+ /// The name of the resource to patch
+ /// A boolean indicating whether or not to persist changes
+ /// A
+ /// A new
+ [HttpPatch("{name}")]
+ [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)]
+ [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))]
+ public virtual async Task PatchResource(string name, [FromBody] Patch patch, bool dryRun = false, CancellationToken cancellationToken = default)
+ {
+ if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState);
+ return this.Process(await this.Mediator.ExecuteAsync(new PatchResourceCommand(name, null, patch, dryRun), cancellationToken).ConfigureAwait(false));
+ }
+
+ ///
+ /// Deletes the specified resource
+ ///
+ /// The name of the resource to delete
+ /// A boolean indicating whether or not to persist changes
+ /// A
+ /// A new
+ [HttpDelete("{name}")]
+ [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)]
+ [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))]
+ public virtual async Task DeleteResource(string name, bool dryRun = false, CancellationToken cancellationToken = default)
+ {
+ if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState);
+ return this.Process(await this.Mediator.ExecuteAsync(new DeleteResourceCommand(name, null, dryRun), cancellationToken).ConfigureAwait(false));
+ }
+
+}
\ No newline at end of file
diff --git a/src/core/CloudStreams.Core.Api/Controllers/BrokersController.cs b/src/core/CloudStreams.Core.Api/Controllers/BrokersController.cs
index edd13ca8..7ee14bb7 100644
--- a/src/core/CloudStreams.Core.Api/Controllers/BrokersController.cs
+++ b/src/core/CloudStreams.Core.Api/Controllers/BrokersController.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -11,22 +11,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using CloudStreams.Core.Data;
-using Hylo.Api.Http;
-using MediatR;
-using Microsoft.AspNetCore.Mvc;
-
namespace CloudStreams.Core.Api.Controllers;
///
/// Represents the used to manage s
///
+///
[Route("api/resources/v1/brokers")]
-public class BrokersController
- : ClusterResourceApiController
+public class BrokersController(IMediator mediator)
+ : ClusterResourceApiController(mediator)
{
-
- ///
- public BrokersController(IMediator mediator) : base(mediator) { }
-
}
diff --git a/src/core/CloudStreams.Core.Api/Controllers/CloudEventPartitionsController.cs b/src/core/CloudStreams.Core.Api/Controllers/CloudEventPartitionsController.cs
index 3195e663..29ed4973 100644
--- a/src/core/CloudStreams.Core.Api/Controllers/CloudEventPartitionsController.cs
+++ b/src/core/CloudStreams.Core.Api/Controllers/CloudEventPartitionsController.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -12,25 +12,18 @@
// limitations under the License.
using CloudStreams.Core.Application.Queries.Partitions;
-using CloudStreams.Core.Data;
-using Hylo.Api.Http;
-using MediatR;
-using Microsoft.AspNetCore.Mvc;
-using System.Net;
namespace CloudStreams.Core.Api.Controllers;
///
/// Represents the API controller used to manage the cloud event partitions
///
+///
[Route("api/core/v1/cloud-events/partitions")]
-public class CloudEventPartitionsController
- : ApiController
+public class CloudEventPartitionsController(IMediator mediator)
+ : ApiController(mediator)
{
- ///
- public CloudEventPartitionsController(IMediator mediator) : base(mediator) { }
-
///
/// Lists the ids of the cloud event partitions of the specified type
///
@@ -39,15 +32,15 @@ public CloudEventPartitionsController(IMediator mediator) : base(mediator) { }
/// A new
[HttpGet("{type}")]
[ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)]
- [ProducesResponseType(typeof(Hylo.ProblemDetails), (int)HttpStatusCode.BadRequest)]
+ [ProducesResponseType(typeof(Neuroglia.ProblemDetails), (int)HttpStatusCode.BadRequest)]
public virtual async Task ListPartitionsByType(CloudEventPartitionType type, CancellationToken cancellationToken)
{
if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState);
- return this.Process(await this.Mediator.Send(new ListEventPartitionIdsQuery(type), cancellationToken).ConfigureAwait(false));
+ return this.Process(await this.Mediator.ExecuteAsync(new ListEventPartitionIdsQuery(type), cancellationToken).ConfigureAwait(false));
}
///
- /// Gets the specificied cloud event partition's metadata
+ /// Gets the specified cloud event partition's metadata
///
/// The type of the partition to get the metadata of
/// The id of the partition to get the metadata of
@@ -55,11 +48,11 @@ public virtual async Task ListPartitionsByType(CloudEventPartitio
/// A new
[HttpGet("{type}/{id}")]
[ProducesResponseType(typeof(PartitionMetadata), (int)HttpStatusCode.OK)]
- [ProducesResponseType(typeof(Hylo.ProblemDetails), (int)HttpStatusCode.BadRequest)]
+ [ProducesResponseType(typeof(Neuroglia.ProblemDetails), (int)HttpStatusCode.BadRequest)]
public virtual async Task GetPartitionMetadata(CloudEventPartitionType type, string id, CancellationToken cancellationToken)
{
if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState);
- return this.Process(await this.Mediator.Send(new GetEventPartitionMetadataQuery(new(type, id)), cancellationToken).ConfigureAwait(false));
+ return this.Process(await this.Mediator.ExecuteAsync(new GetEventPartitionMetadataQuery(new(type, id)), cancellationToken).ConfigureAwait(false));
}
}
\ No newline at end of file
diff --git a/src/core/CloudStreams.Core.Api/Controllers/CloudEventStreamController.cs b/src/core/CloudStreams.Core.Api/Controllers/CloudEventStreamController.cs
index cce46497..98455c65 100644
--- a/src/core/CloudStreams.Core.Api/Controllers/CloudEventStreamController.cs
+++ b/src/core/CloudStreams.Core.Api/Controllers/CloudEventStreamController.cs
@@ -1,4 +1,4 @@
-// Copyright © 2023-Present The Cloud Streams Authors
+// Copyright © 2024-Present The Cloud Streams Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"),
// you may not use this file except in compliance with the License.
@@ -12,25 +12,18 @@
// limitations under the License.
using CloudStreams.Core.Application.Queries.Streams;
-using CloudStreams.Core.Data;
-using Hylo.Api.Http;
-using MediatR;
-using Microsoft.AspNetCore.Mvc;
-using System.Net;
namespace CloudStreams.Core.Api.Controllers;
///
/// Represents the API controller used to manage the cloud event streams
///
+///
[Route("api/core/v1/cloud-events/stream")]
-public class CloudEventStreamController
- : ApiController
+public class CloudEventStreamController(IMediator mediator)
+ : ApiController(mediator)
{
- ///
- public CloudEventStreamController(IMediator mediator) : base(mediator) { }
-
///
/// Gets the cloud event stream's metadata
///
@@ -38,11 +31,11 @@ public CloudEventStreamController(IMediator mediator) : base(mediator) { }
/// A new
[HttpGet]
[ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)]
- [ProducesResponseType(typeof(Hylo.ProblemDetails), (int)HttpStatusCode.BadRequest)]
+ [ProducesResponseType(typeof(Neuroglia.ProblemDetails), (int)HttpStatusCode.BadRequest)]
public virtual async Task GetStreamMetadata(CancellationToken cancellationToken)
{
if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState);
- return this.Process(await this.Mediator.Send(new GetEventStreamMetadataQuery(), cancellationToken).ConfigureAwait(false));
+ return this.Process(await this.Mediator.ExecuteAsync(new GetEventStreamMetadataQuery(), cancellationToken).ConfigureAwait(false));
}
///
@@ -53,11 +46,11 @@ public virtual async Task GetStreamMetadata(CancellationToken can
/// A new
[HttpGet("read")]
[ProducesResponseType(typeof(IEnumerable