diff --git a/Client.IntegrationTests/Client.IntegrationTests.csproj b/Client.IntegrationTests/Client.IntegrationTests.csproj index a7f54abf..e16f6401 100644 --- a/Client.IntegrationTests/Client.IntegrationTests.csproj +++ b/Client.IntegrationTests/Client.IntegrationTests.csproj @@ -29,6 +29,9 @@ PreserveNewest + + PreserveNewest + diff --git a/Client.IntegrationTests/EvaluateDecisionTest.cs b/Client.IntegrationTests/EvaluateDecisionTest.cs new file mode 100644 index 00000000..578af3b5 --- /dev/null +++ b/Client.IntegrationTests/EvaluateDecisionTest.cs @@ -0,0 +1,69 @@ +// +// Copyright (c) 2021 camunda services GmbH (info@camunda.com) +// +// 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 System; +using System.IO; +using System.Threading.Tasks; +using NUnit.Framework; +using Zeebe.Client; + +namespace Client.IntegrationTests; + +[TestFixture] +public class EvaluateDecisionTest +{ + private static readonly string DecisionPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "dinnerDecisions.dmn"); + private static readonly string DevisionEvaluationVariables = "{\"season\":\"Fall\", \"guestCount\":12}"; + + private readonly ZeebeIntegrationTestHelper testHelper = ZeebeIntegrationTestHelper.Latest(); + private IZeebeClient zeebeClient; + + [OneTimeSetUp] + public async Task Setup() + { + zeebeClient = await testHelper.SetupIntegrationTest(); + } + + [OneTimeTearDown] + public async Task Stop() + { + await testHelper.TearDownIntegrationTest(); + } + + [Test] + public async Task ShouldEvaluateDecision() + { + // given + var deployResponse = await zeebeClient.NewDeployCommand() + .AddResourceFile(DecisionPath) + .Send(); + var decisionKey = deployResponse.Decisions[0].DecisionKey; + + // when + var evaluateDecisionResponse = await zeebeClient + .NewEvaluateDecisionCommand() + .DecisionKey(decisionKey) + .Variables(DevisionEvaluationVariables) + .Send(); + + // then + Assert.AreEqual(evaluateDecisionResponse.DecisionVersion, 1); + Assert.AreEqual(decisionKey, evaluateDecisionResponse.DecisionKey); + Assert.AreEqual("dish", evaluateDecisionResponse.DecisionId); + Assert.Greater(evaluateDecisionResponse.DecisionKey, 1); + // right now it seems the DMN engine returns an double quated string + Assert.AreEqual("\"Stew\"", evaluateDecisionResponse.DecisionOutput); + } +} \ No newline at end of file diff --git a/Client.IntegrationTests/Resources/dinnerDecisions.dmn b/Client.IntegrationTests/Resources/dinnerDecisions.dmn new file mode 100644 index 00000000..e360d180 --- /dev/null +++ b/Client.IntegrationTests/Resources/dinnerDecisions.dmn @@ -0,0 +1,95 @@ + + + + + + + season + + + + + guestCount + + + + + + + "Fall" + + + <= 8 + + + "Spareribs" + + + + + "Winter" + + + <= 8 + + + "Roastbeef" + + + + + "Spring" + + + <= 4 + + + "Dry Aged Gourmet Steak" + + + + Save money + + "Spring" + + + [5..8] + + + "Steak" + + + + Less effort + + "Fall","Winter","Spring" + + + > 8 + + + "Stew" + + + + Hey, why not? + + "Summer" + + + + + + "Light Salad and nice Steak" + + + + + + + + + + + + diff --git a/Client.UnitTests/EvaluateDecisionTest.cs b/Client.UnitTests/EvaluateDecisionTest.cs new file mode 100644 index 00000000..2e46f17d --- /dev/null +++ b/Client.UnitTests/EvaluateDecisionTest.cs @@ -0,0 +1,260 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using GatewayProtocol; +using Grpc.Core; +using NUnit.Framework; + +namespace Zeebe.Client +{ + [TestFixture] + public class EvaluateDecisionTest : BaseZeebeTest + { + [Test] + public async Task ShouldSendRequestAsExpected() + { + // given + var expectedRequest = new EvaluateDecisionRequest + { + DecisionId = "decision", + }; + + // when + await ZeebeClient.NewEvaluateDecisionCommand() + .DecisionId("decision") + .Send(); + + // then + var request = TestService.Requests[typeof(EvaluateDecisionRequest)][0]; + Assert.AreEqual(expectedRequest, request); + } + + [Test] + public void ShouldTimeoutRequest() + { + // given + + // when + var task = ZeebeClient.NewEvaluateDecisionCommand() + .DecisionId("decision") + .Send(TimeSpan.Zero); + var aggregateException = Assert.Throws(() => task.Wait()); + var rpcException = (RpcException)aggregateException.InnerExceptions[0]; + + // then + Assert.AreEqual(Grpc.Core.StatusCode.DeadlineExceeded, rpcException.Status.StatusCode); + } + + [Test] + public void ShouldCancelRequest() + { + // given + + // when + var task = ZeebeClient.NewEvaluateDecisionCommand() + .DecisionId("decision") + .Send(new CancellationTokenSource(TimeSpan.Zero).Token); + var aggregateException = Assert.Throws(() => task.Wait()); + var rpcException = (RpcException)aggregateException.InnerExceptions[0]; + + // then + Assert.AreEqual(Grpc.Core.StatusCode.Cancelled, rpcException.Status.StatusCode); + } + + [Test] + public async Task ShouldSendRequestWithDecisionKeyExpected() + { + // given + var expectedRequest = new EvaluateDecisionRequest + { + DecisionKey = 12 + }; + + // when + await ZeebeClient.NewEvaluateDecisionCommand() + .DecisionKey(12) + .Send(); + + // then + var request = TestService.Requests[typeof(EvaluateDecisionRequest)][0]; + Assert.AreEqual(expectedRequest, request); + } + + [Test] + public async Task ShouldSendRequestWithVariablesAsExpected() + { + // given + var expectedRequest = new EvaluateDecisionRequest + { + DecisionKey = 12, + Variables = "{\"foo\":1}" + }; + + // when + await ZeebeClient.NewEvaluateDecisionCommand() + .DecisionKey(12) + .Variables("{\"foo\":1}") + .Send(); + + // then + var request = TestService.Requests[typeof(EvaluateDecisionRequest)][0]; + Assert.AreEqual(expectedRequest, request); + } + + [Test] + public async Task ShouldSendRequestWithVariablesAndDecisionIdAsExpected() + { + // given + var expectedRequest = new EvaluateDecisionRequest + { + DecisionId = "decision", + Variables = "{\"foo\":1}" + }; + + // when + await ZeebeClient.NewEvaluateDecisionCommand() + .DecisionId("decision") + .Variables("{\"foo\":1}") + .Send(); + + // then + var request = TestService.Requests[typeof(EvaluateDecisionRequest)][0]; + Assert.AreEqual(expectedRequest, request); + } + + [Test] + public async Task ShouldReceiveResponseAsExpected() + { + // given + var expectedResponse = new EvaluateDecisionResponse + { + DecisionId = "decision", + DecisionKey = 123, + DecisionName = "decision-123", + DecisionOutput = "1", + DecisionVersion = 2, + FailureMessage = "", + FailedDecisionId = "", + DecisionRequirementsId = "12", + DecisionRequirementsKey = 1234, + EvaluatedDecisions = + { + new EvaluatedDecision + { + DecisionId = "decision", + DecisionKey = 123, + DecisionName = "decision-123", + DecisionOutput = "1", + DecisionVersion = 2, + DecisionType = "noop", + EvaluatedInputs = + { + new EvaluatedDecisionInput + { + InputId = "moep", + InputName = "moepmoep", + InputValue = "boom" + }, + new EvaluatedDecisionInput + { + InputId = "moeb", + InputName = "moebmoeb", + InputValue = "boom" + } + }, + MatchedRules = + { + new MatchedDecisionRule + { + EvaluatedOutputs = + { + new EvaluatedDecisionOutput + { + OutputId = "outputId", + OutputName = "output", + OutputValue = "val" + }, + new EvaluatedDecisionOutput + { + OutputId = "outputId2", + OutputName = "output2", + OutputValue = "val2" + } + }, + RuleId = "ruleid", + RuleIndex = 1 + } + } + } + }, + }; + + TestService.AddRequestHandler(typeof(EvaluateDecisionRequest), request => expectedResponse); + + // when + var evaluatedDecisionResponse = await ZeebeClient.NewEvaluateDecisionCommand() + .DecisionId("decision") + .Send(); + + // then + Assert.AreEqual("decision", evaluatedDecisionResponse.DecisionId); + Assert.AreEqual(123, evaluatedDecisionResponse.DecisionKey); + Assert.AreEqual("decision-123", evaluatedDecisionResponse.DecisionName); + Assert.AreEqual("1", evaluatedDecisionResponse.DecisionOutput); + Assert.AreEqual(2, evaluatedDecisionResponse.DecisionVersion); + Assert.AreEqual("", evaluatedDecisionResponse.FailureMessage); + Assert.AreEqual("", evaluatedDecisionResponse.FailedDecisionId); + Assert.AreEqual("12", evaluatedDecisionResponse.DecisionRequirementsId); + Assert.AreEqual(1234, evaluatedDecisionResponse.DecisionRequirementsKey); + + var evaluatedDecisions = evaluatedDecisionResponse.EvaluatedDecisions; + Assert.AreEqual(1, evaluatedDecisions.Count); + + var decision = evaluatedDecisions[0]; + Assert.AreEqual("decision", decision.DecisionId); + Assert.AreEqual(123, decision.DecisionKey); + Assert.AreEqual("decision-123", decision.DecisionName); + Assert.AreEqual("1", decision.DecisionOutput); + Assert.AreEqual(2, decision.DecisionVersion); + Assert.AreEqual("noop", decision.DecisionType); + + var decisionEvaluatedInputs = decision.EvaluatedInputs; + Assert.AreEqual(2, decisionEvaluatedInputs.Count); + + var decisionEvaluatedInput = decisionEvaluatedInputs[0]; + Assert.AreEqual("moep", decisionEvaluatedInput.InputId); + Assert.AreEqual("moepmoep", decisionEvaluatedInput.InputName); + Assert.AreEqual("boom", decisionEvaluatedInput.InputValue); + + decisionEvaluatedInput = decisionEvaluatedInputs[1]; + Assert.AreEqual("moeb", decisionEvaluatedInput.InputId); + Assert.AreEqual("moebmoeb", decisionEvaluatedInput.InputName); + Assert.AreEqual("boom", decisionEvaluatedInput.InputValue); + + + var decisionMatchedRules = decision.MatchedRules; + Assert.AreEqual(1, decisionMatchedRules.Count); + var decisionMatchedRule = decisionMatchedRules[0]; + + Assert.AreEqual("ruleid", decisionMatchedRule.RuleId); + Assert.AreEqual(1, decisionMatchedRule.RuleIndex); + + var evaluatedDecisionOutputs = decisionMatchedRule.EvaluatedOutputs; + Assert.AreEqual(2, evaluatedDecisionOutputs.Count); + + var evaluatedDecisionOutput = evaluatedDecisionOutputs[0]; + Assert.AreEqual("outputId", evaluatedDecisionOutput.OutputId); + Assert.AreEqual("output", evaluatedDecisionOutput.OutputName); + Assert.AreEqual("val", evaluatedDecisionOutput.OutputValue); + + evaluatedDecisionOutput = evaluatedDecisionOutputs[1]; + Assert.AreEqual("outputId2", evaluatedDecisionOutput.OutputId); + Assert.AreEqual("output2", evaluatedDecisionOutput.OutputName); + Assert.AreEqual("val2", evaluatedDecisionOutput.OutputValue); + } + } +} + + + diff --git a/Client.UnitTests/GatewayTestService.cs b/Client.UnitTests/GatewayTestService.cs index 46d36b8a..936cf6a6 100644 --- a/Client.UnitTests/GatewayTestService.cs +++ b/Client.UnitTests/GatewayTestService.cs @@ -61,6 +61,7 @@ public GatewayTestService() typedRequestHandler.Add(typeof(SetVariablesRequest), request => new SetVariablesResponse()); typedRequestHandler.Add(typeof(ResolveIncidentRequest), request => new ResolveIncidentResponse()); typedRequestHandler.Add(typeof(CreateProcessInstanceWithResultRequest), request => new CreateProcessInstanceWithResultResponse()); + typedRequestHandler.Add(typeof(EvaluateDecisionRequest), request => new EvaluateDecisionResponse()); foreach (var pair in typedRequestHandler) { @@ -144,6 +145,11 @@ public override Task DeployResource(DeployResourceReques return Task.FromResult((DeployResourceResponse)HandleRequest(request, context)); } + public override Task EvaluateDecision(EvaluateDecisionRequest request, ServerCallContext context) + { + return Task.FromResult((EvaluateDecisionResponse)HandleRequest(request, context)); + } + public delegate void ConsumeMetadata(Metadata metadata); public void ConsumeRequestHeaders(ConsumeMetadata consumer) diff --git a/Client.UnitTests/TestDataProvider.cs b/Client.UnitTests/TestDataProvider.cs index fa59b14a..808587cd 100644 --- a/Client.UnitTests/TestDataProvider.cs +++ b/Client.UnitTests/TestDataProvider.cs @@ -6,14 +6,6 @@ using NUnit.Framework; using Zeebe.Client.Api.Commands; using Zeebe.Client.Api.Responses; -using Zeebe.Client.Impl.Responses; -using CancelProcessInstanceResponse = GatewayProtocol.CancelProcessInstanceResponse; -using CompleteJobResponse = GatewayProtocol.CompleteJobResponse; -using FailJobResponse = GatewayProtocol.FailJobResponse; -using PublishMessageResponse = GatewayProtocol.PublishMessageResponse; -using ResolveIncidentResponse = GatewayProtocol.ResolveIncidentResponse; -using SetVariablesResponse = GatewayProtocol.SetVariablesResponse; -using ThrowErrorResponse = GatewayProtocol.ThrowErrorResponse; namespace Zeebe.Client { @@ -140,9 +132,17 @@ public static IEnumerable Provider() } } }, - new GatewayProtocol.DeployResourceResponse(), + new DeployResourceResponse(), (RequestCreator) (zeebeClient => zeebeClient.NewDeployCommand().AddResourceFile(DemoProcessPath))); + yield return new TestCaseData( + new EvaluateDecisionRequest + { + DecisionId = "decision" + }, + new EvaluateDecisionResponse(), + (RequestCreator) + (zeebeClient => zeebeClient.NewEvaluateDecisionCommand().DecisionId("decision"))); } } } \ No newline at end of file diff --git a/Client/Api/Commands/IEvaluateDecisionCommandStep1.cs b/Client/Api/Commands/IEvaluateDecisionCommandStep1.cs new file mode 100644 index 00000000..a7e24433 --- /dev/null +++ b/Client/Api/Commands/IEvaluateDecisionCommandStep1.cs @@ -0,0 +1,54 @@ +// +// Copyright (c) 2021 camunda services GmbH (info@camunda.com) +// +// 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 GatewayProtocol; +using Zeebe.Client.Api.Responses; + +namespace Zeebe.Client.Api.Commands; + +public interface IEvaluateDecisionCommandStep1 +{ + /// + /// Set the id of the decision to evaluate. This is the static id of the decision in the DMN XML + /// (i.e. "<decision id='my-decision'>"). + /// + /// + /// The DMN id of the decision. + /// the builder for this command. Call to complete the command and send + /// it to the broker. + IEvaluateDecisionCommandStep2 DecisionId(string decisionId); + + /// + /// Set the key of the decision to evaluate. The key is assigned by the broker while deploying the + /// decision. It can be picked from the deployment. + /// + /// + /// The key of the decision. + /// the builder for this command. Call to complete the command and send + /// it to the broker. + IEvaluateDecisionCommandStep2 DecisionKey(long decisionKey); + + public interface IEvaluateDecisionCommandStep2 : IFinalCommandWithRetryStep + { + /// + /// Set the variables for the decision evaluation. + /// + /// + /// The variables JSON document as String. + /// the builder for this command. Call to complete the command and send + /// it to the broker. + IEvaluateDecisionCommandStep2 Variables(string variables); + } +} \ No newline at end of file diff --git a/Client/Api/Responses/IEvaluateDecisionResponse.cs b/Client/Api/Responses/IEvaluateDecisionResponse.cs new file mode 100644 index 00000000..7f58c499 --- /dev/null +++ b/Client/Api/Responses/IEvaluateDecisionResponse.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; + +namespace Zeebe.Client.Api.Responses +{ + /// + /// Response for evaluating a decision on the broker. + /// + public interface IEvaluateDecisionResponse + { + /// + /// the decision ID, as parsed during deployment; together with the versions forms a unique + /// identifier for a specific decision + /// + string DecisionId { get; } + + /// + /// the assigned decision version + /// + int DecisionVersion { get; } + + /// + /// the assigned decision key, which acts as a unique identifier for this decision + /// + long DecisionKey { get; } + + /// + /// the name of the decision, as parsed during deployment + /// + string DecisionName { get; } + + /// + /// the ID of the decision requirements graph that this decision is part of, as parsed + /// during deployment + /// + string DecisionRequirementsId { get; } + + /// + /// the assigned key of the decision requirements graph that this decision is part of + /// + long DecisionRequirementsKey { get; } + + /// + /// the output of the evaluated decision + /// + string DecisionOutput { get; } + + /// + /// a list of decisions that were evaluated within the requested decision evaluation + /// + IList EvaluatedDecisions { get; } + + /// + /// a string indicating the ID of the decision which failed during evaluation + /// + string FailedDecisionId { get; } + + /// + /// a message describing why the decision which was evaluated failed + /// + string FailureMessage { get; } + } +} \ No newline at end of file diff --git a/Client/Api/Responses/IEvaluatedDecision.cs b/Client/Api/Responses/IEvaluatedDecision.cs new file mode 100644 index 00000000..98b7fea9 --- /dev/null +++ b/Client/Api/Responses/IEvaluatedDecision.cs @@ -0,0 +1,63 @@ +// +// Copyright (c) 2021 camunda services GmbH (info@camunda.com) +// +// 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 System.Collections.Generic; +using GatewayProtocol; + +namespace Zeebe.Client.Api.Responses; + +public interface IEvaluatedDecision +{ + /// + /// the decision ID, as parsed during deployment; together with the versions forms a unique + /// identifier for a specific decision + /// + string DecisionId { get; } + + /// + /// the assigned decision version + /// + int DecisionVersion { get; } + + /// + /// the assigned decision key, which acts as a unique identifier for this decision + /// + long DecisionKey { get; } + + /// + /// the name of the decision, as parsed during deployment + /// + string DecisionName { get; } + + /// + /// the type of the evaluated decision + /// + string DecisionType { get; } + + /// + /// the output of the evaluated decision + /// + string DecisionOutput { get; } + + /// + /// the decision inputs that were evaluated within this decision evaluation + /// + IList EvaluatedInputs { get; } + + /// + /// the decision rules that matched within this decision evaluation + /// + IList MatchedRules { get; } +} \ No newline at end of file diff --git a/Client/Api/Responses/IEvaluatedDecisionInput.cs b/Client/Api/Responses/IEvaluatedDecisionInput.cs new file mode 100644 index 00000000..4a85509b --- /dev/null +++ b/Client/Api/Responses/IEvaluatedDecisionInput.cs @@ -0,0 +1,34 @@ +// +// Copyright (c) 2021 camunda services GmbH (info@camunda.com) +// +// 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. + +namespace Zeebe.Client.Api.Responses; + +public interface IEvaluatedDecisionInput +{ + /// + /// the id of the evaluated decision input + /// + string InputId { get; } + + /// + /// the name of the evaluated decision input + /// + string InputName { get; } + + /// + /// the value of the evaluated decision input + /// + string InputValue { get; } +} \ No newline at end of file diff --git a/Client/Api/Responses/IEvaluatedDecisionOutput.cs b/Client/Api/Responses/IEvaluatedDecisionOutput.cs new file mode 100644 index 00000000..874d00e8 --- /dev/null +++ b/Client/Api/Responses/IEvaluatedDecisionOutput.cs @@ -0,0 +1,34 @@ +// +// Copyright (c) 2021 camunda services GmbH (info@camunda.com) +// +// 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. + +namespace Zeebe.Client.Api.Responses; + +public interface IEvaluatedDecisionOutput +{ + /// + /// the id of the evaluated decision output + /// + string OutputId { get; } + + /// + /// the name of the evaluated decision output + /// + string OutputName { get; } + + /// + /// the value of the evaluated decision output + /// + string OutputValue { get; } +} \ No newline at end of file diff --git a/Client/Api/Responses/IMatchedDecisionRule.cs b/Client/Api/Responses/IMatchedDecisionRule.cs new file mode 100644 index 00000000..d8cc7617 --- /dev/null +++ b/Client/Api/Responses/IMatchedDecisionRule.cs @@ -0,0 +1,37 @@ +// +// Copyright (c) 2021 camunda services GmbH (info@camunda.com) +// +// 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 System.Collections.Generic; +using GatewayProtocol; + +namespace Zeebe.Client.Api.Responses; + +public interface IMatchedDecisionRule +{ + /// + /// the id of the matched rule + /// + string RuleId { get; } + + /// + /// the index of the matched rule + /// + int RuleIndex { get; } + + /// + /// the evaluated decision outputs + /// + IList EvaluatedOutputs { get; } +} \ No newline at end of file diff --git a/Client/Client.csproj b/Client/Client.csproj index 78f2b406..c6191d7b 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -150,7 +150,7 @@ This release is based on the Zeebe 8.1.X release (https://github.com/zeebe-io/ze True - + diff --git a/Client/IZeebeClient.cs b/Client/IZeebeClient.cs index 51e7c877..9ef5d022 100644 --- a/Client/IZeebeClient.cs +++ b/Client/IZeebeClient.cs @@ -136,6 +136,24 @@ public interface IZeebeClient : IJobClient, IDisposable /// a builder for the deploy command /// IDeployResourceCommandStep1 NewDeployCommand(); + + /// + /// Command to evaluate a decision. + /// + /// + /// + /// + /// zeebeClient + /// .NewEvaluateDecisionCommand() + /// .DecisionKey("my-decision") + /// .Variables(json) + /// .Send(); + /// + /// + /// + /// a builder for the deploy command + /// + IEvaluateDecisionCommandStep1 NewEvaluateDecisionCommand(); /// /// Command to create/start a new instance of a process. diff --git a/Client/Impl/Commands/EvaluateDecisionCommand.cs b/Client/Impl/Commands/EvaluateDecisionCommand.cs new file mode 100644 index 00000000..332964db --- /dev/null +++ b/Client/Impl/Commands/EvaluateDecisionCommand.cs @@ -0,0 +1,74 @@ +// +// Copyright (c) 2021 camunda services GmbH (info@camunda.com) +// +// 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 System; +using System.Threading; +using System.Threading.Tasks; +using GatewayProtocol; +using Zeebe.Client.Api.Commands; +using Zeebe.Client.Api.Misc; +using Zeebe.Client.Api.Responses; +using Zeebe.Client.Impl.Responses; + +namespace Zeebe.Client.Impl.Commands; + +public class EvaluateDecisionCommand : IEvaluateDecisionCommandStep1, IEvaluateDecisionCommandStep1.IEvaluateDecisionCommandStep2 +{ + private readonly EvaluateDecisionRequest request; + private readonly Gateway.GatewayClient gatewayClient; + private readonly IAsyncRetryStrategy asyncRetryStrategy; + + public EvaluateDecisionCommand(Gateway.GatewayClient client, IAsyncRetryStrategy asyncRetryStrategy) + { + gatewayClient = client; + request = new EvaluateDecisionRequest(); + this.asyncRetryStrategy = asyncRetryStrategy; + } + + public IEvaluateDecisionCommandStep1.IEvaluateDecisionCommandStep2 DecisionId(string decisionId) + { + request.DecisionId = decisionId; + return this; + } + + public IEvaluateDecisionCommandStep1.IEvaluateDecisionCommandStep2 DecisionKey(long decisionKey) + { + request.DecisionKey = decisionKey; + return this; + } + + public async Task Send(TimeSpan? timeout = null, CancellationToken token = default) + { + var asyncReply = gatewayClient.EvaluateDecisionAsync(request, deadline: timeout?.FromUtcNow(), cancellationToken: token); + var response = await asyncReply.ResponseAsync; + return new EvaluatedDecisionResponse(response); + } + + public async Task Send(CancellationToken cancellationToken) + { + return await Send(token: cancellationToken); + } + + public async Task SendWithRetry(TimeSpan? timespan = null, CancellationToken token = default) + { + return await asyncRetryStrategy.DoWithRetry(() => Send(timespan, token)); + } + + public IEvaluateDecisionCommandStep1.IEvaluateDecisionCommandStep2 Variables(string variables) + { + request.Variables = variables; + return this; + } +} \ No newline at end of file diff --git a/Client/Impl/Responses/EvaluatedDecision.cs b/Client/Impl/Responses/EvaluatedDecision.cs new file mode 100644 index 00000000..6c42e7aa --- /dev/null +++ b/Client/Impl/Responses/EvaluatedDecision.cs @@ -0,0 +1,53 @@ +// +// Copyright (c) 2021 camunda services GmbH (info@camunda.com) +// +// 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 System.Collections.Generic; +using Zeebe.Client.Api.Responses; + +namespace Zeebe.Client.Impl.Responses; + +public class EvaluatedDecision : IEvaluatedDecision +{ + public string DecisionId { get; } + public int DecisionVersion { get; } + public long DecisionKey { get; } + public string DecisionName { get; } + public string DecisionType { get; } + public string DecisionOutput { get; } + public IList EvaluatedInputs { get; } + public IList MatchedRules { get; } + + public EvaluatedDecision(GatewayProtocol.EvaluatedDecision evaluatedDecision) + { + DecisionId = evaluatedDecision.DecisionId; + DecisionVersion = evaluatedDecision.DecisionVersion; + DecisionKey = evaluatedDecision.DecisionKey; + DecisionName = evaluatedDecision.DecisionName; + DecisionType = evaluatedDecision.DecisionType; + DecisionOutput = evaluatedDecision.DecisionOutput; + + EvaluatedInputs = new List(); + foreach (var input in evaluatedDecision.EvaluatedInputs) + { + EvaluatedInputs.Add(new EvaluatedDecisionInput(input)); + } + + MatchedRules = new List(); + foreach (var matchedRule in evaluatedDecision.MatchedRules) + { + MatchedRules.Add(new MatchedDecisionRule(matchedRule)); + } + } +} \ No newline at end of file diff --git a/Client/Impl/Responses/EvaluatedDecisionInput.cs b/Client/Impl/Responses/EvaluatedDecisionInput.cs new file mode 100644 index 00000000..a68cc0ff --- /dev/null +++ b/Client/Impl/Responses/EvaluatedDecisionInput.cs @@ -0,0 +1,32 @@ +// +// Copyright (c) 2021 camunda services GmbH (info@camunda.com) +// +// 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 Zeebe.Client.Api.Responses; + +namespace Zeebe.Client.Impl.Responses; + +public class EvaluatedDecisionInput : IEvaluatedDecisionInput +{ + public string InputId { get; } + public string InputName { get; } + public string InputValue { get; } + + public EvaluatedDecisionInput(GatewayProtocol.EvaluatedDecisionInput evaluatedDecisionInput) + { + InputId = evaluatedDecisionInput.InputId; + InputName = evaluatedDecisionInput.InputName; + InputValue = evaluatedDecisionInput.InputValue; + } +} \ No newline at end of file diff --git a/Client/Impl/Responses/EvaluatedDecisionOutput.cs b/Client/Impl/Responses/EvaluatedDecisionOutput.cs new file mode 100644 index 00000000..bc6a533a --- /dev/null +++ b/Client/Impl/Responses/EvaluatedDecisionOutput.cs @@ -0,0 +1,32 @@ +// +// Copyright (c) 2021 camunda services GmbH (info@camunda.com) +// +// 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 Zeebe.Client.Api.Responses; + +namespace Zeebe.Client.Impl.Responses; + +public class EvaluatedDecisionOutput : IEvaluatedDecisionOutput +{ + public string OutputId { get; } + public string OutputName { get; } + public string OutputValue { get; } + + public EvaluatedDecisionOutput(GatewayProtocol.EvaluatedDecisionOutput evaluatedDecisionOutput) + { + OutputId = evaluatedDecisionOutput.OutputId; + OutputName = evaluatedDecisionOutput.OutputName; + OutputValue = evaluatedDecisionOutput.OutputValue; + } +} \ No newline at end of file diff --git a/Client/Impl/Responses/EvaluatedDecisionResponse.cs b/Client/Impl/Responses/EvaluatedDecisionResponse.cs new file mode 100644 index 00000000..c0c6c4f9 --- /dev/null +++ b/Client/Impl/Responses/EvaluatedDecisionResponse.cs @@ -0,0 +1,52 @@ +// +// Copyright (c) 2021 camunda services GmbH (info@camunda.com) +// +// 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 System.Collections.Generic; +using Zeebe.Client.Api.Responses; + +namespace Zeebe.Client.Impl.Responses; + +public class EvaluatedDecisionResponse : IEvaluateDecisionResponse +{ + public string DecisionId { get; } + public int DecisionVersion { get; } + public long DecisionKey { get; } + public string DecisionName { get; } + public string DecisionRequirementsId { get; } + public long DecisionRequirementsKey { get; } + public string DecisionOutput { get; } + public IList EvaluatedDecisions { get; } + public string FailedDecisionId { get; } + public string FailureMessage { get; } + + public EvaluatedDecisionResponse(GatewayProtocol.EvaluateDecisionResponse response) + { + DecisionId = response.DecisionId; + DecisionVersion = response.DecisionVersion; + DecisionKey = response.DecisionKey; + DecisionName = response.DecisionName; + DecisionRequirementsId = response.DecisionRequirementsId; + DecisionRequirementsKey = response.DecisionRequirementsKey; + DecisionOutput = response.DecisionOutput; + this.EvaluatedDecisions = new List(); + foreach (var decision in response.EvaluatedDecisions) + { + this.EvaluatedDecisions.Add(new EvaluatedDecision(decision)); + } + + FailedDecisionId = response.FailedDecisionId; + FailureMessage = response.FailureMessage; + } +} \ No newline at end of file diff --git a/Client/Impl/Responses/MatchedDecisionRule.cs b/Client/Impl/Responses/MatchedDecisionRule.cs new file mode 100644 index 00000000..534220d4 --- /dev/null +++ b/Client/Impl/Responses/MatchedDecisionRule.cs @@ -0,0 +1,38 @@ +// +// Copyright (c) 2021 camunda services GmbH (info@camunda.com) +// +// 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 System.Collections.Generic; +using Zeebe.Client.Api.Responses; + +namespace Zeebe.Client.Impl.Responses; + +public class MatchedDecisionRule : IMatchedDecisionRule +{ + public string RuleId { get; } + public int RuleIndex { get; } + public IList EvaluatedOutputs { get; } + + public MatchedDecisionRule(GatewayProtocol.MatchedDecisionRule matchedDecisionRule) + { + RuleId = matchedDecisionRule.RuleId; + RuleIndex = matchedDecisionRule.RuleIndex; + + EvaluatedOutputs = new List(); + foreach (var evaluatedOutput in matchedDecisionRule.EvaluatedOutputs) + { + EvaluatedOutputs.Add(new EvaluatedDecisionOutput(evaluatedOutput)); + } + } +} \ No newline at end of file diff --git a/Client/ZeebeClient.cs b/Client/ZeebeClient.cs index a1568f69..c41398a1 100644 --- a/Client/ZeebeClient.cs +++ b/Client/ZeebeClient.cs @@ -142,6 +142,11 @@ public IDeployResourceCommandStep1 NewDeployCommand() return new DeployResourceCommand(gatewayClient, asyncRetryStrategy); } + public IEvaluateDecisionCommandStep1 NewEvaluateDecisionCommand() + { + return new EvaluateDecisionCommand(gatewayClient, asyncRetryStrategy); + } + public ICreateProcessInstanceCommandStep1 NewCreateProcessInstanceCommand() { return new CreateProcessInstanceCommand(gatewayClient, asyncRetryStrategy);