Skip to content

Commit dbd385b

Browse files
Add Post-Dapr-PubSub Reaction
1 parent 7cebd89 commit dbd385b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+3740
-0
lines changed

.github/workflows/draft-release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ env:
6868
{"label": "Gremlin", "path": "reactions/gremlin/gremlin-reaction", "name": "reaction-gremlin", "platforms": "linux/amd64,linux/arm64"},
6969
{"label": "Result", "path": "reactions/platform/result-reaction", "name": "reaction-result", "platforms": "linux/amd64,linux/arm64"},
7070
{"label": "StorageQueue", "path": "reactions/azure/storagequeue-reaction", "name": "reaction-storage-queue", "platforms": "linux/amd64,linux/arm64"},
71+
{"label": "PostDaprPubSub", "path": "reactions/dapr/post-pubsub", "name": "reaction-post-dapr-pubsub", "platforms": "linux/amd64,linux/arm64"},
7172
{"label": "StoredProc", "path": "reactions/sql/storedproc-reaction", "name": "reaction-storedproc", "platforms": "linux/amd64,linux/arm64"}]'
7273

7374
jobs:

cli/installers/resources/default-reaction-providers.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,11 @@ spec:
240240
- endpoint
241241
- clientId
242242
- secret
243+
---
244+
apiVersion: v1
245+
kind: ReactionProvider
246+
name: PostDaprPubSub
247+
spec:
248+
services:
249+
reaction:
250+
image: reaction-post-dapr-pubsub

reactions/dapr/post-pubsub/Dockerfile

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Build stage
2+
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
3+
ARG BUILD_CONFIGURATION=Release
4+
WORKDIR /src
5+
6+
# Copy solution and project files
7+
COPY post-dapr-pubsub.sln ./
8+
COPY Drasi.Reactions.PostDaprPubSub/Drasi.Reactions.PostDaprPubSub.csproj ./Drasi.Reactions.PostDaprPubSub/
9+
10+
# Restore dependencies
11+
RUN dotnet restore "./Drasi.Reactions.PostDaprPubSub/Drasi.Reactions.PostDaprPubSub.csproj"
12+
13+
# Copy only the source code
14+
COPY Drasi.Reactions.PostDaprPubSub/ ./Drasi.Reactions.PostDaprPubSub/
15+
16+
# Build the reaction project
17+
WORKDIR /src/Drasi.Reactions.PostDaprPubSub
18+
RUN dotnet publish "./Drasi.Reactions.PostDaprPubSub.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
19+
20+
# Final stage/image
21+
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final
22+
WORKDIR /app
23+
COPY --from=build /app/publish .
24+
25+
# Set log levels for reaction in production
26+
ENV Logging__LogLevel__Default="Debug"
27+
ENV Logging__LogLevel__Microsoft="Warning"
28+
ENV Logging__LogLevel__Microsoft_Hosting_Lifetime="Information"
29+
ENV Logging__LogLevel__Drasi_Reactions_PostDaprPubSub="Debug"
30+
31+
USER app
32+
ENTRYPOINT ["dotnet", "Drasi.Reactions.PostDaprPubSub.dll"]
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Build stage
2+
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
3+
ARG BUILD_CONFIGURATION=Release
4+
WORKDIR /src
5+
6+
# Copy solution and project files
7+
COPY post-dapr-pubsub.sln ./
8+
COPY Drasi.Reactions.PostDaprPubSub/Drasi.Reactions.PostDaprPubSub.csproj ./Drasi.Reactions.PostDaprPubSub/
9+
10+
# Restore dependencies
11+
RUN dotnet restore "./Drasi.Reactions.PostDaprPubSub/Drasi.Reactions.PostDaprPubSub.csproj"
12+
13+
# Copy only the source code
14+
COPY Drasi.Reactions.PostDaprPubSub/ ./Drasi.Reactions.PostDaprPubSub/
15+
16+
# Build the reaction project
17+
WORKDIR /src/Drasi.Reactions.PostDaprPubSub
18+
RUN dotnet publish "./Drasi.Reactions.PostDaprPubSub.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
19+
20+
# Final stage/image
21+
FROM ubuntu:25.04 AS final
22+
RUN apt-get update && apt-get install -y bash curl dotnet-runtime-8.0 aspnetcore-runtime-8.0 && rm -rf /var/lib/apt/lists/*
23+
WORKDIR /app
24+
COPY --from=build /app/publish .
25+
26+
# Set log levels for reaction in debug environment
27+
ENV Logging__LogLevel__Default="Debug"
28+
ENV Logging__LogLevel__Microsoft="Information"
29+
ENV Logging__LogLevel__Microsoft_Hosting_Lifetime="Information"
30+
ENV Logging__LogLevel__Drasi_Reactions_PostDaprPubSub="Debug"
31+
32+
USER app
33+
ENTRYPOINT ["dotnet", "Drasi.Reactions.PostDaprPubSub.dll"]
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
// Copyright 2024 The Drasi Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using Xunit;
16+
using Moq;
17+
using Microsoft.Extensions.Logging;
18+
using Drasi.Reaction.SDK.Models.QueryOutput;
19+
using Drasi.Reactions.PostDaprPubSub.Services;
20+
using System.Text.Json;
21+
22+
namespace Drasi.Reactions.PostDaprPubSub.Tests;
23+
24+
public class ChangeFormatterTests
25+
{
26+
[Fact]
27+
public void DrasiChangeFormatter_FormatEmptyChangeEvent_ShouldReturnEmptyCollection()
28+
{
29+
// Arrange
30+
var formatter = new DrasiChangeFormatter();
31+
var evt = new ChangeEvent
32+
{
33+
QueryId = "test-query",
34+
Sequence = 1,
35+
AddedResults = Array.Empty<Dictionary<string, object>>(),
36+
UpdatedResults = Array.Empty<UpdatedResultElement>(),
37+
DeletedResults = Array.Empty<Dictionary<string, object>>()
38+
};
39+
40+
// Act
41+
var result = formatter.Format(evt);
42+
43+
// Assert
44+
Assert.Empty(result);
45+
}
46+
47+
[Fact]
48+
public void DrasiChangeFormatter_FormatAddedResults_ShouldReturnCorrectFormat()
49+
{
50+
// Arrange
51+
var formatter = new DrasiChangeFormatter();
52+
var addedItem = new Dictionary<string, object> { { "id", "123" }, { "name", "test" } };
53+
var evt = new ChangeEvent
54+
{
55+
QueryId = "test-query",
56+
Sequence = 1,
57+
SourceTimeMs = 1000,
58+
AddedResults = new[] { addedItem },
59+
UpdatedResults = Array.Empty<UpdatedResultElement>(),
60+
DeletedResults = Array.Empty<Dictionary<string, object>>()
61+
};
62+
63+
// Act
64+
var result = formatter.Format(evt).ToList();
65+
66+
// Assert
67+
Assert.Single(result);
68+
var json = result[0].GetRawText();
69+
Assert.Contains("\"op\":\"i\"", json);
70+
Assert.Contains("\"queryId\":\"test-query\"", json);
71+
Assert.Contains("\"id\":\"123\"", json);
72+
Assert.Contains("\"name\":\"test\"", json);
73+
}
74+
75+
[Fact]
76+
public void DrasiChangeFormatter_FormatUpdatedResults_ShouldReturnCorrectFormat()
77+
{
78+
// Arrange
79+
var formatter = new DrasiChangeFormatter();
80+
var beforeItem = new Dictionary<string, object> { { "id", "123" }, { "name", "before" } };
81+
var afterItem = new Dictionary<string, object> { { "id", "123" }, { "name", "after" } };
82+
var updatedElement = new UpdatedResultElement { Before = beforeItem, After = afterItem };
83+
84+
var evt = new ChangeEvent
85+
{
86+
QueryId = "test-query",
87+
Sequence = 1,
88+
SourceTimeMs = 1000,
89+
AddedResults = Array.Empty<Dictionary<string, object>>(),
90+
UpdatedResults = new[] { updatedElement },
91+
DeletedResults = Array.Empty<Dictionary<string, object>>()
92+
};
93+
94+
// Act
95+
var result = formatter.Format(evt).ToList();
96+
97+
// Assert
98+
Assert.Single(result);
99+
var json = result[0].GetRawText();
100+
Assert.Contains("\"op\":\"u\"", json);
101+
Assert.Contains("\"queryId\":\"test-query\"", json);
102+
Assert.Contains("\"before\":{", json);
103+
Assert.Contains("\"after\":{", json);
104+
Assert.Contains("\"name\":\"before\"", json);
105+
Assert.Contains("\"name\":\"after\"", json);
106+
}
107+
108+
[Fact]
109+
public void DrasiChangeFormatter_FormatDeletedResults_ShouldReturnCorrectFormat()
110+
{
111+
// Arrange
112+
var formatter = new DrasiChangeFormatter();
113+
var deletedItem = new Dictionary<string, object> { { "id", "123" }, { "name", "test" } };
114+
var evt = new ChangeEvent
115+
{
116+
QueryId = "test-query",
117+
Sequence = 1,
118+
SourceTimeMs = 1000,
119+
AddedResults = Array.Empty<Dictionary<string, object>>(),
120+
UpdatedResults = Array.Empty<UpdatedResultElement>(),
121+
DeletedResults = new[] { deletedItem }
122+
};
123+
124+
// Act
125+
var result = formatter.Format(evt).ToList();
126+
127+
// Assert
128+
Assert.Single(result);
129+
var json = result[0].GetRawText();
130+
Assert.Contains("\"op\":\"d\"", json);
131+
Assert.Contains("\"queryId\":\"test-query\"", json);
132+
Assert.Contains("\"before\":{", json);
133+
Assert.Contains("\"id\":\"123\"", json);
134+
Assert.DoesNotContain("\"after\":{", json);
135+
}
136+
137+
[Fact]
138+
public void DrasiChangeFormatter_FormatMultipleResults_ShouldReturnCorrectCount()
139+
{
140+
// Arrange
141+
var formatter = new DrasiChangeFormatter();
142+
var addedItem = new Dictionary<string, object> { { "id", "123" }, { "name", "test" } };
143+
var deletedItem = new Dictionary<string, object> { { "id", "456" }, { "name", "deleted" } };
144+
var beforeItem = new Dictionary<string, object> { { "id", "789" }, { "name", "before" } };
145+
var afterItem = new Dictionary<string, object> { { "id", "789" }, { "name", "after" } };
146+
var updatedElement = new UpdatedResultElement { Before = beforeItem, After = afterItem };
147+
148+
var evt = new ChangeEvent
149+
{
150+
QueryId = "test-query",
151+
Sequence = 1,
152+
SourceTimeMs = 1000,
153+
AddedResults = new[] { addedItem },
154+
UpdatedResults = new[] { updatedElement },
155+
DeletedResults = new[] { deletedItem }
156+
};
157+
158+
// Act
159+
var result = formatter.Format(evt).ToList();
160+
161+
// Assert
162+
Assert.Equal(3, result.Count);
163+
Assert.Contains(result, r => r.GetRawText().Contains("\"op\":\"i\""));
164+
Assert.Contains(result, r => r.GetRawText().Contains("\"op\":\"u\""));
165+
Assert.Contains(result, r => r.GetRawText().Contains("\"op\":\"d\""));
166+
}
167+
168+
[Fact]
169+
public void ChangeFormatterFactory_GetFormatter_ShouldReturnDrasiFormatter()
170+
{
171+
// Arrange
172+
var serviceProvider = new Mock<IServiceProvider>();
173+
var drasiFormatter = new DrasiChangeFormatter();
174+
175+
serviceProvider.Setup(s => s.GetService(typeof(DrasiChangeFormatter))).Returns(drasiFormatter);
176+
177+
var factory = new ChangeFormatterFactory(serviceProvider.Object);
178+
179+
// Act
180+
var drasiResult = factory.GetFormatter();
181+
182+
// Assert
183+
Assert.Same(drasiFormatter, drasiResult);
184+
}
185+
}

0 commit comments

Comments
 (0)