From 2e94bb190597d93fc612fd50e2e159637bcf840c Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Tue, 25 Jun 2024 14:30:18 -0500 Subject: [PATCH 01/30] Added overload for DaprClient DI registration (#1289) * Added overload for DaprClient DI registration allowing the consumer to easily use values from injected services (e.g. IConfiguration). Signed-off-by: Whit Waldo * Added supporting unit test Signed-off-by: Whit Waldo --------- Signed-off-by: Whit Waldo Co-authored-by: Phillip Hoff --- .../DaprServiceCollectionExtensions.cs | 30 ++++++++++++++----- .../DaprServiceCollectionExtensionsTest.cs | 30 +++++++++++++++++++ 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/Dapr.AspNetCore/DaprServiceCollectionExtensions.cs b/src/Dapr.AspNetCore/DaprServiceCollectionExtensions.cs index 8491cb9b2..388015b80 100644 --- a/src/Dapr.AspNetCore/DaprServiceCollectionExtensions.cs +++ b/src/Dapr.AspNetCore/DaprServiceCollectionExtensions.cs @@ -31,18 +31,32 @@ public static class DaprServiceCollectionExtensions /// public static void AddDaprClient(this IServiceCollection services, Action configure = null) { - if (services is null) - { - throw new ArgumentNullException(nameof(services)); - } + ArgumentNullException.ThrowIfNull(services, nameof(services)); services.TryAddSingleton(_ => { var builder = new DaprClientBuilder(); - if (configure != null) - { - configure.Invoke(builder); - } + configure?.Invoke(builder); + + return builder.Build(); + }); + } + + /// + /// Adds Dapr client services to the provided . This does not include integration + /// with ASP.NET Core MVC. Use the AddDapr() extension method on IMvcBuilder to register MVC integration. + /// + /// The . + /// + public static void AddDaprClient(this IServiceCollection services, + Action configure) + { + ArgumentNullException.ThrowIfNull(services, nameof(services)); + + services.TryAddSingleton(serviceProvider => + { + var builder = new DaprClientBuilder(); + configure?.Invoke(serviceProvider, builder); return builder.Build(); }); diff --git a/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs b/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs index 614faf5e4..a82948cf3 100644 --- a/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs +++ b/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs @@ -48,6 +48,31 @@ public void AddDaprClient_RegistersDaprClientOnlyOnce() Assert.True(daprClient.JsonSerializerOptions.PropertyNameCaseInsensitive); } + [Fact] + public void AddDaprClient_RegistersUsingDependencyFromIServiceProvider() + { + + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddDaprClient((provider, builder) => + { + var configProvider = provider.GetRequiredService(); + var caseSensitivity = configProvider.GetCaseSensitivity(); + + builder.UseJsonSerializationOptions(new JsonSerializerOptions + { + PropertyNameCaseInsensitive = caseSensitivity + }); + }); + + var serviceProvider = services.BuildServiceProvider(); + + DaprClientGrpc client = serviceProvider.GetRequiredService() as DaprClientGrpc; + + //Registers with case-insensitive as true by default, but we set as false above + Assert.False(client.JsonSerializerOptions.PropertyNameCaseInsensitive); + } + #if NET8_0_OR_GREATER [Fact] public void AddDaprClient_WithKeyedServices() @@ -65,5 +90,10 @@ public void AddDaprClient_WithKeyedServices() Assert.NotNull(daprClient); } #endif + + private class TestConfigurationProvider + { + public bool GetCaseSensitivity() => false; + } } } From 512c9eaaf4dcab826e9ba5a79dfbee6f06671cf7 Mon Sep 17 00:00:00 2001 From: Phillip Hoff Date: Tue, 25 Jun 2024 13:46:25 -0700 Subject: [PATCH 02/30] Merge `release-1.13` back into `master` (#1285) * Update protos and related use for Dapr 1.13. (#1236) * Update protos and related use. Signed-off-by: Phillip Hoff * Update Dapr runtime version. Signed-off-by: Phillip Hoff * Init properties. Signed-off-by: Phillip Hoff --------- Signed-off-by: Phillip Hoff * Update artifact action versions. (#1240) Signed-off-by: Phillip Hoff * Make recursive true as default (#1243) Signed-off-by: Shivam Kumar * Fix for secret key transformation in multi-value scenarios (#1274) * Add repro test. Signed-off-by: Phillip Hoff * Fix for secret key transformation in multi-value scenarios. Signed-off-by: Phillip Hoff --------- Signed-off-by: Phillip Hoff * Update Dapr version numbers used during testing. Signed-off-by: Phillip Hoff --------- Signed-off-by: Phillip Hoff Signed-off-by: Shivam Kumar Co-authored-by: Shivam Kumar --- .github/workflows/itests.yml | 4 +-- .../DaprSecretStoreConfigurationProvider.cs | 11 +++++-- ...aprSecretStoreConfigurationProviderTest.cs | 29 +++++++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/.github/workflows/itests.yml b/.github/workflows/itests.yml index 2c97343ed..00121c9f5 100644 --- a/.github/workflows/itests.yml +++ b/.github/workflows/itests.yml @@ -43,8 +43,8 @@ jobs: GOARCH: amd64 GOPROXY: https://proxy.golang.org DAPR_CLI_VER: 1.13.0 - DAPR_RUNTIME_VER: 1.13.0 - DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/release-1.13/install/install.sh + DAPR_RUNTIME_VER: 1.13.2 + DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/release-1.12/install/install.sh DAPR_CLI_REF: '' steps: - name: Set up Dapr CLI diff --git a/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationProvider.cs b/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationProvider.cs index 5991a7dad..ecd0ac91b 100644 --- a/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationProvider.cs +++ b/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationProvider.cs @@ -227,8 +227,15 @@ private async Task LoadAsync() $"A duplicate key '{key}' was found in the secret store '{store}'. Please remove any duplicates from your secret store."); } - data.Add(normalizeKey ? NormalizeKey(secretDescriptor.SecretName) : secretDescriptor.SecretName, - result[key]); + // The name of the key "as desired" by the user based on the descriptor. + // + // NOTE: This should vary only if a single secret of the same name is returned. + string desiredKey = StringComparer.Ordinal.Equals(key, secretDescriptor.SecretKey) ? secretDescriptor.SecretName : key; + + // The name of the key normalized based on the configured delimiters. + string normalizedKey = normalizeKey ? NormalizeKey(desiredKey) : desiredKey; + + data.Add(normalizedKey, result[key]); } } diff --git a/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs b/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs index d35275dd1..9bac31352 100644 --- a/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs +++ b/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs @@ -198,6 +198,35 @@ public void LoadSecrets_FromSecretStoreThatCanReturnsMultipleValues() config[secondSecretKey].Should().Be(secondSecretValue); } + [Fact] + public void LoadSecrets_FromSecretStoreThatCanReturnsMultivaluedValues() + { + var storeName = "store"; + var parentSecretKey = "connectionStrings"; + var firstSecretKey = "first_secret"; + var secondSecretKey = "second_secret"; + var firstSecretValue = "secret1"; + var secondSecretValue = "secret2"; + + var secretDescriptors = new[] + { + new DaprSecretDescriptor(parentSecretKey) + }; + + var daprClient = new Mock(); + + daprClient.Setup(c => c.GetSecretAsync(storeName, parentSecretKey, + It.IsAny>(), default)) + .ReturnsAsync(new Dictionary { { firstSecretKey, firstSecretValue }, { secondSecretKey, secondSecretValue } }); + + var config = CreateBuilder() + .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object) + .Build(); + + config[firstSecretKey].Should().Be(firstSecretValue); + config[secondSecretKey].Should().Be(secondSecretValue); + } + [Fact] public void LoadSecrets_FromSecretStoreWithADifferentSecretKeyAndName() { From 84962532f1bec0c646805e630133be3b59b2e26e Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> Date: Wed, 26 Jun 2024 18:19:53 +0200 Subject: [PATCH 03/30] Samples - Add k8s deployment yaml to DemoActor sample (#1308) * up Signed-off-by: Manuel Menegazzo * Fixed build Signed-off-by: Manuel Menegazzo * Added scripts for image build Signed-off-by: Manuel Menegazzo * Added readme Build and push Docker image Signed-off-by: Manuel Menegazzo * Added demo-actor.yaml Signed-off-by: Manuel Menegazzo * Fixed typo Signed-off-by: Manuel Menegazzo * Updated guide, fixed invocation throw curl Signed-off-by: Manuel Menegazzo * Removed dockerfile, updated readme, removed ps1 and sh scripts Signed-off-by: Manuel Menegazzo * Updated base image Signed-off-by: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> Signed-off-by: Manuel Menegazzo * Update demo-actor.yaml Signed-off-by: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> Signed-off-by: Manuel Menegazzo * Added overload for DaprClient DI registration (#1289) * Added overload for DaprClient DI registration allowing the consumer to easily use values from injected services (e.g. IConfiguration). Signed-off-by: Whit Waldo * Added supporting unit test Signed-off-by: Whit Waldo --------- Signed-off-by: Whit Waldo Co-authored-by: Phillip Hoff Signed-off-by: Manuel Menegazzo * Merge `release-1.13` back into `master` (#1285) * Update protos and related use for Dapr 1.13. (#1236) * Update protos and related use. Signed-off-by: Phillip Hoff * Update Dapr runtime version. Signed-off-by: Phillip Hoff * Init properties. Signed-off-by: Phillip Hoff --------- Signed-off-by: Phillip Hoff * Update artifact action versions. (#1240) Signed-off-by: Phillip Hoff * Make recursive true as default (#1243) Signed-off-by: Shivam Kumar * Fix for secret key transformation in multi-value scenarios (#1274) * Add repro test. Signed-off-by: Phillip Hoff * Fix for secret key transformation in multi-value scenarios. Signed-off-by: Phillip Hoff --------- Signed-off-by: Phillip Hoff * Update Dapr version numbers used during testing. Signed-off-by: Phillip Hoff --------- Signed-off-by: Phillip Hoff Signed-off-by: Shivam Kumar Co-authored-by: Shivam Kumar Signed-off-by: Manuel Menegazzo --------- Signed-off-by: Manuel Menegazzo Signed-off-by: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> Signed-off-by: Whit Waldo Signed-off-by: Phillip Hoff Signed-off-by: Shivam Kumar Co-authored-by: Whit Waldo Co-authored-by: Phillip Hoff Co-authored-by: Shivam Kumar --- all.sln | 14 +---- examples/Actor/DemoActor/DemoActor.csproj | 29 ++++++--- examples/Actor/DemoActor/Startup.cs | 2 +- examples/Actor/DemoActor/demo-actor.yaml | 67 ++++++++++++++++++++ examples/Actor/README.md | 77 +++++++++++++++++++++++ 5 files changed, 166 insertions(+), 23 deletions(-) create mode 100644 examples/Actor/DemoActor/demo-actor.yaml diff --git a/all.sln b/all.sln index 0b95478f3..228047852 100644 --- a/all.sln +++ b/all.sln @@ -32,6 +32,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1BD1276E-D28A-45EA-89B1-6AD48471500D}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + README.md = README.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Actors.AspNetCore.Test", "test\Dapr.Actors.AspNetCore.Test\Dapr.Actors.AspNetCore.Test.csproj", "{9C1D6ABA-5EDE-4FA0-A8A9-0AB98CB74737}" @@ -347,16 +348,3 @@ Global SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} EndGlobalSection EndGlobal -8-446B-AECD-DCC2CC871F73} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} - EndGlobalSection -EndGlobal -C991940} = {DD020B34-460F-455F-8D17-CF4A949F100B} - {C74FBA78-13E8-407F-A173-4555AEE41FF3} = {A7F41094-8648-446B-AECD-DCC2CC871F73} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} - EndGlobalSection -EndGlobal diff --git a/examples/Actor/DemoActor/DemoActor.csproj b/examples/Actor/DemoActor/DemoActor.csproj index 1ee37fdbe..24a42ee0e 100644 --- a/examples/Actor/DemoActor/DemoActor.csproj +++ b/examples/Actor/DemoActor/DemoActor.csproj @@ -1,13 +1,24 @@  - - net6 - - - - - - - + + net6 + + + + true + true + demo-actor + + + + + + + + + + + + diff --git a/examples/Actor/DemoActor/Startup.cs b/examples/Actor/DemoActor/Startup.cs index c04dfdcba..da2b9e764 100644 --- a/examples/Actor/DemoActor/Startup.cs +++ b/examples/Actor/DemoActor/Startup.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2021 The Dapr 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/examples/Actor/DemoActor/demo-actor.yaml b/examples/Actor/DemoActor/demo-actor.yaml new file mode 100644 index 000000000..99a8abd34 --- /dev/null +++ b/examples/Actor/DemoActor/demo-actor.yaml @@ -0,0 +1,67 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: statestore +spec: + type: state.in-memory + version: v1 + metadata: + - name: actorStateStore + value: "true" +--- +kind: Service +apiVersion: v1 +metadata: + name: demoactor + labels: + app: demoactor +spec: + selector: + app: demoactor + ports: + - name: app-port + protocol: TCP + port: 5010 + targetPort: app-port + - name: dapr-http + protocol: TCP + port: 3500 + targetPort: 3500 + type: LoadBalancer +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: demoactor + labels: + app: demoactor +spec: + replicas: 1 + selector: + matchLabels: + app: demoactor + template: + metadata: + labels: + app: demoactor + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "demoactor" + dapr.io/app-port: "5010" + dapr.io/enable-api-logging: "true" + dapr.io/sidecar-listen-addresses: "0.0.0.0" + spec: + containers: + - name: demoactor + # image: /demo-actor:latest + image: demo-actor:latest + # if you are using docker desktop, you can use imagePullPolicy: Never to use local image + imagePullPolicy: Never + env: + - name: APP_PORT + value: "5010" + - name: ASPNETCORE_URLS + value: "http://+:5010" + ports: + - name: app-port + containerPort: 5010 diff --git a/examples/Actor/README.md b/examples/Actor/README.md index ddc42cecf..a7bb46c03 100644 --- a/examples/Actor/README.md +++ b/examples/Actor/README.md @@ -80,3 +80,80 @@ On Windows: ```sh curl -X POST http://127.0.0.1:3500/v1.0/actors/DemoActor/abc/method/GetData ``` + +### Build and push Docker image +You can build the docker image of `DemoActor` service by running the following commands in the `DemoActor` project directory: + +``` Bash +dotnet publish --os linux --arch x64 /t:PublishContainer -p ContainerImageTags='"latest"' --self-contained +``` + +The build produce and image with tag `demo-actor:latest` and load it in the local registry. +Now the image can be pushed to your remote Docker registry by running the following commands: + +``` Bash +# Replace with your Docker registry +docker tag demo-actor:latest /demo-actor:latest + +# Push the image to your Docker registry +docker push /demo-actor:latest +``` + +### Deploy the Actor service to Kubernetes +#### Prerequisites +- A Kubernetes cluster with `kubectl` configured to access it. +- Dapr v1.13+ installed on the Kubernetes cluster. Follow the instructions [here](https://docs.dapr.io/getting-started/install-dapr-kubernetes/). +- A Docker registry where you pushed the `DemoActor` image. + +#### Deploy the Actor service +For quick deployment you can install dapr in dev mode using the following command: + +``` Bash +dapr init -k --dev +``` + +To deploy the `DemoActor` service to Kubernetes, you can use the provided Kubernetes manifest file `demo-actor.yaml` in the `DemoActor` project directory. +Before applying the manifest file, replace the image name in the manifest file with the image name you pushed to your Docker registry. + +Part to update in `demo-actor.yaml`: +``` YAML +image: /demoactor:latest +``` + +To install the application in `default` namespace, run the following command: + +``` Bash +kubectl apply -f demo-actor.yaml +``` + +This will deploy the `DemoActor` service to Kubernetes. You can check the status of the deployment by running: + +``` Bash +kubectl get pods -n default --watch +``` + +The manifest create 2 services: + +- `demoactor` service: The service that hosts the `DemoActor` actor. +- `demoactor-dapr` service: The service that hosts the Dapr sidecar for the `DemoActor` actor. + +### Make client calls to the deployed Actor service +To make client calls to the deployed `DemoActor` service, you can use the `ActorClient` project. +Before running the client, update the `DAPR_HTTP_PORT` environment variable in the `ActorClient` project directory to the port on which Dapr is running in the Kubernetes cluster. + +On Linux, MacOS: +``` Bash +export DAPR_HTTP_PORT=3500 +``` + +Than port-forward the `DemoActor` service to your local machine: + +``` Bash +kubectl port-forward svc/demoactor 3500:3500 +``` + +Now you can run the client project from the `ActorClient` directory: + +``` Bash +dotnet run +``` \ No newline at end of file From 76d2c3eadaaf625dbb11da9ff8196e4e8e3eea0f Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Wed, 26 Jun 2024 11:24:20 +0200 Subject: [PATCH 04/30] Consolidated version of coverlet.msbuild, coverlet.collector, xunit, xunit.runner.visualstudio, Microsoft.AspNetCore.Mvc.Testing, Moq to the same version in all projects. Signed-off-by: Manuel Menegazzo --- .../WorkflowUnitTest/WorkflowUnitTest.csproj | 8 ++++---- ...apr.Actors.AspNetCore.IntegrationTest.csproj | 8 ++++---- .../Dapr.Actors.AspNetCore.Test.csproj | 8 ++++---- .../Dapr.Actors.Generators.Test.csproj | 6 +++--- test/Dapr.Actors.Test/Dapr.Actors.Test.csproj | 8 ++++---- .../Dapr.AspNetCore.IntegrationTest.csproj | 8 ++++---- .../Dapr.AspNetCore.Test.csproj | 6 +++--- test/Dapr.Client.Test/Dapr.Client.Test.csproj | 10 +++++----- .../Dapr.E2E.Test.Actors.Generators.csproj | 12 +++++------- test/Dapr.E2E.Test/Dapr.E2E.Test.csproj | 4 ++-- test/Dapr.E2E.Test/Workflows/WorkflowTest.cs | 17 ++++++++--------- .../Dapr.Extensions.Configuration.Test.csproj | 8 ++++---- 12 files changed, 50 insertions(+), 53 deletions(-) diff --git a/examples/Workflow/WorkflowUnitTest/WorkflowUnitTest.csproj b/examples/Workflow/WorkflowUnitTest/WorkflowUnitTest.csproj index 4ce0c9801..7163f4e0c 100644 --- a/examples/Workflow/WorkflowUnitTest/WorkflowUnitTest.csproj +++ b/examples/Workflow/WorkflowUnitTest/WorkflowUnitTest.csproj @@ -8,13 +8,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/Dapr.Actors.AspNetCore.IntegrationTest/Dapr.Actors.AspNetCore.IntegrationTest.csproj b/test/Dapr.Actors.AspNetCore.IntegrationTest/Dapr.Actors.AspNetCore.IntegrationTest.csproj index deccfc1e6..c44d19f61 100644 --- a/test/Dapr.Actors.AspNetCore.IntegrationTest/Dapr.Actors.AspNetCore.IntegrationTest.csproj +++ b/test/Dapr.Actors.AspNetCore.IntegrationTest/Dapr.Actors.AspNetCore.IntegrationTest.csproj @@ -1,14 +1,14 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Dapr.Actors.AspNetCore.Test/Dapr.Actors.AspNetCore.Test.csproj b/test/Dapr.Actors.AspNetCore.Test/Dapr.Actors.AspNetCore.Test.csproj index 83ea494a8..9a8b55c2f 100644 --- a/test/Dapr.Actors.AspNetCore.Test/Dapr.Actors.AspNetCore.Test.csproj +++ b/test/Dapr.Actors.AspNetCore.Test/Dapr.Actors.AspNetCore.Test.csproj @@ -5,16 +5,16 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj b/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj index 212faed2d..02aaf1bb3 100644 --- a/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj +++ b/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj @@ -18,12 +18,12 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj b/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj index 8852dd465..d87ea3cd3 100644 --- a/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj +++ b/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj @@ -5,16 +5,16 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj b/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj index 3cd79d908..ed110191f 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj +++ b/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj @@ -1,15 +1,15 @@  - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj b/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj index aa463be98..32416dd8a 100644 --- a/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj +++ b/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj @@ -1,14 +1,14 @@  - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Dapr.Client.Test/Dapr.Client.Test.csproj b/test/Dapr.Client.Test/Dapr.Client.Test.csproj index aef5b4113..06322f4d1 100644 --- a/test/Dapr.Client.Test/Dapr.Client.Test.csproj +++ b/test/Dapr.Client.Test/Dapr.Client.Test.csproj @@ -1,7 +1,7 @@  - + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -11,11 +11,11 @@ - + - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Dapr.E2E.Test.Actors.Generators/Dapr.E2E.Test.Actors.Generators.csproj b/test/Dapr.E2E.Test.Actors.Generators/Dapr.E2E.Test.Actors.Generators.csproj index 8618647cb..6ef9c009d 100644 --- a/test/Dapr.E2E.Test.Actors.Generators/Dapr.E2E.Test.Actors.Generators.csproj +++ b/test/Dapr.E2E.Test.Actors.Generators/Dapr.E2E.Test.Actors.Generators.csproj @@ -9,14 +9,14 @@ - + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -24,9 +24,7 @@ - + diff --git a/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj b/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj index f899167c4..be3027269 100644 --- a/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj +++ b/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj @@ -5,8 +5,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs b/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs index d95929ca3..2079c7ac6 100644 --- a/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs +++ b/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs @@ -11,15 +11,14 @@ // limitations under the License. // ------------------------------------------------------------------------ using System; -using System.IO; using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Dapr.Client; using FluentAssertions; using Xunit; -using System.Linq; -using System.Diagnostics; namespace Dapr.E2E.Test { @@ -43,7 +42,7 @@ public async Task TestWorkflowLogging() var health = await daprClient.CheckHealthAsync(); health.Should().Be(true, "DaprClient is not healthy"); - var searchTask = Task.Run(async() => + var searchTask = Task.Run(async () => { using (StreamReader reader = new StreamReader(logFilePath)) { @@ -76,7 +75,7 @@ public async Task TestWorkflowLogging() } if (!allLogsFound) { - Assert.True(false, "The logs were not able to found within the timeout"); + Assert.Fail("The logs were not able to found within the timeout"); } } [Fact] @@ -96,7 +95,7 @@ public async Task TestWorkflows() // START WORKFLOW TEST var startResponse = await daprClient.StartWorkflowAsync( - instanceId: instanceId, + instanceId: instanceId, workflowComponent: workflowComponent, workflowName: workflowName, input: input, @@ -131,10 +130,10 @@ public async Task TestWorkflows() // PURGE TEST await daprClient.PurgeWorkflowAsync(instanceId, workflowComponent); - try + try { getResponse = await daprClient.GetWorkflowAsync(instanceId, workflowComponent); - Assert.True(false, "The GetWorkflowAsync call should have failed since the instance was purged"); + Assert.Fail("The GetWorkflowAsync call should have failed since the instance was purged"); } catch (DaprException ex) { @@ -159,7 +158,7 @@ public async Task TestWorkflows() var externalEvents = Task.WhenAll(event1, event2, event3, event4, event5); var winner = await Task.WhenAny(externalEvents, Task.Delay(TimeSpan.FromSeconds(30))); externalEvents.IsCompletedSuccessfully.Should().BeTrue($"Unsuccessful at raising events. Status of events: {externalEvents.IsCompletedSuccessfully}"); - + // Wait up to 30 seconds for the workflow to complete and check the output using var cts = new CancellationTokenSource(delay: TimeSpan.FromSeconds(30)); getResponse = await daprClient.WaitForWorkflowCompletionAsync(instanceId2, workflowComponent, cts.Token); diff --git a/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj b/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj index 7d11d5c40..d259f2ab1 100644 --- a/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj +++ b/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj @@ -1,15 +1,15 @@  - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive From ddce8a2972c4ba4a9f5a7d7c6cd7a22412294d5b Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> Date: Wed, 26 Jun 2024 23:37:19 +0200 Subject: [PATCH 05/30] Added ActorReference creation from the ActorBase class informations (#1277) * Handled creation of ActorReference from Actor base class Signed-off-by: Manuel Menegazzo * Updated null check Signed-off-by: Manuel Menegazzo * Added unit test for GetActorReference from null actore and actor proxy Signed-off-by: Manuel Menegazzo * Added test for ActorReference created inside Actor implementation Signed-off-by: Manuel Menegazzo * Updated description Signed-off-by: Manuel Menegazzo * Fixed test method naming Signed-off-by: Manuel Menegazzo * Added unit test for exception generated in case the type is not convertible to an ActorReference Signed-off-by: Manuel Menegazzo --------- Signed-off-by: Manuel Menegazzo --- src/Dapr.Actors/ActorReference.cs | 30 ++++--- test/Dapr.Actors.Test/ActorReferenceTests.cs | 93 ++++++++++++++++++++ 2 files changed, 111 insertions(+), 12 deletions(-) create mode 100644 test/Dapr.Actors.Test/ActorReferenceTests.cs diff --git a/src/Dapr.Actors/ActorReference.cs b/src/Dapr.Actors/ActorReference.cs index 0e199c3ae..d72b6676f 100644 --- a/src/Dapr.Actors/ActorReference.cs +++ b/src/Dapr.Actors/ActorReference.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2021 The Dapr Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ namespace Dapr.Actors using System; using System.Runtime.Serialization; using Dapr.Actors.Client; + using Dapr.Actors.Runtime; /// /// Encapsulation of a reference to an actor for serialization. @@ -69,23 +70,28 @@ public object Bind(Type actorInterfaceType) private static ActorReference GetActorReference(object actor) { - if (actor == null) - { - throw new ArgumentNullException("actor"); - } + ArgumentNullException.ThrowIfNull(actor, nameof(actor)); - // try as IActorProxy for backward compatibility as customers's mock framework may rely on it before V2 remoting stack. - if (actor is IActorProxy actorProxy) + var actorReference = actor switch { - return new ActorReference() + // try as IActorProxy for backward compatibility as customers's mock framework may rely on it before V2 remoting stack. + IActorProxy actorProxy => new ActorReference() { ActorId = actorProxy.ActorId, ActorType = actorProxy.ActorType, - }; - } + }, + // Handle case when we want to get ActorReference inside the Actor implementation, + // we gather actor id and actor type from Actor base class. + Actor actorBase => new ActorReference() + { + ActorId = actorBase.Id, + ActorType = actorBase.Host.ActorTypeInfo.ActorTypeName, + }, + // Handle case when we can't cast to IActorProxy or Actor. + _ => throw new ArgumentOutOfRangeException("actor", "Invalid actor object type."), + }; - // TODO check for ActorBase - throw new ArgumentOutOfRangeException("actor"); + return actorReference; } } } diff --git a/test/Dapr.Actors.Test/ActorReferenceTests.cs b/test/Dapr.Actors.Test/ActorReferenceTests.cs new file mode 100644 index 000000000..7450f616c --- /dev/null +++ b/test/Dapr.Actors.Test/ActorReferenceTests.cs @@ -0,0 +1,93 @@ +using System; +using System.Threading.Tasks; +using Dapr.Actors.Client; +using Dapr.Actors.Runtime; +using Dapr.Actors.Test; +using Xunit; + +namespace Dapr.Actors +{ + public class ActorReferenceTests + { + [Fact] + public void Get_WhenActorIsNull_ReturnsNull() + { + // Arrange + object actor = null; + + // Act + var result = ActorReference.Get(actor); + + // Assert + Assert.Null(result); + } + + [Fact] + public void Get_FromActorProxy_ReturnsActorReference() + { + // Arrange + var expectedActorId = new ActorId("abc"); + var expectedActorType = "TestActor"; + var proxy = ActorProxy.Create(expectedActorId, typeof(ITestActor), expectedActorType); + + // Act + var actorReference = ActorReference.Get(proxy); + + // Assert + Assert.NotNull(actorReference); + Assert.Equal(expectedActorId, actorReference.ActorId); + Assert.Equal(expectedActorType, actorReference.ActorType); + } + + [Fact] + public async Task Get_FromActorImplementation_ReturnsActorReference() + { + // Arrange + var expectedActorId = new ActorId("abc"); + var expectedActorType = nameof(ActorReferenceTestActor); + var host = ActorHost.CreateForTest(new ActorTestOptions() { ActorId = expectedActorId }); + var actor = new ActorReferenceTestActor(host); + + // Act + var actorReference = await actor.GetActorReference(); + + // Assert + Assert.NotNull(actorReference); + Assert.Equal(expectedActorId, actorReference.ActorId); + Assert.Equal(expectedActorType, actorReference.ActorType); + } + + [Fact] + public void Get_WithInvalidObjectType_ThrowArgumentOutOfRangeException() + { + // Arrange + var actor = new object(); + + // Act + var act = () => ActorReference.Get(actor); + + // Assert + var exception = Assert.Throws(act); + Assert.Equal("actor", exception.ParamName); + Assert.Equal("Invalid actor object type. (Parameter 'actor')", exception.Message); + } + } + + public interface IActorReferenceTestActor : IActor + { + Task GetActorReference(); + } + + public class ActorReferenceTestActor : Actor, IActorReferenceTestActor + { + public ActorReferenceTestActor(ActorHost host) + : base(host) + { + } + + public Task GetActorReference() + { + return Task.FromResult(ActorReference.Get(this)); + } + } +} From 3768a983b7de973e22c7f070c315e13c955b6e07 Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Wed, 3 Jul 2024 12:47:54 -0500 Subject: [PATCH 06/30] Added overload to support SDK supplying query string on invoked URL (#1310) * Refactored extensions and their tests into separate directories Signed-off-by: Whit Waldo * Added overload to method invocation to allow query string parameters to be passed in via the SDK instead of being uncermoniously added to the end of the produced HttpRequestMessage URI Signed-off-by: Whit Waldo * Added unit tests to support implementation Signed-off-by: Whit Waldo * Marking HttpExtensions as internal to prevent external usage and updating to work against Uri instead of HttpRequestMessage. Signed-off-by: Whit Waldo * Updated unit tests to match new extension purpose Signed-off-by: Whit Waldo * Resolved an ambiguous method invocation wherein it was taking the query string and passing it as the payload for a request. Removed the offending method and reworked the remaining configurations so there's no API impact. Signed-off-by: Whit Waldo --------- Signed-off-by: Whit Waldo --- src/Dapr.Client/DaprClient.cs | 40 ++++++++++-- src/Dapr.Client/DaprClientGrpc.cs | 46 +++++++++++++- .../{ => Extensions}/EnumExtensions.cs | 5 +- src/Dapr.Client/Extensions/HttpExtensions.cs | 51 +++++++++++++++ .../DaprClientTest.InvokeMethodAsync.cs | 40 ++++++++++++ .../{ => Extensions}/EnumExtensionTest.cs | 6 +- .../Extensions/HttpExtensionTest.cs | 63 +++++++++++++++++++ 7 files changed, 238 insertions(+), 13 deletions(-) rename src/Dapr.Client/{ => Extensions}/EnumExtensions.cs (88%) create mode 100644 src/Dapr.Client/Extensions/HttpExtensions.cs rename test/Dapr.Client.Test/{ => Extensions}/EnumExtensionTest.cs (87%) create mode 100644 test/Dapr.Client.Test/Extensions/HttpExtensionTest.cs diff --git a/src/Dapr.Client/DaprClient.cs b/src/Dapr.Client/DaprClient.cs index 21777105b..4f89d8668 100644 --- a/src/Dapr.Client/DaprClient.cs +++ b/src/Dapr.Client/DaprClient.cs @@ -306,6 +306,20 @@ public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodN return CreateInvokeMethodRequest(HttpMethod.Post, appId, methodName); } + /// + /// Creates an that can be used to perform service invocation for the + /// application identified by and invokes the method specified by + /// with the POST HTTP method. + /// + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// A collection of key/value pairs to populate the query string from. + /// An for use with SendInvokeMethodRequestAsync. + public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodName, IReadOnlyCollection> queryStringParameters) + { + return CreateInvokeMethodRequest(HttpMethod.Post, appId, methodName, queryStringParameters); + } + /// /// Creates an that can be used to perform service invocation for the /// application identified by and invokes the method specified by @@ -317,6 +331,19 @@ public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodN /// An for use with SendInvokeMethodRequestAsync. public abstract HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName); + /// + /// Creates an that can be used to perform service invocation for the + /// application identified by and invokes the method specified by + /// with the HTTP method specified by . + /// + /// The to use for the invocation request. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// A collection of key/value pairs to populate the query string from. + /// An for use with SendInvokeMethodRequestAsync. + public abstract HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, + string methodName, IReadOnlyCollection> queryStringParameters); + /// /// Creates an that can be used to perform service invocation for the /// application identified by and invokes the method specified by @@ -329,9 +356,9 @@ public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodN /// An for use with SendInvokeMethodRequestAsync. public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodName, TRequest data) { - return CreateInvokeMethodRequest(HttpMethod.Post, appId, methodName, data); + return CreateInvokeMethodRequest(HttpMethod.Post, appId, methodName, new List>(), data); } - + /// /// Creates an that can be used to perform service invocation for the /// application identified by and invokes the method specified by @@ -343,9 +370,10 @@ public HttpRequestMessage CreateInvokeMethodRequest(string appId, stri /// The Dapr application id to invoke the method on. /// The name of the method to invoke. /// The data that will be JSON serialized and provided as the request body. + /// A collection of key/value pairs to populate the query string from. /// An for use with SendInvokeMethodRequestAsync. - public abstract HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName, TRequest data); - + public abstract HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName, IReadOnlyCollection> queryStringParameters, TRequest data); + /// /// Perform health-check of Dapr sidecar. Return 'true' if sidecar is healthy. Otherwise 'false'. /// CheckHealthAsync handle and will return 'false' if error will occur on transport level @@ -526,7 +554,7 @@ public Task InvokeMethodAsync( TRequest data, CancellationToken cancellationToken = default) { - var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, data); + var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, new List>(), data); return InvokeMethodAsync(request, cancellationToken); } @@ -620,7 +648,7 @@ public Task InvokeMethodAsync( TRequest data, CancellationToken cancellationToken = default) { - var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, data); + var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, new List>(), data); return InvokeMethodAsync(request, cancellationToken); } diff --git a/src/Dapr.Client/DaprClientGrpc.cs b/src/Dapr.Client/DaprClientGrpc.cs index 3cd7de526..af245afc3 100644 --- a/src/Dapr.Client/DaprClientGrpc.cs +++ b/src/Dapr.Client/DaprClientGrpc.cs @@ -345,7 +345,32 @@ public override async Task InvokeBindingAsync(BindingRequest re #region InvokeMethod Apis + /// + /// Creates an that can be used to perform service invocation for the + /// application identified by and invokes the method specified by + /// with the HTTP method specified by . + /// + /// The to use for the invocation request. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// An for use with SendInvokeMethodRequestAsync. public override HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName) + { + return CreateInvokeMethodRequest(httpMethod, appId, methodName, new List>()); + } + + /// + /// Creates an that can be used to perform service invocation for the + /// application identified by and invokes the method specified by + /// with the HTTP method specified by . + /// + /// The to use for the invocation request. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// A collection of key/value pairs to populate the query string from. + /// An for use with SendInvokeMethodRequestAsync. + public override HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName, + IReadOnlyCollection> queryStringParameters) { ArgumentVerifier.ThrowIfNull(httpMethod, nameof(httpMethod)); ArgumentVerifier.ThrowIfNullOrEmpty(appId, nameof(appId)); @@ -356,7 +381,8 @@ public override HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMeth // // This approach avoids some common pitfalls that could lead to undesired encoding. var path = $"/v1.0/invoke/{appId}/method/{methodName.TrimStart('/')}"; - var request = new HttpRequestMessage(httpMethod, new Uri(this.httpEndpoint, path)); + var requestUri = new Uri(this.httpEndpoint, path).AddQueryParameters(queryStringParameters); + var request = new HttpRequestMessage(httpMethod, requestUri); request.Options.Set(new HttpRequestOptionsKey(AppIdKey), appId); request.Options.Set(new HttpRequestOptionsKey(MethodNameKey), methodName); @@ -369,13 +395,27 @@ public override HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMeth return request; } - public override HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName, TRequest data) + /// + /// Creates an that can be used to perform service invocation for the + /// application identified by and invokes the method specified by + /// with the HTTP method specified by and a JSON serialized request body specified by + /// . + /// + /// The type of the data that will be JSON serialized and provided as the request body. + /// The to use for the invocation request. + /// The Dapr application id to invoke the method on. + /// The name of the method to invoke. + /// The data that will be JSON serialized and provided as the request body. + /// A collection of key/value pairs to populate the query string from. + /// An for use with SendInvokeMethodRequestAsync. + public override HttpRequestMessage CreateInvokeMethodRequest(HttpMethod httpMethod, string appId, string methodName, + IReadOnlyCollection> queryStringParameters, TRequest data) { ArgumentVerifier.ThrowIfNull(httpMethod, nameof(httpMethod)); ArgumentVerifier.ThrowIfNullOrEmpty(appId, nameof(appId)); ArgumentVerifier.ThrowIfNull(methodName, nameof(methodName)); - var request = CreateInvokeMethodRequest(httpMethod, appId, methodName); + var request = CreateInvokeMethodRequest(httpMethod, appId, methodName, queryStringParameters); request.Content = JsonContent.Create(data, options: this.JsonSerializerOptions); return request; } diff --git a/src/Dapr.Client/EnumExtensions.cs b/src/Dapr.Client/Extensions/EnumExtensions.cs similarity index 88% rename from src/Dapr.Client/EnumExtensions.cs rename to src/Dapr.Client/Extensions/EnumExtensions.cs index 6b058ca77..df9c9ad33 100644 --- a/src/Dapr.Client/EnumExtensions.cs +++ b/src/Dapr.Client/Extensions/EnumExtensions.cs @@ -11,6 +11,7 @@ // limitations under the License. // ------------------------------------------------------------------------ +#nullable enable using System; using System.Reflection; using System.Runtime.Serialization; @@ -27,12 +28,14 @@ internal static class EnumExtensions /// public static string GetValueFromEnumMember(this T value) where T : Enum { + ArgumentNullException.ThrowIfNull(value, nameof(value)); + var memberInfo = typeof(T).GetMember(value.ToString(), BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly); if (memberInfo.Length <= 0) return value.ToString(); var attributes = memberInfo[0].GetCustomAttributes(typeof(EnumMemberAttribute), false); - return attributes.Length > 0 ? ((EnumMemberAttribute)attributes[0]).Value : value.ToString(); + return (attributes.Length > 0 ? ((EnumMemberAttribute)attributes[0]).Value : value.ToString()) ?? value.ToString(); } } } diff --git a/src/Dapr.Client/Extensions/HttpExtensions.cs b/src/Dapr.Client/Extensions/HttpExtensions.cs new file mode 100644 index 000000000..259d2747d --- /dev/null +++ b/src/Dapr.Client/Extensions/HttpExtensions.cs @@ -0,0 +1,51 @@ +// ------------------------------------------------------------------------ +// Copyright 2024 The Dapr 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. +// ------------------------------------------------------------------------ + +#nullable enable +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapr.Client +{ + /// + /// Provides extensions specific to HTTP types. + /// + internal static class HttpExtensions + { + /// + /// Appends key/value pairs to the query string on an HttpRequestMessage. + /// + /// The uri to append the query string parameters to. + /// The key/value pairs to populate the query string with. + public static Uri AddQueryParameters(this Uri? uri, + IReadOnlyCollection>? queryStringParameters) + { + ArgumentNullException.ThrowIfNull(uri, nameof(uri)); + if (queryStringParameters is null) + return uri; + + var uriBuilder = new UriBuilder(uri); + var qsBuilder = new StringBuilder(uriBuilder.Query); + foreach (var kvParam in queryStringParameters) + { + if (qsBuilder.Length > 0) + qsBuilder.Append('&'); + qsBuilder.Append($"{Uri.EscapeDataString(kvParam.Key)}={Uri.EscapeDataString(kvParam.Value)}"); + } + + uriBuilder.Query = qsBuilder.ToString(); + return uriBuilder.Uri; + } + } +} diff --git a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs index 5d46000a1..484f327d0 100644 --- a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs +++ b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs @@ -518,6 +518,18 @@ public async Task CreateInvokeMethodRequest_TransformsUrlCorrectly(string method Assert.Equal(new Uri(expected).AbsoluteUri, request.RequestUri.AbsoluteUri); } + [Fact] + public async Task CreateInvokeMethodRequest_AppendQueryStringValuesCorrectly() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var request = client.InnerClient.CreateInvokeMethodRequest("test-app", "mymethod", (IReadOnlyCollection>)new List> { new("a", "0"), new("b", "1") }); + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/test-app/method/mymethod?a=0&b=1").AbsoluteUri, request.RequestUri.AbsoluteUri); + } + [Fact] public async Task CreateInvokeMethodRequest_WithoutApiToken_CreatesHttpRequestWithoutApiTokenHeader() { @@ -617,6 +629,34 @@ public async Task CreateInvokeMethodRequest_WithData_CreatesJsonContent() Assert.Equal(data.Color, actual.Color); } + [Fact] + public async Task CreateInvokeMethodRequest_WithData_CreatesJsonContentWithQueryString() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var data = new Widget + { + Color = "red", + }; + + var request = client.InnerClient.CreateInvokeMethodRequest(HttpMethod.Post, "test-app", "test", new List> { new("a", "0"), new("b", "1") }, data); + + Assert.Equal(new Uri("https://test-endpoint:3501/v1.0/invoke/test-app/method/test?a=0&b=1").AbsoluteUri, request.RequestUri.AbsoluteUri); + + var content = Assert.IsType(request.Content); + Assert.Equal(typeof(Widget), content.ObjectType); + Assert.Same(data, content.Value); + + // the best way to verify the usage of the correct settings object + var actual = await content.ReadFromJsonAsync(this.jsonSerializerOptions); + Assert.Equal(data.Color, actual.Color); + } + + + [Fact] public async Task InvokeMethodWithResponseAsync_ReturnsMessageWithoutCheckingStatus() { diff --git a/test/Dapr.Client.Test/EnumExtensionTest.cs b/test/Dapr.Client.Test/Extensions/EnumExtensionTest.cs similarity index 87% rename from test/Dapr.Client.Test/EnumExtensionTest.cs rename to test/Dapr.Client.Test/Extensions/EnumExtensionTest.cs index be78c3861..83c4354f9 100644 --- a/test/Dapr.Client.Test/EnumExtensionTest.cs +++ b/test/Dapr.Client.Test/Extensions/EnumExtensionTest.cs @@ -1,7 +1,7 @@ using System.Runtime.Serialization; using Xunit; -namespace Dapr.Client.Test +namespace Dapr.Client.Test.Extensions { public class EnumExtensionTest { @@ -29,9 +29,9 @@ public void GetValueFromEnumMember_BlueResolvesAsExpected() public enum TestEnum { - [EnumMember(Value="red")] + [EnumMember(Value = "red")] Red, - [EnumMember(Value="YELLOW")] + [EnumMember(Value = "YELLOW")] Yellow, Blue } diff --git a/test/Dapr.Client.Test/Extensions/HttpExtensionTest.cs b/test/Dapr.Client.Test/Extensions/HttpExtensionTest.cs new file mode 100644 index 000000000..7b93c1c91 --- /dev/null +++ b/test/Dapr.Client.Test/Extensions/HttpExtensionTest.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.Net.Http; +using Xunit; + +namespace Dapr.Client.Test.Extensions +{ + public class HttpExtensionTest + { + [Fact] + public void AddQueryParameters_ReturnsEmptyQueryStringWithNullParameters() + { + const string uri = "https://localhost/mypath"; + var httpRq = new HttpRequestMessage(HttpMethod.Get, uri); + var updatedUri = httpRq.RequestUri.AddQueryParameters(null); + Assert.Equal(uri, updatedUri.AbsoluteUri); + } + + [Fact] + public void AddQueryParameters_ReturnsOriginalQueryStringWithNullParameters() + { + const string uri = "https://localhost/mypath?a=0&b=1"; + var httpRq = new HttpRequestMessage(HttpMethod.Get, uri); + var updatedUri = httpRq.RequestUri.AddQueryParameters(null); + Assert.Equal(uri, updatedUri.AbsoluteUri); + } + + [Fact] + public void AddQueryParameters_BuildsQueryString() + { + var httpRq = new HttpRequestMessage(HttpMethod.Get, "https://localhost/mypath?a=0"); + var updatedUri = httpRq.RequestUri.AddQueryParameters(new List> + { + new("test", "value") + }); + Assert.Equal("https://localhost/mypath?a=0&test=value", updatedUri.AbsoluteUri); + } + + [Fact] + public void AddQueryParameters_BuildQueryStringWithDuplicateKeys() + { + var httpRq = new HttpRequestMessage(HttpMethod.Get, "https://localhost/mypath"); + var updatedUri = httpRq.RequestUri.AddQueryParameters(new List> + { + new("test", "1"), + new("test", "2"), + new("test", "3") + }); + Assert.Equal("https://localhost/mypath?test=1&test=2&test=3", updatedUri.AbsoluteUri); + } + + [Fact] + public void AddQueryParameters_EscapeSpacesInValues() + { + var httpRq = new HttpRequestMessage(HttpMethod.Get, "https://localhost/mypath"); + var updatedUri = httpRq.RequestUri.AddQueryParameters(new List> + { + new("name1", "John Doe"), + new("name2", "Jane Doe") + }); + Assert.Equal("https://localhost/mypath?name1=John%20Doe&name2=Jane%20Doe", updatedUri.AbsoluteUri); + } + } +} From 56367963f46257fbcb109f671ac78dc445435012 Mon Sep 17 00:00:00 2001 From: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Date: Tue, 23 Jul 2024 12:26:24 -0400 Subject: [PATCH 07/30] fix (#1329) Signed-off-by: Hannah Hunter --- daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md index f608cd07d..09aed4c8f 100644 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md +++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md @@ -56,7 +56,7 @@ Console.WriteLine("Returned: id:{0} | Balance:{1}", account.Id, account.Balance) #### gRPC You can use the `DaprClient` to invoke your services over gRPC. -{{% codetab %}} + ```csharp using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20)); var invoker = DaprClient.CreateInvocationInvoker(appId: myAppId, daprEndpoint: serviceEndpoint); @@ -67,8 +67,6 @@ await client.MyMethodAsync(new Empty(), options); Assert.Equal(StatusCode.DeadlineExceeded, ex.StatusCode); ``` -{{% /codetab %}} - - For a full guide on service invocation visit [How-To: Invoke a service]({{< ref howto-invoke-discover-services.md >}}). @@ -162,7 +160,7 @@ var secrets = await client.GetSecretAsync("mysecretstore", "key-value-pair-secre Console.WriteLine($"Got secret keys: {string.Join(", ", secrets.Keys)}"); ``` -{{% / codetab %}} +{{% /codetab %}} {{% codetab %}} From b8e276728935c66b0a335b5aa2ca4102c560dd3d Mon Sep 17 00:00:00 2001 From: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Date: Fri, 9 Aug 2024 00:37:27 -0400 Subject: [PATCH 08/30] link to non-dapr endpoint howto (#1335) Signed-off-by: Hannah Hunter --- daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md index 09aed4c8f..cab44468b 100644 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md +++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md @@ -24,6 +24,12 @@ The .NET SDK allows you to interface with all of the [Dapr building blocks]({{< #### HTTP You can either use the `DaprClient` or `System.Net.Http.HttpClient` to invoke your services. +{{% alert title="Note" color="primary" %}} + You can also [invoke a non-Dapr endpoint using either a named `HTTPEndpoint` or an FQDN URL to the non-Dapr environment]({{< ref "howto-invoke-non-dapr-endpoints.md#using-an-httpendpoint-resource-or-fqdn-url-for-non-dapr-endpoints" >}}). + +{{% /alert %}} + + {{< tabs SDK HTTP>}} {{% codetab %}} From 74f6b0127fb33d72ff1c62aecfce8b1694a6b334 Mon Sep 17 00:00:00 2001 From: Phillip Hoff Date: Wed, 21 Aug 2024 23:47:39 -0700 Subject: [PATCH 09/30] Merge 1.14 release branch back into `master`. (#1337) --- .devcontainer/localinit.sh | 2 +- .github/workflows/itests.yml | 6 +- daprdocs/content/en/dotnet-sdk-docs/_index.md | 2 +- .../dotnet-actors/dotnet-actors-howto.md | 2 +- examples/Actor/README.md | 4 +- .../BulkPublishEventExample/README.md | 2 +- .../PublishEventExample/README.md | 2 +- samples/Client/README.md | 2 +- .../Protos/dapr/proto/common/v1/common.proto | 2 +- .../dapr/proto/dapr/v1/appcallback.proto | 32 ++- .../Protos/dapr/proto/dapr/v1/dapr.proto | 187 ++++++++++++++++-- 11 files changed, 212 insertions(+), 31 deletions(-) diff --git a/.devcontainer/localinit.sh b/.devcontainer/localinit.sh index 80b27e4f4..69c4c1274 100644 --- a/.devcontainer/localinit.sh +++ b/.devcontainer/localinit.sh @@ -6,4 +6,4 @@ az extension add --name containerapp --yes nvm install v18.12.1 # initialize Dapr -dapr init --runtime-version=1.10.0-rc.2 \ No newline at end of file +dapr init --runtime-version=1.14.0 \ No newline at end of file diff --git a/.github/workflows/itests.yml b/.github/workflows/itests.yml index 00121c9f5..36741ce7c 100644 --- a/.github/workflows/itests.yml +++ b/.github/workflows/itests.yml @@ -42,9 +42,9 @@ jobs: GOOS: linux GOARCH: amd64 GOPROXY: https://proxy.golang.org - DAPR_CLI_VER: 1.13.0 - DAPR_RUNTIME_VER: 1.13.2 - DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/release-1.12/install/install.sh + DAPR_CLI_VER: 1.14.0 + DAPR_RUNTIME_VER: 1.14.0 + DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/release-1.14/install/install.sh DAPR_CLI_REF: '' steps: - name: Set up Dapr CLI diff --git a/daprdocs/content/en/dotnet-sdk-docs/_index.md b/daprdocs/content/en/dotnet-sdk-docs/_index.md index e823ca29f..121dde310 100644 --- a/daprdocs/content/en/dotnet-sdk-docs/_index.md +++ b/daprdocs/content/en/dotnet-sdk-docs/_index.md @@ -18,7 +18,7 @@ Dapr offers a variety of packages to help with the development of .NET applicati - [Dapr CLI]({{< ref install-dapr-cli.md >}}) installed - Initialized [Dapr environment]({{< ref install-dapr-selfhost.md >}}) -- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed +- [.NET 6+](https://dotnet.microsoft.com/download) installed ## Installation diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md index 8229d6820..eaa13625d 100644 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md +++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-howto.md @@ -45,7 +45,7 @@ This project contains the implementation of the actor client which calls MyActor - [Dapr CLI]({{< ref install-dapr-cli.md >}}) installed. - Initialized [Dapr environment]({{< ref install-dapr-selfhost.md >}}). -- [.NET Core 3.1 or .NET 6+](https://dotnet.microsoft.com/download) installed. Dapr .NET SDK uses [ASP.NET Core](https://docs.microsoft.com/aspnet/core/introduction-to-aspnet-core?view=aspnetcore-6.0). +- [.NET 6+](https://dotnet.microsoft.com/download) installed. Dapr .NET SDK uses [ASP.NET Core](https://docs.microsoft.com/aspnet/core/introduction-to-aspnet-core?view=aspnetcore-6.0). ## Step 0: Prepare diff --git a/examples/Actor/README.md b/examples/Actor/README.md index a7bb46c03..89b6bf0bb 100644 --- a/examples/Actor/README.md +++ b/examples/Actor/README.md @@ -4,7 +4,7 @@ The Actor example shows how to create a virtual actor (`DemoActor`) and invoke i ## Prerequisites -- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed +- [.NET 6+](https://dotnet.microsoft.com/download) installed - [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/) - [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/) - [Dapr .NET SDK](https://github.com/dapr/dotnet-sdk/) @@ -102,7 +102,7 @@ docker push /demo-actor:latest ### Deploy the Actor service to Kubernetes #### Prerequisites - A Kubernetes cluster with `kubectl` configured to access it. -- Dapr v1.13+ installed on the Kubernetes cluster. Follow the instructions [here](https://docs.dapr.io/getting-started/install-dapr-kubernetes/). +- Dapr v1.14+ installed on the Kubernetes cluster. Follow the instructions [here](https://docs.dapr.io/getting-started/install-dapr-kubernetes/). - A Docker registry where you pushed the `DemoActor` image. #### Deploy the Actor service diff --git a/examples/Client/PublishSubscribe/BulkPublishEventExample/README.md b/examples/Client/PublishSubscribe/BulkPublishEventExample/README.md index dfcc99ca6..39d206fa2 100644 --- a/examples/Client/PublishSubscribe/BulkPublishEventExample/README.md +++ b/examples/Client/PublishSubscribe/BulkPublishEventExample/README.md @@ -2,7 +2,7 @@ ## Prerequisites -- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed +- [.NET 6+](https://dotnet.microsoft.com/download) installed - [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/) - [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/) - [Dapr .NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/) diff --git a/examples/Client/PublishSubscribe/PublishEventExample/README.md b/examples/Client/PublishSubscribe/PublishEventExample/README.md index 455fc2537..9f3af565f 100644 --- a/examples/Client/PublishSubscribe/PublishEventExample/README.md +++ b/examples/Client/PublishSubscribe/PublishEventExample/README.md @@ -2,7 +2,7 @@ ## Prerequisites -- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed +- [.NET 6+](https://dotnet.microsoft.com/download) installed - [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/) - [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/) - [Dapr .NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/) diff --git a/samples/Client/README.md b/samples/Client/README.md index 2501bfadc..2bb738d89 100644 --- a/samples/Client/README.md +++ b/samples/Client/README.md @@ -8,7 +8,7 @@ The following examples will show you how to: ## Prerequisites -* [.Net Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) +* [.NET 6+](https://dotnet.microsoft.com/download) * [Dapr CLI](https://github.com/dapr/cli) * [Dapr DotNet SDK](https://github.com/dapr/dotnet-sdk) diff --git a/src/Dapr.Client/Protos/dapr/proto/common/v1/common.proto b/src/Dapr.Client/Protos/dapr/proto/common/v1/common.proto index 1e63b885d..4acf9159d 100644 --- a/src/Dapr.Client/Protos/dapr/proto/common/v1/common.proto +++ b/src/Dapr.Client/Protos/dapr/proto/common/v1/common.proto @@ -157,4 +157,4 @@ message ConfigurationItem { // the metadata which will be passed to/from configuration store component. map metadata = 3; -} +} \ No newline at end of file diff --git a/src/Dapr.Client/Protos/dapr/proto/dapr/v1/appcallback.proto b/src/Dapr.Client/Protos/dapr/proto/dapr/v1/appcallback.proto index 823c0aae4..a86040364 100644 --- a/src/Dapr.Client/Protos/dapr/proto/dapr/v1/appcallback.proto +++ b/src/Dapr.Client/Protos/dapr/proto/dapr/v1/appcallback.proto @@ -15,6 +15,7 @@ syntax = "proto3"; package dapr.proto.runtime.v1; +import "google/protobuf/any.proto"; import "google/protobuf/empty.proto"; import "dapr/proto/common/v1/common.proto"; import "google/protobuf/struct.proto"; @@ -59,8 +60,37 @@ service AppCallbackHealthCheck { service AppCallbackAlpha { // Subscribes bulk events from Pubsub rpc OnBulkTopicEventAlpha1(TopicEventBulkRequest) returns (TopicEventBulkResponse) {} + + // Sends job back to the app's endpoint at trigger time. + rpc OnJobEventAlpha1 (JobEventRequest) returns (JobEventResponse); +} + +message JobEventRequest { + // Job name. + string name = 1; + + // Job data to be sent back to app. + google.protobuf.Any data = 2; + + // Required. method is a method name which will be invoked by caller. + string method = 3; + + // The type of data content. + // + // This field is required if data delivers http request body + // Otherwise, this is optional. + string content_type = 4; + + // HTTP specific fields if request conveys http-compatible request. + // + // This field is required for http-compatible request. Otherwise, + // this field is optional. + common.v1.HTTPExtension http_extension = 5; } +// JobEventResponse is the response from the app when a job is triggered. +message JobEventResponse {} + // TopicEventRequest message is compatible with CloudEvent spec v1.0 // https://github.com/cloudevents/spec/blob/v1.0/spec.md message TopicEventRequest { @@ -310,4 +340,4 @@ message ListInputBindingsResponse { // HealthCheckResponse is the message with the response to the health check. // This message is currently empty as used as placeholder. -message HealthCheckResponse {} +message HealthCheckResponse {} \ No newline at end of file diff --git a/src/Dapr.Client/Protos/dapr/proto/dapr/v1/dapr.proto b/src/Dapr.Client/Protos/dapr/proto/dapr/v1/dapr.proto index 5ec1cc9d8..4185fb391 100644 --- a/src/Dapr.Client/Protos/dapr/proto/dapr/v1/dapr.proto +++ b/src/Dapr.Client/Protos/dapr/proto/dapr/v1/dapr.proto @@ -19,6 +19,7 @@ import "google/protobuf/any.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/timestamp.proto"; import "dapr/proto/common/v1/common.proto"; +import "dapr/proto/dapr/v1/appcallback.proto"; option csharp_namespace = "Dapr.Client.Autogen.Grpc.v1"; option java_outer_classname = "DaprProtos"; @@ -58,6 +59,10 @@ service Dapr { // Bulk Publishes multiple events to the specified topic. rpc BulkPublishEventAlpha1(BulkPublishRequest) returns (BulkPublishResponse) {} + // SubscribeTopicEventsAlpha1 subscribes to a PubSub topic and receives topic + // events from it. + rpc SubscribeTopicEventsAlpha1(stream SubscribeTopicEventsRequestAlpha1) returns (stream TopicEventRequest) {} + // Invokes binding data to specific output bindings rpc InvokeBinding(InvokeBindingRequest) returns (InvokeBindingResponse) {} @@ -188,6 +193,15 @@ service Dapr { rpc RaiseEventWorkflowBeta1 (RaiseEventWorkflowRequest) returns (google.protobuf.Empty) {} // Shutdown the sidecar rpc Shutdown (ShutdownRequest) returns (google.protobuf.Empty) {} + + // Create and schedule a job + rpc ScheduleJobAlpha1(ScheduleJobRequest) returns (ScheduleJobResponse) {} + + // Gets a scheduled job + rpc GetJobAlpha1(GetJobRequest) returns (GetJobResponse) {} + + // Delete a job + rpc DeleteJobAlpha1(DeleteJobRequest) returns (DeleteJobResponse) {} } // InvokeServiceRequest represents the request message for Service invocation. @@ -411,6 +425,47 @@ message BulkPublishResponseFailedEntry { string error = 2; } +// SubscribeTopicEventsRequestAlpha1 is a message containing the details for +// subscribing to a topic via streaming. +// The first message must always be the initial request. All subsequent +// messages must be event responses. +message SubscribeTopicEventsRequestAlpha1 { + oneof subscribe_topic_events_request_type { + SubscribeTopicEventsInitialRequestAlpha1 initial_request = 1; + SubscribeTopicEventsResponseAlpha1 event_response = 2; + } +} + +// SubscribeTopicEventsInitialRequestAlpha1 is the initial message containing the +// details for subscribing to a topic via streaming. +message SubscribeTopicEventsInitialRequestAlpha1 { + // The name of the pubsub component + string pubsub_name = 1; + + // The pubsub topic + string topic = 2; + + // The metadata passing to pub components + // + // metadata property: + // - key : the key of the message. + map metadata = 3; + + // dead_letter_topic is the topic to which messages that fail to be processed + // are sent. + optional string dead_letter_topic = 4; +} + +// SubscribeTopicEventsResponseAlpha1 is a message containing the result of a +// subscription to a topic. +message SubscribeTopicEventsResponseAlpha1 { + // id is the unique identifier for the subscription request. + string id = 1; + + // status is the result of the subscription request. + TopicEventResponse status = 2; +} + // InvokeBindingRequest is the message to send data to output bindings message InvokeBindingRequest { // The name of the output binding to invoke. @@ -504,45 +559,45 @@ message ExecuteStateTransactionRequest { // RegisterActorTimerRequest is the message to register a timer for an actor of a given type and id. message RegisterActorTimerRequest { - string actor_type = 1; - string actor_id = 2; + string actor_type = 1 [json_name = "actorType"]; + string actor_id = 2 [json_name = "actorId"]; string name = 3; - string due_time = 4; + string due_time = 4 [json_name = "dueTime"]; string period = 5; string callback = 6; - bytes data = 7; + bytes data = 7; string ttl = 8; } // UnregisterActorTimerRequest is the message to unregister an actor timer message UnregisterActorTimerRequest { - string actor_type = 1; - string actor_id = 2; + string actor_type = 1 [json_name = "actorType"]; + string actor_id = 2 [json_name = "actorId"]; string name = 3; } // RegisterActorReminderRequest is the message to register a reminder for an actor of a given type and id. message RegisterActorReminderRequest { - string actor_type = 1; - string actor_id = 2; + string actor_type = 1 [json_name = "actorType"]; + string actor_id = 2 [json_name = "actorId"]; string name = 3; - string due_time = 4; + string due_time = 4 [json_name = "dueTime"]; string period = 5; - bytes data = 6; + bytes data = 6; string ttl = 7; } // UnregisterActorReminderRequest is the message to unregister an actor reminder. message UnregisterActorReminderRequest { - string actor_type = 1; - string actor_id = 2; + string actor_type = 1 [json_name = "actorType"]; + string actor_id = 2 [json_name = "actorId"]; string name = 3; } // GetActorStateRequest is the message to get key-value states from specific actor. message GetActorStateRequest { - string actor_type = 1; - string actor_id = 2; + string actor_type = 1 [json_name = "actorType"]; + string actor_id = 2 [json_name = "actorId"]; string key = 3; } @@ -556,8 +611,8 @@ message GetActorStateResponse { // ExecuteActorStateTransactionRequest is the message to execute multiple operations on a specified actor. message ExecuteActorStateTransactionRequest { - string actor_type = 1; - string actor_id = 2; + string actor_type = 1 [json_name = "actorType"]; + string actor_id = 2 [json_name = "actorId"]; repeated TransactionalActorStateOperation operations = 3; } @@ -575,8 +630,8 @@ message TransactionalActorStateOperation { // InvokeActorRequest is the message to call an actor. message InvokeActorRequest { - string actor_type = 1; - string actor_id = 2; + string actor_type = 1 [json_name = "actorType"]; + string actor_id = 2 [json_name = "actorId"]; string method = 3; bytes data = 4; map metadata = 5; @@ -605,6 +660,7 @@ message GetMetadataResponse { string runtime_version = 8 [json_name = "runtimeVersion"]; repeated string enabled_features = 9 [json_name = "enabledFeatures"]; ActorRuntime actor_runtime = 10 [json_name = "actorRuntime"]; + //TODO: Cassie: probably add scheduler runtime status } message ActorRuntime { @@ -665,6 +721,19 @@ message PubsubSubscription { map metadata = 3 [json_name = "metadata"]; PubsubSubscriptionRules rules = 4 [json_name = "rules"]; string dead_letter_topic = 5 [json_name = "deadLetterTopic"]; + PubsubSubscriptionType type = 6 [json_name = "type"]; +} + +// PubsubSubscriptionType indicates the type of subscription +enum PubsubSubscriptionType { + // UNKNOWN is the default value for the subscription type. + UNKNOWN = 0; + // Declarative subscription (k8s CRD) + DECLARATIVE = 1; + // Programmatically created subscription + PROGRAMMATIC = 2; + // Bidirectional Streaming subscription + STREAMING = 3; } message PubsubSubscriptionRules { @@ -1108,3 +1177,85 @@ message PurgeWorkflowRequest { message ShutdownRequest { // Empty } + +// Job is the definition of a job. At least one of schedule or due_time must be +// provided but can also be provided together. +message Job { + // The unique name for the job. + string name = 1 [json_name = "name"]; + + // schedule is an optional schedule at which the job is to be run. + // Accepts both systemd timer style cron expressions, as well as human + // readable '@' prefixed period strings as defined below. + // + // Systemd timer style cron accepts 6 fields: + // seconds | minutes | hours | day of month | month | day of week + // 0-59 | 0-59 | 0-23 | 1-31 | 1-12/jan-dec | 0-7/sun-sat + // + // "0 30 * * * *" - every hour on the half hour + // "0 15 3 * * *" - every day at 03:15 + // + // Period string expressions: + // Entry | Description | Equivalent To + // ----- | ----------- | ------------- + // @every | Run every (e.g. '@every 1h30m') | N/A + // @yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 0 1 1 * + // @monthly | Run once a month, midnight, first of month | 0 0 0 1 * * + // @weekly | Run once a week, midnight on Sunday | 0 0 0 * * 0 + // @daily (or @midnight) | Run once a day, midnight | 0 0 0 * * * + // @hourly | Run once an hour, beginning of hour | 0 0 * * * * + optional string schedule = 2 [json_name = "schedule"]; + + // repeats is the optional number of times in which the job should be + // triggered. If not set, the job will run indefinitely or until expiration. + optional uint32 repeats = 3 [json_name = "repeats"]; + + // due_time is the optional time at which the job should be active, or the + // "one shot" time if other scheduling type fields are not provided. Accepts + // a "point in time" string in the format of RFC3339, Go duration string + // (calculated from job creation time), or non-repeating ISO8601. + optional string due_time = 4 [json_name = "dueTime"]; + + // ttl is the optional time to live or expiration of the job. Accepts a + // "point in time" string in the format of RFC3339, Go duration string + // (calculated from job creation time), or non-repeating ISO8601. + optional string ttl = 5 [json_name = "ttl"]; + + // payload is the serialized job payload that will be sent to the recipient + // when the job is triggered. + google.protobuf.Any data = 6 [json_name = "data"]; +} + +// ScheduleJobRequest is the message to create/schedule the job. +message ScheduleJobRequest { + // The job details. + Job job = 1; +} + +// ScheduleJobResponse is the message response to create/schedule the job. +message ScheduleJobResponse { + // Empty +} + +// GetJobRequest is the message to retrieve a job. +message GetJobRequest { + // The name of the job. + string name = 1; +} + +// GetJobResponse is the message's response for a job retrieved. +message GetJobResponse { + // The job details. + Job job = 1; +} + +// DeleteJobRequest is the message to delete the job by name. +message DeleteJobRequest { + // The name of the job. + string name = 1; +} + +// DeleteJobResponse is the message response to delete the job by name. +message DeleteJobResponse { + // Empty +} From 0709a586f9a89a91bdeed382b2713b21bacd4e8d Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Tue, 3 Sep 2024 06:30:41 -0500 Subject: [PATCH 10/30] Removed deprecated methods from DaprClient and tests as well as unused types Signed-off-by: Whit Waldo --- examples/Actor/ActorClient/Program.cs | 6 +- examples/Actor/DemoActor/BankService.cs | 4 +- examples/Actor/DemoActor/DemoActor.cs | 16 +- examples/Actor/DemoActor/Program.cs | 8 +- examples/Actor/DemoActor/Startup.cs | 15 +- examples/Actor/IDemoActor/IBankActor.cs | 10 +- examples/Actor/IDemoActor/IDemoActor.cs | 11 +- .../ControllerSample/CustomTopicAttribute.cs | 2 + .../AspNetCore/ControllerSample/Startup.cs | 2 + .../Services/BankingService.cs | 2 +- .../AspNetCore/GrpcServiceSample/Startup.cs | 2 + examples/Client/ConfigurationApi/Startup.cs | 1 + examples/Client/Cryptography/Program.cs | 3 +- examples/Client/DistributedLock/Startup.cs | 3 +- .../Activities/ProcessPaymentActivity.cs | 1 - .../Activities/RequestApprovalActivity.cs | 1 - .../Activities/ReserveInventoryActivity.cs | 1 - .../Activities/UpdateInventoryActivity.cs | 1 - .../Workflow/WorkflowConsoleApp/Models.cs | 2 +- .../Workflow/WorkflowConsoleApp/Program.cs | 2 +- .../Workflows/OrderProcessingWorkflow.cs | 1 - .../WorkflowUnitTest/OrderProcessingTests.cs | 2 +- src/Dapr.Client/DaprClient.cs | 187 +----------- src/Dapr.Client/DaprClientGrpc.cs | 281 ------------------ src/Dapr.Client/GetWorkflowResponse.cs | 100 ------- src/Dapr.Client/WorkflowFailureDetails.cs | 35 --- .../WorkflowRuntimeStatus.cs | 2 +- test/Dapr.E2E.Test/Workflows/WorkflowTest.cs | 91 +----- 28 files changed, 53 insertions(+), 739 deletions(-) delete mode 100644 src/Dapr.Client/GetWorkflowResponse.cs delete mode 100644 src/Dapr.Client/WorkflowFailureDetails.cs rename src/{Dapr.Client => Dapr.Workflow}/WorkflowRuntimeStatus.cs (98%) diff --git a/examples/Actor/ActorClient/Program.cs b/examples/Actor/ActorClient/Program.cs index bae5d2ec2..f6ca26f53 100644 --- a/examples/Actor/ActorClient/Program.cs +++ b/examples/Actor/ActorClient/Program.cs @@ -11,6 +11,9 @@ // limitations under the License. // ------------------------------------------------------------------------ +using Dapr.Actors.Communication; +using IDemoActor; + namespace ActorClient { using System; @@ -18,7 +21,6 @@ namespace ActorClient using System.Threading.Tasks; using Dapr.Actors; using Dapr.Actors.Client; - using IDemoActorInterface; /// /// Actor Client class. @@ -43,7 +45,7 @@ public static async Task Main(string[] args) // Make strongly typed Actor calls with Remoting. // DemoActor is the type registered with Dapr runtime in the service. - var proxy = ActorProxy.Create(actorId, "DemoActor"); + var proxy = ActorProxy.Create(actorId, "DemoActor"); Console.WriteLine("Making call using actor proxy to save data."); await proxy.SaveData(data, TimeSpan.FromMinutes(10)); diff --git a/examples/Actor/DemoActor/BankService.cs b/examples/Actor/DemoActor/BankService.cs index 0a164183f..a24eadedb 100644 --- a/examples/Actor/DemoActor/BankService.cs +++ b/examples/Actor/DemoActor/BankService.cs @@ -11,9 +11,9 @@ // limitations under the License. // ------------------------------------------------------------------------ -using IDemoActorInterface; +using IDemoActor; -namespace DaprDemoActor +namespace DemoActor { public class BankService { diff --git a/examples/Actor/DemoActor/DemoActor.cs b/examples/Actor/DemoActor/DemoActor.cs index da780d517..b5ef53e93 100644 --- a/examples/Actor/DemoActor/DemoActor.cs +++ b/examples/Actor/DemoActor/DemoActor.cs @@ -11,14 +11,14 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace DaprDemoActor -{ - using System; - using System.Text.Json; - using System.Threading.Tasks; - using Dapr.Actors.Runtime; - using IDemoActorInterface; +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Dapr.Actors.Runtime; +using IDemoActor; +namespace DemoActor +{ // The following example showcases a few features of Actors // // Every actor should inherit from the Actor type, and must implement one or more actor interfaces. @@ -27,7 +27,7 @@ namespace DaprDemoActor // For Actors to use Reminders, it must derive from IRemindable. // If you don't intend to use Reminder feature, you can skip implementing IRemindable and reminder // specific methods which are shown in the code below. - public class DemoActor : Actor, IDemoActor, IBankActor, IRemindable + public class DemoActor : Actor, IDemoActor.IDemoActor, IBankActor, IRemindable { private const string StateName = "my_data"; diff --git a/examples/Actor/DemoActor/Program.cs b/examples/Actor/DemoActor/Program.cs index a56681fdb..1d538b471 100644 --- a/examples/Actor/DemoActor/Program.cs +++ b/examples/Actor/DemoActor/Program.cs @@ -11,11 +11,11 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace DaprDemoActor -{ - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Hosting; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +namespace DemoActor +{ public class Program { public static void Main(string[] args) diff --git a/examples/Actor/DemoActor/Startup.cs b/examples/Actor/DemoActor/Startup.cs index da2b9e764..881bc6a27 100644 --- a/examples/Actor/DemoActor/Startup.cs +++ b/examples/Actor/DemoActor/Startup.cs @@ -11,14 +11,15 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace DaprDemoActor -{ - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Hosting; +using Dapr.Actors.AspNetCore; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +namespace DemoActor +{ public class Startup { public Startup(IConfiguration configuration) diff --git a/examples/Actor/IDemoActor/IBankActor.cs b/examples/Actor/IDemoActor/IBankActor.cs index 95ac23844..c495f027b 100644 --- a/examples/Actor/IDemoActor/IBankActor.cs +++ b/examples/Actor/IDemoActor/IBankActor.cs @@ -11,12 +11,12 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace IDemoActorInterface -{ - using System; - using System.Threading.Tasks; - using Dapr.Actors; +using System; +using System.Threading.Tasks; +using Dapr.Actors; +namespace IDemoActor +{ public interface IBankActor : IActor { Task GetAccountBalance(); diff --git a/examples/Actor/IDemoActor/IDemoActor.cs b/examples/Actor/IDemoActor/IDemoActor.cs index 25ce09370..6f2d32801 100644 --- a/examples/Actor/IDemoActor/IDemoActor.cs +++ b/examples/Actor/IDemoActor/IDemoActor.cs @@ -11,13 +11,12 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace IDemoActorInterface -{ - using System; - using System.Threading.Tasks; - using Dapr.Actors; - using Dapr.Actors.Runtime; +using System; +using System.Threading.Tasks; +using Dapr.Actors; +namespace IDemoActor +{ /// /// Interface for Actor method. /// diff --git a/examples/AspNetCore/ControllerSample/CustomTopicAttribute.cs b/examples/AspNetCore/ControllerSample/CustomTopicAttribute.cs index 96eb918fb..5c9996aea 100644 --- a/examples/AspNetCore/ControllerSample/CustomTopicAttribute.cs +++ b/examples/AspNetCore/ControllerSample/CustomTopicAttribute.cs @@ -11,6 +11,8 @@ // limitations under the License. // ------------------------------------------------------------------------ +using Dapr.AspNetCore; + namespace ControllerSample { using System; diff --git a/examples/AspNetCore/ControllerSample/Startup.cs b/examples/AspNetCore/ControllerSample/Startup.cs index 11b81d8b3..64cfba512 100644 --- a/examples/AspNetCore/ControllerSample/Startup.cs +++ b/examples/AspNetCore/ControllerSample/Startup.cs @@ -11,6 +11,8 @@ // limitations under the License. // ------------------------------------------------------------------------ +using Dapr.AspNetCore; + namespace ControllerSample { using Microsoft.AspNetCore.Builder; diff --git a/examples/AspNetCore/GrpcServiceSample/Services/BankingService.cs b/examples/AspNetCore/GrpcServiceSample/Services/BankingService.cs index 56b80cad6..9518fd610 100644 --- a/examples/AspNetCore/GrpcServiceSample/Services/BankingService.cs +++ b/examples/AspNetCore/GrpcServiceSample/Services/BankingService.cs @@ -22,7 +22,7 @@ using GrpcServiceSample.Generated; using Microsoft.Extensions.Logging; -namespace GrpcServiceSample +namespace GrpcServiceSample.Services { /// /// BankAccount gRPC service diff --git a/examples/AspNetCore/GrpcServiceSample/Startup.cs b/examples/AspNetCore/GrpcServiceSample/Startup.cs index 752d62448..4aa5ac7d3 100644 --- a/examples/AspNetCore/GrpcServiceSample/Startup.cs +++ b/examples/AspNetCore/GrpcServiceSample/Startup.cs @@ -11,6 +11,8 @@ // limitations under the License. // ------------------------------------------------------------------------ +using Dapr.AspNetCore; +using GrpcServiceSample.Services; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; diff --git a/examples/Client/ConfigurationApi/Startup.cs b/examples/Client/ConfigurationApi/Startup.cs index 62a77ac49..db5b921c9 100644 --- a/examples/Client/ConfigurationApi/Startup.cs +++ b/examples/Client/ConfigurationApi/Startup.cs @@ -1,4 +1,5 @@ using System; +using Dapr.AspNetCore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; diff --git a/examples/Client/Cryptography/Program.cs b/examples/Client/Cryptography/Program.cs index 74e3c7f48..da81bef8f 100644 --- a/examples/Client/Cryptography/Program.cs +++ b/examples/Client/Cryptography/Program.cs @@ -11,10 +11,9 @@ // limitations under the License. // ------------------------------------------------------------------------ -using Cryptography; using Cryptography.Examples; -namespace Samples.Client +namespace Cryptography { class Program { diff --git a/examples/Client/DistributedLock/Startup.cs b/examples/Client/DistributedLock/Startup.cs index 0309af0f5..9f40e4752 100644 --- a/examples/Client/DistributedLock/Startup.cs +++ b/examples/Client/DistributedLock/Startup.cs @@ -1,4 +1,5 @@ -using DistributedLock.Services; +using Dapr.AspNetCore; +using DistributedLock.Services; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; diff --git a/examples/Workflow/WorkflowConsoleApp/Activities/ProcessPaymentActivity.cs b/examples/Workflow/WorkflowConsoleApp/Activities/ProcessPaymentActivity.cs index dc4cc531b..1ddb51bbf 100644 --- a/examples/Workflow/WorkflowConsoleApp/Activities/ProcessPaymentActivity.cs +++ b/examples/Workflow/WorkflowConsoleApp/Activities/ProcessPaymentActivity.cs @@ -1,7 +1,6 @@ using Dapr.Client; using Dapr.Workflow; using Microsoft.Extensions.Logging; -using WorkflowConsoleApp.Models; namespace WorkflowConsoleApp.Activities { diff --git a/examples/Workflow/WorkflowConsoleApp/Activities/RequestApprovalActivity.cs b/examples/Workflow/WorkflowConsoleApp/Activities/RequestApprovalActivity.cs index af0b1fa13..d40078fc8 100644 --- a/examples/Workflow/WorkflowConsoleApp/Activities/RequestApprovalActivity.cs +++ b/examples/Workflow/WorkflowConsoleApp/Activities/RequestApprovalActivity.cs @@ -1,6 +1,5 @@ using Dapr.Workflow; using Microsoft.Extensions.Logging; -using WorkflowConsoleApp.Models; namespace WorkflowConsoleApp.Activities { diff --git a/examples/Workflow/WorkflowConsoleApp/Activities/ReserveInventoryActivity.cs b/examples/Workflow/WorkflowConsoleApp/Activities/ReserveInventoryActivity.cs index fc6c48921..cdae1c6ed 100644 --- a/examples/Workflow/WorkflowConsoleApp/Activities/ReserveInventoryActivity.cs +++ b/examples/Workflow/WorkflowConsoleApp/Activities/ReserveInventoryActivity.cs @@ -1,7 +1,6 @@ using Dapr.Client; using Dapr.Workflow; using Microsoft.Extensions.Logging; -using WorkflowConsoleApp.Models; namespace WorkflowConsoleApp.Activities { diff --git a/examples/Workflow/WorkflowConsoleApp/Activities/UpdateInventoryActivity.cs b/examples/Workflow/WorkflowConsoleApp/Activities/UpdateInventoryActivity.cs index 947dab6cb..c035aadde 100644 --- a/examples/Workflow/WorkflowConsoleApp/Activities/UpdateInventoryActivity.cs +++ b/examples/Workflow/WorkflowConsoleApp/Activities/UpdateInventoryActivity.cs @@ -1,6 +1,5 @@ using Dapr.Client; using Dapr.Workflow; -using WorkflowConsoleApp.Models; using Microsoft.Extensions.Logging; namespace WorkflowConsoleApp.Activities diff --git a/examples/Workflow/WorkflowConsoleApp/Models.cs b/examples/Workflow/WorkflowConsoleApp/Models.cs index 6c9583d84..7892c7525 100644 --- a/examples/Workflow/WorkflowConsoleApp/Models.cs +++ b/examples/Workflow/WorkflowConsoleApp/Models.cs @@ -1,4 +1,4 @@ -namespace WorkflowConsoleApp.Models +namespace WorkflowConsoleApp { public record OrderPayload(string Name, double TotalCost, int Quantity = 1); public record InventoryRequest(string RequestId, string ItemName, int Quantity); diff --git a/examples/Workflow/WorkflowConsoleApp/Program.cs b/examples/Workflow/WorkflowConsoleApp/Program.cs index 2b8213887..26d34615d 100644 --- a/examples/Workflow/WorkflowConsoleApp/Program.cs +++ b/examples/Workflow/WorkflowConsoleApp/Program.cs @@ -1,10 +1,10 @@ using Dapr.Client; using Dapr.Workflow; using WorkflowConsoleApp.Activities; -using WorkflowConsoleApp.Models; using WorkflowConsoleApp.Workflows; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.DependencyInjection; +using WorkflowConsoleApp; const string StoreName = "statestore"; diff --git a/examples/Workflow/WorkflowConsoleApp/Workflows/OrderProcessingWorkflow.cs b/examples/Workflow/WorkflowConsoleApp/Workflows/OrderProcessingWorkflow.cs index bd2a710b6..3b8af5951 100644 --- a/examples/Workflow/WorkflowConsoleApp/Workflows/OrderProcessingWorkflow.cs +++ b/examples/Workflow/WorkflowConsoleApp/Workflows/OrderProcessingWorkflow.cs @@ -1,6 +1,5 @@ using Dapr.Workflow; using WorkflowConsoleApp.Activities; -using WorkflowConsoleApp.Models; namespace WorkflowConsoleApp.Workflows { diff --git a/examples/Workflow/WorkflowUnitTest/OrderProcessingTests.cs b/examples/Workflow/WorkflowUnitTest/OrderProcessingTests.cs index ac53c4081..e38a0c940 100644 --- a/examples/Workflow/WorkflowUnitTest/OrderProcessingTests.cs +++ b/examples/Workflow/WorkflowUnitTest/OrderProcessingTests.cs @@ -1,8 +1,8 @@ using System.Threading.Tasks; using Dapr.Workflow; using Moq; +using WorkflowConsoleApp; using WorkflowConsoleApp.Activities; -using WorkflowConsoleApp.Models; using WorkflowConsoleApp.Workflows; using Xunit; diff --git a/src/Dapr.Client/DaprClient.cs b/src/Dapr.Client/DaprClient.cs index 4f89d8668..94a43b759 100644 --- a/src/Dapr.Client/DaprClient.cs +++ b/src/Dapr.Client/DaprClient.cs @@ -1296,192 +1296,7 @@ public abstract Task Unlock( string resourceId, string lockOwner, CancellationToken cancellationToken = default); - - /// - /// Attempt to start the given workflow with response indicating success. - /// - /// The component to interface with. - /// Name of the workflow to run. - /// Identifier of the specific run. - /// The JSON-serializeable input for the given workflow. - /// The list of options that are potentially needed to start a workflow. - /// A that can be used to cancel the operation. - /// A containing a - [Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public abstract Task StartWorkflowAsync( - string workflowComponent, - string workflowName, - string instanceId = null, - object input = null, - IReadOnlyDictionary workflowOptions = default, - CancellationToken cancellationToken = default); - - /// - /// Waits for a workflow to start running and returns a object that contains metadata - /// about the started workflow. - /// - /// - /// - /// A "started" workflow instance is any instance not in the state. - /// - /// This method will return a completed task if the workflow has already started running or has already completed. - /// - /// - /// The unique ID of the workflow instance to wait for. - /// The component to interface with. - /// A that can be used to cancel the wait operation. - /// - /// Returns a record that describes the workflow instance and its execution status. - /// - /// - /// Thrown if is canceled before the workflow starts running. - /// - [Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public virtual async Task WaitForWorkflowStartAsync( - string instanceId, - string workflowComponent, - CancellationToken cancellationToken = default) - { - while (true) - { - var response = await this.GetWorkflowAsync(instanceId, workflowComponent, cancellationToken); - if (response.RuntimeStatus != WorkflowRuntimeStatus.Pending) - { - return response; - } - - await Task.Delay(TimeSpan.FromMilliseconds(500), cancellationToken); - } - } - - /// - /// Waits for a workflow to complete and returns a - /// object that contains metadata about the started instance. - /// - /// - /// - /// A "completed" workflow instance is any instance in one of the terminal states. For example, the - /// , , or - /// states. - /// - /// Workflows are long-running and could take hours, days, or months before completing. - /// Workflows can also be eternal, in which case they'll never complete unless terminated. - /// In such cases, this call may block indefinitely, so care must be taken to ensure appropriate timeouts are - /// enforced using the parameter. - /// - /// If a workflow instance is already complete when this method is called, the method will return immediately. - /// - /// - /// - /// Returns a record that describes the workflow instance and its execution status. - /// - /// - /// Thrown if is canceled before the workflow completes. - /// - [Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public virtual async Task WaitForWorkflowCompletionAsync( - string instanceId, - string workflowComponent, - CancellationToken cancellationToken = default) - { - while (true) - { - var response = await this.GetWorkflowAsync(instanceId, workflowComponent, cancellationToken); - if (response.RuntimeStatus == WorkflowRuntimeStatus.Completed || - response.RuntimeStatus == WorkflowRuntimeStatus.Failed || - response.RuntimeStatus == WorkflowRuntimeStatus.Terminated) - { - return response; - } - - await Task.Delay(TimeSpan.FromMilliseconds(500), cancellationToken); - } - } - - /// - /// Attempt to get information about the given workflow. - /// - /// The unique ID of the target workflow instance. - /// The component to interface with. - /// A that can be used to cancel the operation. - /// A containing a - [Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public abstract Task GetWorkflowAsync( - string instanceId, - string workflowComponent, - CancellationToken cancellationToken = default); - - /// - /// Attempt to get terminate the given workflow. - /// - /// The unique ID of the target workflow instance. - /// The component to interface with. - /// A that can be used to cancel the operation. - /// A that will complete when the terminate operation has been scheduled. If the wrapped value is true the operation suceeded. - [Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public abstract Task TerminateWorkflowAsync( - string instanceId, - string workflowComponent, - CancellationToken cancellationToken = default); - - /// - /// Attempt to raise an event the given workflow with response indicating success. - /// - /// Identifier of the specific run. - /// The component to interface with. - /// Name of the event to raise. - /// The JSON-serializable event payload to include in the raised event. - /// A that can be used to cancel the operation. - /// A that will complete when the raise event operation has been scheduled. If the wrapped value is true the operation suceeded. - [Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public abstract Task RaiseWorkflowEventAsync( - string instanceId, - string workflowComponent, - string eventName, - object eventData = null, - CancellationToken cancellationToken = default); - - - /// - /// Pauses the specified workflow instance. - /// - /// The unique ID of the target workflow instance. - /// The component to interface with. - /// A that can be used to cancel the operation. - /// A that will complete when the pause operation has been scheduled. If the wrapped value is true the operation suceeded. - [Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public abstract Task PauseWorkflowAsync( - string instanceId, - string workflowComponent, - CancellationToken cancellationToken = default); - - - /// - /// Resumes a paused workflow instance. - /// - /// The unique ID of the target workflow instance. - /// The component to interface with. - /// A that can be used to cancel the operation. - /// A that will complete when the resume operation has been scheduled. If the wrapped value is true the operation suceeded. - [Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public abstract Task ResumeWorkflowAsync( - string instanceId, - string workflowComponent, - CancellationToken cancellationToken = default); - - /// - /// Delete all state associated with the specified workflow instance. The workflow must be in a non-running state to be purged. - /// - /// The unique ID of the target workflow instance. - /// The component to interface with. - /// A that can be used to cancel the operation. - /// A that will complete when the purge operation has been scheduled. If the wrapped value is true the operation suceeded. - [Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] - public abstract Task PurgeWorkflowAsync( - string instanceId, - string workflowComponent, - CancellationToken cancellationToken = default); - + /// public void Dispose() { diff --git a/src/Dapr.Client/DaprClientGrpc.cs b/src/Dapr.Client/DaprClientGrpc.cs index af245afc3..73ab1ec67 100644 --- a/src/Dapr.Client/DaprClientGrpc.cs +++ b/src/Dapr.Client/DaprClientGrpc.cs @@ -2036,287 +2036,6 @@ public async override Task Unlock( #endregion - - #region Workflow API - /// - [Obsolete] - public async override Task StartWorkflowAsync( - string workflowComponent, - string workflowName, - string instanceId = null, - object input = null, - IReadOnlyDictionary workflowOptions = default, - CancellationToken cancellationToken = default) - { - ArgumentVerifier.ThrowIfNullOrEmpty(instanceId, nameof(instanceId)); - ArgumentVerifier.ThrowIfNullOrEmpty(workflowComponent, nameof(workflowComponent)); - ArgumentVerifier.ThrowIfNullOrEmpty(workflowName, nameof(workflowName)); - ArgumentVerifier.ThrowIfNull(input, nameof(input)); - - // Serialize json data. Converts input object to bytes and then bytestring inside the request. - byte[] jsonUtf8Bytes = null; - if (input is not null) - { - jsonUtf8Bytes = JsonSerializer.SerializeToUtf8Bytes(input); - } - - var request = new Autogenerated.StartWorkflowRequest() - { - InstanceId = instanceId, - WorkflowComponent = workflowComponent, - WorkflowName = workflowName, - Input = jsonUtf8Bytes is not null ? ByteString.CopyFrom(jsonUtf8Bytes) : null, - }; - - if (workflowOptions?.Count > 0) - { - foreach (var item in workflowOptions) - { - request.Options[item.Key] = item.Value; - } - } - - try - { - var options = CreateCallOptions(headers: null, cancellationToken); - var response = await client.StartWorkflowAlpha1Async(request, options); - return new StartWorkflowResponse(response.InstanceId); - - } - catch (RpcException ex) - { - throw new DaprException("Start Workflow operation failed: the Dapr endpoint indicated a failure. See InnerException for details.", ex); - } - } - - /// - [Obsolete] - public async override Task GetWorkflowAsync( - string instanceId, - string workflowComponent, - CancellationToken cancellationToken = default) - { - ArgumentVerifier.ThrowIfNullOrEmpty(instanceId, nameof(instanceId)); - ArgumentVerifier.ThrowIfNullOrEmpty(workflowComponent, nameof(workflowComponent)); - - var request = new Autogenerated.GetWorkflowRequest() - { - InstanceId = instanceId, - WorkflowComponent = workflowComponent - }; - - try - { - var options = CreateCallOptions(headers: null, cancellationToken); - var response = await client.GetWorkflowAlpha1Async(request, options); - if (response == null) - { - throw new DaprException("Get workflow operation failed: the Dapr endpoint returned an empty result."); - } - - response.CreatedAt ??= new Timestamp(); - response.LastUpdatedAt ??= response.CreatedAt; - - return new GetWorkflowResponse - { - InstanceId = response.InstanceId, - WorkflowName = response.WorkflowName, - WorkflowComponentName = workflowComponent, - CreatedAt = response.CreatedAt.ToDateTime(), - LastUpdatedAt = response.LastUpdatedAt.ToDateTime(), - RuntimeStatus = GetWorkflowRuntimeStatus(response.RuntimeStatus), - Properties = response.Properties, - FailureDetails = GetWorkflowFailureDetails(response, workflowComponent), - }; - } - catch (RpcException ex) - { - throw new DaprException("Get workflow operation failed: the Dapr endpoint indicated a failure. See InnerException for details.", ex); - } - } - - private static WorkflowRuntimeStatus GetWorkflowRuntimeStatus(string runtimeStatus) - { - if (!System.Enum.TryParse(runtimeStatus, true /* ignoreCase */, out WorkflowRuntimeStatus status)) - { - status = WorkflowRuntimeStatus.Unknown; - } - - return status; - } - - private static WorkflowFailureDetails GetWorkflowFailureDetails(Autogenerated.GetWorkflowResponse response, string componentName) - { - // FUTURE: Make this part of the protobuf contract instead of getting it from properties - // NOTE: The use of | instead of || is intentional. We want to get all the values. - if (response.Properties.TryGetValue($"{componentName}.workflow.failure.error_type", out string errorType) | - response.Properties.TryGetValue($"{componentName}.workflow.failure.error_message", out string errorMessage) | - response.Properties.TryGetValue($"{componentName}.workflow.failure.stack_trace", out string stackTrace)) - { - return new WorkflowFailureDetails(errorMessage, errorType, stackTrace); - } - - return null; - } - - /// - [Obsolete] - public async override Task TerminateWorkflowAsync( - string instanceId, - string workflowComponent, - CancellationToken cancellationToken = default) - { - ArgumentVerifier.ThrowIfNullOrEmpty(instanceId, nameof(instanceId)); - ArgumentVerifier.ThrowIfNullOrEmpty(workflowComponent, nameof(workflowComponent)); - - var request = new Autogenerated.TerminateWorkflowRequest() - { - InstanceId = instanceId, - WorkflowComponent = workflowComponent - }; - - var options = CreateCallOptions(headers: null, cancellationToken); - - try - { - await client.TerminateWorkflowAlpha1Async(request, options); - } - catch (RpcException ex) - { - throw new DaprException("Terminate workflow operation failed: the Dapr endpoint indicated a failure. See InnerException for details.", ex); - } - - } - - /// - [Obsolete] - public async override Task RaiseWorkflowEventAsync( - string instanceId, - string workflowComponent, - string eventName, - Object eventData, - CancellationToken cancellationToken = default) - { - ArgumentVerifier.ThrowIfNullOrEmpty(instanceId, nameof(instanceId)); - ArgumentVerifier.ThrowIfNullOrEmpty(workflowComponent, nameof(workflowComponent)); - ArgumentVerifier.ThrowIfNullOrEmpty(eventName, nameof(eventName)); - - byte[] jsonUtf8Bytes = new byte[0]; - // Serialize json data. Converts eventData object to bytes and then bytestring inside the request. - if (eventData != null) - { - jsonUtf8Bytes = JsonSerializer.SerializeToUtf8Bytes(eventData); - } - - var request = new Autogenerated.RaiseEventWorkflowRequest() - { - InstanceId = instanceId, - WorkflowComponent = workflowComponent, - EventName = eventName, - EventData = ByteString.CopyFrom(jsonUtf8Bytes), - }; - - var options = CreateCallOptions(headers: null, cancellationToken); - - try - { - await client.RaiseEventWorkflowAlpha1Async(request, options); - } - catch (RpcException ex) - { - throw new DaprException("Start Workflow operation failed: the Dapr endpoint indicated a failure. See InnerException for details.", ex); - } - } - - - /// - [Obsolete] - public async override Task PauseWorkflowAsync( - string instanceId, - string workflowComponent, - CancellationToken cancellationToken = default) - { - ArgumentVerifier.ThrowIfNullOrEmpty(instanceId, nameof(instanceId)); - ArgumentVerifier.ThrowIfNullOrEmpty(workflowComponent, nameof(workflowComponent)); - - var request = new Autogenerated.PauseWorkflowRequest() - { - InstanceId = instanceId, - WorkflowComponent = workflowComponent - }; - - var options = CreateCallOptions(headers: null, cancellationToken); - - try - { - await client.PauseWorkflowAlpha1Async(request, options); - } - catch (RpcException ex) - { - throw new DaprException("Pause workflow operation failed: the Dapr endpoint indicated a failure. See InnerException for details.", ex); - } - } - - /// - [Obsolete] - public async override Task ResumeWorkflowAsync( - string instanceId, - string workflowComponent, - CancellationToken cancellationToken = default) - { - ArgumentVerifier.ThrowIfNullOrEmpty(instanceId, nameof(instanceId)); - ArgumentVerifier.ThrowIfNullOrEmpty(workflowComponent, nameof(workflowComponent)); - - var request = new Autogenerated.ResumeWorkflowRequest() - { - InstanceId = instanceId, - WorkflowComponent = workflowComponent - }; - - var options = CreateCallOptions(headers: null, cancellationToken); - - try - { - await client.ResumeWorkflowAlpha1Async(request, options); - } - catch (RpcException ex) - { - throw new DaprException("Resume workflow operation failed: the Dapr endpoint indicated a failure. See InnerException for details.", ex); - } - - } - - /// - [Obsolete] - public async override Task PurgeWorkflowAsync( - string instanceId, - string workflowComponent, - CancellationToken cancellationToken = default) - { - ArgumentVerifier.ThrowIfNullOrEmpty(instanceId, nameof(instanceId)); - ArgumentVerifier.ThrowIfNullOrEmpty(workflowComponent, nameof(workflowComponent)); - - var request = new Autogenerated.PurgeWorkflowRequest() - { - InstanceId = instanceId, - WorkflowComponent = workflowComponent - }; - - var options = CreateCallOptions(headers: null, cancellationToken); - - try - { - await client.PurgeWorkflowAlpha1Async(request, options); - } - catch (RpcException ex) - { - throw new DaprException("Purge workflow operation failed: the Dapr endpoint indicated a failure. See InnerException for details.", ex); - } - - } - #endregion - - #region Dapr Sidecar Methods /// diff --git a/src/Dapr.Client/GetWorkflowResponse.cs b/src/Dapr.Client/GetWorkflowResponse.cs deleted file mode 100644 index 11fc253ac..000000000 --- a/src/Dapr.Client/GetWorkflowResponse.cs +++ /dev/null @@ -1,100 +0,0 @@ -// ------------------------------------------------------------------------ -// Copyright 2021 The Dapr 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 System; -using System.Collections.Generic; -using System.Text.Json; - -namespace Dapr.Client -{ - /// - /// The response type for the API. - /// - public class GetWorkflowResponse - { - /// - /// Gets the instance ID of the workflow. - /// - public string InstanceId { get; init; } - - /// - /// Gets the name of the workflow. - /// - public string WorkflowName { get; init; } - - /// - /// Gets the name of the workflow component. - /// - public string WorkflowComponentName { get; init; } - - /// - /// Gets the time at which the workflow was created. - /// - public DateTime CreatedAt { get; init; } - - /// - /// Gets the time at which the workflow was last updated. - /// - public DateTime LastUpdatedAt { get; init; } - - /// - /// Gets the runtime status of the workflow. - /// - public WorkflowRuntimeStatus RuntimeStatus { get; init; } - - /// - /// Gets the component-specific workflow properties. - /// - public IReadOnlyDictionary Properties { get; init; } - - /// - /// Gets the details associated with the workflow failure, if any. - /// - public WorkflowFailureDetails FailureDetails { get; init; } - - /// - /// Deserializes the workflow input into using . - /// - /// The type to deserialize the workflow input into. - /// Options to control the behavior during parsing. - /// Returns the input as , or returns a default value if the workflow doesn't have an input. - public T ReadInputAs(JsonSerializerOptions options = null) - { - // FUTURE: Make this part of the protobuf contract instead of properties - string defaultInputKey = $"{this.WorkflowComponentName}.workflow.input"; - if (!this.Properties.TryGetValue(defaultInputKey, out string serializedInput)) - { - return default; - } - - return JsonSerializer.Deserialize(serializedInput, options); - } - - /// - /// Deserializes the workflow output into using . - /// - /// The type to deserialize the workflow output into. - /// Options to control the behavior during parsing. - /// Returns the output as , or returns a default value if the workflow doesn't have an output. - public T ReadOutputAs(JsonSerializerOptions options = null) - { - // FUTURE: Make this part of the protobuf contract instead of properties - string defaultOutputKey = $"{this.WorkflowComponentName}.workflow.output"; - if (!this.Properties.TryGetValue(defaultOutputKey, out string serializedOutput)) - { - return default; - } - - return JsonSerializer.Deserialize(serializedOutput, options); - } - } -} diff --git a/src/Dapr.Client/WorkflowFailureDetails.cs b/src/Dapr.Client/WorkflowFailureDetails.cs deleted file mode 100644 index a61754ff1..000000000 --- a/src/Dapr.Client/WorkflowFailureDetails.cs +++ /dev/null @@ -1,35 +0,0 @@ -// ------------------------------------------------------------------------ -// Copyright 2021 The Dapr 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. -// ------------------------------------------------------------------------ - -namespace Dapr.Client -{ - /// - /// Represents workflow failure details. - /// - /// A summary description of the failure, which is typically an exception message. - /// The error type, which is defined by the workflow component implementation. - /// The stack trace of the failure. - public record WorkflowFailureDetails( - string ErrorMessage, - string ErrorType, - string StackTrace = null) - { - /// - /// Creates a user-friendly string representation of the failure information. - /// - public override string ToString() - { - return $"{this.ErrorType}: {this.ErrorMessage}"; - } - } -} diff --git a/src/Dapr.Client/WorkflowRuntimeStatus.cs b/src/Dapr.Workflow/WorkflowRuntimeStatus.cs similarity index 98% rename from src/Dapr.Client/WorkflowRuntimeStatus.cs rename to src/Dapr.Workflow/WorkflowRuntimeStatus.cs index dc652630e..24024cd63 100644 --- a/src/Dapr.Client/WorkflowRuntimeStatus.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeStatus.cs @@ -11,7 +11,7 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Client +namespace Dapr.Workflow { /// /// Enum describing the runtime status of a workflow. diff --git a/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs b/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs index d95929ca3..cfcbc4bd8 100644 --- a/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs +++ b/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs @@ -19,7 +19,7 @@ using FluentAssertions; using Xunit; using System.Linq; -using System.Diagnostics; +using Dapr.Workflow; namespace Dapr.E2E.Test { @@ -79,94 +79,5 @@ public async Task TestWorkflowLogging() Assert.True(false, "The logs were not able to found within the timeout"); } } - [Fact] - public async Task TestWorkflows() - { - var instanceId = "testInstanceId"; - var instanceId2 = "EventRaiseId"; - var workflowComponent = "dapr"; - var workflowName = "PlaceOrder"; - object input = "paperclips"; - Dictionary workflowOptions = new Dictionary(); - workflowOptions.Add("task_queue", "testQueue"); - - using var daprClient = new DaprClientBuilder().UseGrpcEndpoint(this.GrpcEndpoint).UseHttpEndpoint(this.HttpEndpoint).Build(); - var health = await daprClient.CheckHealthAsync(); - health.Should().Be(true, "DaprClient is not healthy"); - - // START WORKFLOW TEST - var startResponse = await daprClient.StartWorkflowAsync( - instanceId: instanceId, - workflowComponent: workflowComponent, - workflowName: workflowName, - input: input, - workflowOptions: workflowOptions); - - startResponse.InstanceId.Should().Be("testInstanceId", $"Instance ID {startResponse.InstanceId} was not correct"); - - // GET INFO TEST - var getResponse = await daprClient.GetWorkflowAsync(instanceId, workflowComponent); - getResponse.InstanceId.Should().Be("testInstanceId"); - getResponse.RuntimeStatus.Should().Be(WorkflowRuntimeStatus.Running, $"Instance ID {getResponse.RuntimeStatus} was not correct"); - - // PAUSE TEST: - await daprClient.PauseWorkflowAsync(instanceId, workflowComponent); - getResponse = await daprClient.GetWorkflowAsync(instanceId, workflowComponent); - getResponse.RuntimeStatus.Should().Be(WorkflowRuntimeStatus.Suspended, $"Instance ID {getResponse.RuntimeStatus} was not correct"); - - // RESUME TEST: - await daprClient.ResumeWorkflowAsync(instanceId, workflowComponent); - getResponse = await daprClient.GetWorkflowAsync(instanceId, workflowComponent); - getResponse.RuntimeStatus.Should().Be(WorkflowRuntimeStatus.Running, $"Instance ID {getResponse.RuntimeStatus} was not correct"); - - // RAISE EVENT TEST - await daprClient.RaiseWorkflowEventAsync(instanceId, workflowComponent, "ChangePurchaseItem", "computers"); - getResponse = await daprClient.GetWorkflowAsync(instanceId, workflowComponent); - - // TERMINATE TEST: - await daprClient.TerminateWorkflowAsync(instanceId, workflowComponent); - getResponse = await daprClient.GetWorkflowAsync(instanceId, workflowComponent); - getResponse.RuntimeStatus.Should().Be(WorkflowRuntimeStatus.Terminated, $"Instance ID {getResponse.RuntimeStatus} was not correct"); - - // PURGE TEST - await daprClient.PurgeWorkflowAsync(instanceId, workflowComponent); - - try - { - getResponse = await daprClient.GetWorkflowAsync(instanceId, workflowComponent); - Assert.True(false, "The GetWorkflowAsync call should have failed since the instance was purged"); - } - catch (DaprException ex) - { - ex.InnerException.Message.Should().Contain("no such instance exists", $"Instance {instanceId} was not correctly purged"); - } - - // Start another workflow for event raising purposes - startResponse = await daprClient.StartWorkflowAsync( - instanceId: instanceId2, - workflowComponent: workflowComponent, - workflowName: workflowName, - input: input, - workflowOptions: workflowOptions); - - // PARALLEL RAISE EVENT TEST - var event1 = daprClient.RaiseWorkflowEventAsync(instanceId2, workflowComponent, "ChangePurchaseItem", "computers"); - var event2 = daprClient.RaiseWorkflowEventAsync(instanceId2, workflowComponent, "ChangePurchaseItem", "computers"); - var event3 = daprClient.RaiseWorkflowEventAsync(instanceId2, workflowComponent, "ChangePurchaseItem", "computers"); - var event4 = daprClient.RaiseWorkflowEventAsync(instanceId2, workflowComponent, "ChangePurchaseItem", "computers"); - var event5 = daprClient.RaiseWorkflowEventAsync(instanceId2, workflowComponent, "ChangePurchaseItem", "computers"); - - var externalEvents = Task.WhenAll(event1, event2, event3, event4, event5); - var winner = await Task.WhenAny(externalEvents, Task.Delay(TimeSpan.FromSeconds(30))); - externalEvents.IsCompletedSuccessfully.Should().BeTrue($"Unsuccessful at raising events. Status of events: {externalEvents.IsCompletedSuccessfully}"); - - // Wait up to 30 seconds for the workflow to complete and check the output - using var cts = new CancellationTokenSource(delay: TimeSpan.FromSeconds(30)); - getResponse = await daprClient.WaitForWorkflowCompletionAsync(instanceId2, workflowComponent, cts.Token); - var outputString = getResponse.Properties["dapr.workflow.output"]; - outputString.Should().Be("\"computers\"", $"Purchased item {outputString} was not correct"); - var deserializedOutput = getResponse.ReadOutputAs(); - deserializedOutput.Should().Be("computers", $"Deserialized output '{deserializedOutput}' was not expected"); - } } } From 366a3b390e2e52387c1e0e828078663ee6ae63a7 Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Tue, 3 Sep 2024 18:45:36 -0500 Subject: [PATCH 11/30] Removed unused (and invalid) reference Signed-off-by: Whit Waldo --- examples/Actor/DemoActor/Startup.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/Actor/DemoActor/Startup.cs b/examples/Actor/DemoActor/Startup.cs index 881bc6a27..f1165e3c7 100644 --- a/examples/Actor/DemoActor/Startup.cs +++ b/examples/Actor/DemoActor/Startup.cs @@ -11,7 +11,6 @@ // limitations under the License. // ------------------------------------------------------------------------ -using Dapr.Actors.AspNetCore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; From 167c5226a0796b38308bb3f6fd49e2716fae5daf Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Fri, 13 Sep 2024 00:38:41 -0500 Subject: [PATCH 12/30] Added unit test to prove out concern raised on Discord Signed-off-by: Whit Waldo --- test/Dapr.Client.Test/SecretApiTest.cs | 30 +++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/test/Dapr.Client.Test/SecretApiTest.cs b/test/Dapr.Client.Test/SecretApiTest.cs index c94c82844..3e21440e3 100644 --- a/test/Dapr.Client.Test/SecretApiTest.cs +++ b/test/Dapr.Client.Test/SecretApiTest.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2021 The Dapr 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 @@ // limitations under the License. // ------------------------------------------------------------------------ +using FluentAssertions.Equivalency; + namespace Dapr.Client.Test { using System; @@ -93,6 +95,32 @@ public async Task GetSecretAsync_ReturnSingleSecret() secretsResponse["redis_secret"].Should().Be("Guess_Redis"); } + [Fact] + public async Task GetSecretAsync_WithSlashesInName() + { + await using var client = TestClient.CreateForDaprClient(); + + var request = await client.CaptureGrpcRequestAsync(async DaprClient => + { + return await DaprClient.GetSecretAsync("testStore", "us-west-1/org/xpto/secretabc"); + }); + + request.Dismiss(); + + //Get Request and validate + var envelope = await request.GetRequestEnvelopeAsync(); + envelope.StoreName.Should().Be("testStore"); + envelope.Key.Should().Be("us-west-1/org/xpto/secretabc"); + + var secrets = new Dictionary { { "us-west-1/org/xpto/secretabc", "abc123" } }; + var secretsResponse = await SendResponseWithSecrets(secrets, request); + + //Get response and validate + secretsResponse.Count.Should().Be(1); + secretsResponse.ContainsKey("us-west-1/org/xpto/secretabc").Should().BeTrue(); + secretsResponse["us-west-1/org/xpto/secretabc"].Should().Be("abc123"); + } + [Fact] public async Task GetSecretAsync_ReturnMultipleSecrets() { From 3d1fa01d0f52298c880f8d92b08690aa317be270 Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Fri, 13 Sep 2024 11:45:07 -0500 Subject: [PATCH 13/30] Added missing workflow status branch (#1348) Signed-off-by: Whit Waldo --- src/Dapr.Workflow/WorkflowState.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Dapr.Workflow/WorkflowState.cs b/src/Dapr.Workflow/WorkflowState.cs index aefe4be6b..ea1ffae22 100644 --- a/src/Dapr.Workflow/WorkflowState.cs +++ b/src/Dapr.Workflow/WorkflowState.cs @@ -84,6 +84,8 @@ public WorkflowRuntimeStatus RuntimeStatus return WorkflowRuntimeStatus.Terminated; case OrchestrationRuntimeStatus.Pending: return WorkflowRuntimeStatus.Pending; + case OrchestrationRuntimeStatus.Suspended: + return WorkflowRuntimeStatus.Suspended; default: return WorkflowRuntimeStatus.Unknown; } From 70d092ea79461835848d7b8b04fe86cc3ac7bdd8 Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Thu, 19 Sep 2024 08:28:04 -0500 Subject: [PATCH 14/30] Removed unused using Signed-off-by: Whit Waldo --- test/Dapr.Client.Test/SecretApiTest.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/Dapr.Client.Test/SecretApiTest.cs b/test/Dapr.Client.Test/SecretApiTest.cs index 3e21440e3..26048e2a4 100644 --- a/test/Dapr.Client.Test/SecretApiTest.cs +++ b/test/Dapr.Client.Test/SecretApiTest.cs @@ -11,8 +11,6 @@ // limitations under the License. // ------------------------------------------------------------------------ -using FluentAssertions.Equivalency; - namespace Dapr.Client.Test { using System; From bc62fd5b634ef7c22522f5ba3399135609ea4065 Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Sun, 6 Oct 2024 02:29:53 -0500 Subject: [PATCH 15/30] Added fix to handle null return values Signed-off-by: Whit Waldo --- src/Dapr.Client/DaprClientGrpc.cs | 15 +++++++++++---- src/Dapr.Client/DaprMetadata.cs | 1 - 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Dapr.Client/DaprClientGrpc.cs b/src/Dapr.Client/DaprClientGrpc.cs index af245afc3..84d0a1117 100644 --- a/src/Dapr.Client/DaprClientGrpc.cs +++ b/src/Dapr.Client/DaprClientGrpc.cs @@ -2390,10 +2390,17 @@ public override async Task GetMetadataAsync(CancellationToken canc try { var response = await client.GetMetadataAsync(new Autogenerated.GetMetadataRequest(), options); - return new DaprMetadata(response.Id, - response.ActorRuntime.ActiveActors.Select(c => new DaprActorMetadata(c.Type, c.Count)).ToList(), - response.ExtendedMetadata.ToDictionary(c => c.Key, c => c.Value), - response.RegisteredComponents.Select(c => new DaprComponentsMetadata(c.Name, c.Type, c.Version, c.Capabilities.ToArray())).ToList()); + if (response is null) + return null; + + return new DaprMetadata(response.Id ?? "", + response.ActorRuntime?.ActiveActors?.Select(c => new DaprActorMetadata(c.Type, c.Count)).ToList() ?? + new List(), + response.ExtendedMetadata?.ToDictionary(c => c.Key, c => c.Value) ?? + new Dictionary(), + response.RegisteredComponents?.Select(c => + new DaprComponentsMetadata(c.Name, c.Type, c.Version, c.Capabilities.ToArray())).ToList() ?? + new List()); } catch (RpcException ex) { diff --git a/src/Dapr.Client/DaprMetadata.cs b/src/Dapr.Client/DaprMetadata.cs index a58707c99..4cd812e04 100644 --- a/src/Dapr.Client/DaprMetadata.cs +++ b/src/Dapr.Client/DaprMetadata.cs @@ -11,7 +11,6 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; using System.Collections.Generic; namespace Dapr.Client From 23f82fae5f4a819546ffb0567315bac3aa0d3b39 Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Mon, 7 Oct 2024 10:46:39 -0500 Subject: [PATCH 16/30] Added unit test to validate that headers aren't being stripped off request Signed-off-by: Whit Waldo --- .../DaprClientTest.InvokeMethodAsync.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs index 484f327d0..3359c3b48 100644 --- a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs +++ b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodAsync.cs @@ -11,6 +11,9 @@ // limitations under the License. // ------------------------------------------------------------------------ +using System.Linq; +using System.Net.Http.Headers; + namespace Dapr.Client.Test { using System; @@ -654,8 +657,34 @@ public async Task CreateInvokeMethodRequest_WithData_CreatesJsonContentWithQuery var actual = await content.ReadFromJsonAsync(this.jsonSerializerOptions); Assert.Equal(data.Color, actual.Color); } + + [Fact] + public async Task InvokeMethodWithoutResponse_WithExtraneousHeaders() + { + await using var client = TestClient.CreateForDaprClient(c => + { + c.UseGrpcEndpoint("http://localhost").UseHttpEndpoint("https://test-endpoint:3501").UseJsonSerializationOptions(this.jsonSerializerOptions); + }); + + var req = await client.CaptureHttpRequestAsync(async DaprClient => + { + var request = client.InnerClient.CreateInvokeMethodRequest(HttpMethod.Get, "test-app", "mymethod"); + request.Headers.Add("test-api-key", "test"); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "abc123"); + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + await DaprClient.InvokeMethodAsync(request); + }); + req.Dismiss(); + + Assert.NotNull(req); + Assert.True(req.Request.Headers.Contains("test-api-key")); + Assert.Equal("test", req.Request.Headers.GetValues("test-api-key").First()); + Assert.True(req.Request.Headers.Contains("Authorization")); + Assert.Equal("Bearer abc123", req.Request.Headers.GetValues("Authorization").First()); + Assert.Equal("application/json", req.Request.Headers.GetValues("Accept").First()); + } [Fact] public async Task InvokeMethodWithResponseAsync_ReturnsMessageWithoutCheckingStatus() From 156ed567cebe31f4a636a3d18113265de9ea02aa Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Mon, 7 Oct 2024 10:47:24 -0500 Subject: [PATCH 17/30] Fixed spelling typo Signed-off-by: Whit Waldo --- test/Shared/TestClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Shared/TestClient.cs b/test/Shared/TestClient.cs index 350c4c6e6..c84fe4c9e 100644 --- a/test/Shared/TestClient.cs +++ b/test/Shared/TestClient.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2021 The Dapr Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -394,7 +394,7 @@ public async Task> CaptureGrpcRequestAsync(Func Date: Fri, 11 Oct 2024 01:47:37 -0500 Subject: [PATCH 18/30] Removed unnecessary null check Signed-off-by: Whit Waldo --- src/Dapr.Client/DaprClientGrpc.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Dapr.Client/DaprClientGrpc.cs b/src/Dapr.Client/DaprClientGrpc.cs index 84d0a1117..a5f5833a2 100644 --- a/src/Dapr.Client/DaprClientGrpc.cs +++ b/src/Dapr.Client/DaprClientGrpc.cs @@ -2390,9 +2390,6 @@ public override async Task GetMetadataAsync(CancellationToken canc try { var response = await client.GetMetadataAsync(new Autogenerated.GetMetadataRequest(), options); - if (response is null) - return null; - return new DaprMetadata(response.Id ?? "", response.ActorRuntime?.ActiveActors?.Select(c => new DaprActorMetadata(c.Type, c.Count)).ToList() ?? new List(), From 920d7ad80cf0b9b3d5cd05a915d08261a17c7fc3 Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Fri, 11 Oct 2024 02:13:06 -0500 Subject: [PATCH 19/30] Removed E2E workflow test as it validated DaprClient and the functionality has been moved out to the Dapr.Workflow project instead. Signed-off-by: Whit Waldo --- test/Dapr.E2E.Test/Workflows/WorkflowTest.cs | 83 -------------------- 1 file changed, 83 deletions(-) delete mode 100644 test/Dapr.E2E.Test/Workflows/WorkflowTest.cs diff --git a/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs b/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs deleted file mode 100644 index cfcbc4bd8..000000000 --- a/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs +++ /dev/null @@ -1,83 +0,0 @@ -// ------------------------------------------------------------------------ -// Copyright 2022 The Dapr 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 System; -using System.IO; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Dapr.Client; -using FluentAssertions; -using Xunit; -using System.Linq; -using Dapr.Workflow; - -namespace Dapr.E2E.Test -{ - [Obsolete] - public partial class E2ETests - { - [Fact] - public async Task TestWorkflowLogging() - { - // This test starts the daprclient and searches through the logfile to ensure the - // workflow logger is correctly logging the registered workflow(s) and activity(s) - - Dictionary logStrings = new Dictionary(); - logStrings["PlaceOrder"] = false; - logStrings["ShipProduct"] = false; - var logFilePath = "../../../../../test/Dapr.E2E.Test.App/log.txt"; - var allLogsFound = false; - var timeout = 30; // 30s - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeout)); - using var daprClient = new DaprClientBuilder().UseGrpcEndpoint(this.GrpcEndpoint).UseHttpEndpoint(this.HttpEndpoint).Build(); - var health = await daprClient.CheckHealthAsync(); - health.Should().Be(true, "DaprClient is not healthy"); - - var searchTask = Task.Run(async() => - { - using (StreamReader reader = new StreamReader(logFilePath)) - { - string line; - while ((line = await reader.ReadLineAsync().WaitAsync(cts.Token)) != null) - { - foreach (var entry in logStrings) - { - if (line.Contains(entry.Key)) - { - logStrings[entry.Key] = true; - } - } - allLogsFound = logStrings.All(k => k.Value); - if (allLogsFound) - { - break; - } - } - } - }, cts.Token); - - try - { - await searchTask; - } - finally - { - File.Delete(logFilePath); - } - if (!allLogsFound) - { - Assert.True(false, "The logs were not able to found within the timeout"); - } - } - } -} From 2450ced25fb3a6e9e9e8297287b481bdbbc07abd Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Mon, 14 Oct 2024 18:18:36 -0500 Subject: [PATCH 20/30] Adding instance-based CreateInvokableHttpClient (#1319) This PR takes the implementation of the static method and puts it into the DaprClient instance, pulling from the existing apiTokenHeader on the instance to populate the daprApiToken, pulling the endpoint from the instance's httpEndpoint value and accepting only an appId argument so as to specify the ID of the Dapr app to connect to and place in the resulting URI. --------- Signed-off-by: Whit Waldo --- src/Dapr.Client/DaprClient.cs | 26 +++++++++- src/Dapr.Client/DaprClientGrpc.cs | 25 +++++++++ ...lientTest.CreateInvokableHttpClientTest.cs | 52 +++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 test/Dapr.Client.Test/DaprClientTest.CreateInvokableHttpClientTest.cs diff --git a/src/Dapr.Client/DaprClient.cs b/src/Dapr.Client/DaprClient.cs index 94a43b759..9f107578f 100644 --- a/src/Dapr.Client/DaprClient.cs +++ b/src/Dapr.Client/DaprClient.cs @@ -58,7 +58,7 @@ public abstract class DaprClient : IDisposable /// The client will read the property, and /// interpret the hostname as the destination app-id. The /// property will be replaced with a new URI with the authority section replaced by - /// and the path portion of the URI rewitten to follow the format of a Dapr service invocation request. + /// and the path portion of the URI rewritten to follow the format of a Dapr service invocation request. /// /// /// @@ -448,6 +448,30 @@ public HttpRequestMessage CreateInvokeMethodRequest(string appId, stri /// A that will return the value when the operation has completed. public abstract Task InvokeMethodWithResponseAsync(HttpRequestMessage request, CancellationToken cancellationToken = default); +#nullable enable + /// + /// + /// Creates an that can be used to perform Dapr service invocation using + /// objects. + /// + /// + /// The client will read the property, and + /// interpret the hostname as the destination app-id. The + /// property will be replaced with a new URI with the authority section replaced by the HTTP endpoint value + /// and the path portion of the URI rewritten to follow the format of a Dapr service invocation request. + /// + /// + /// + /// An optional app-id. If specified, the app-id will be configured as the value of + /// so that relative URIs can be used. It is mandatory to set this parameter if your app-id contains at least one upper letter. + /// If some requests use absolute URL with an app-id which contains at least one upper letter, it will not work, the workaround is to create one HttpClient for each app-id with the app-ip parameter set. + /// + /// An that can be used to perform service invocation requests. + /// + /// + public abstract HttpClient CreateInvokableHttpClient(string? appId = null); +#nullable disable + /// /// Perform service invocation using the request provided by . If the response has a non-success /// status an exception will be thrown. diff --git a/src/Dapr.Client/DaprClientGrpc.cs b/src/Dapr.Client/DaprClientGrpc.cs index c0c0015e0..c70aef77b 100644 --- a/src/Dapr.Client/DaprClientGrpc.cs +++ b/src/Dapr.Client/DaprClientGrpc.cs @@ -450,6 +450,31 @@ public override async Task InvokeMethodWithResponseAsync(Ht } } + /// + /// + /// Creates an that can be used to perform Dapr service invocation using + /// objects. + /// + /// + /// The client will read the property, and + /// interpret the hostname as the destination app-id. The + /// property will be replaced with a new URI with the authority section replaced by the instance's value + /// and the path portion of the URI rewritten to follow the format of a Dapr service invocation request. + /// + /// + /// + /// An optional app-id. If specified, the app-id will be configured as the value of + /// so that relative URIs can be used. It is mandatory to set this parameter if your app-id contains at least one upper letter. + /// If some requests use absolute URL with an app-id which contains at least one upper letter, it will not work, the workaround is to create one HttpClient for each app-id with the app-ip parameter set. + /// + /// An that can be used to perform service invocation requests. + /// + /// +#nullable enable + public override HttpClient CreateInvokableHttpClient(string? appId = null) => + DaprClient.CreateInvokeHttpClient(appId, this.httpEndpoint?.AbsoluteUri, this.apiTokenHeader?.Value); + #nullable disable + public async override Task InvokeMethodAsync(HttpRequestMessage request, CancellationToken cancellationToken = default) { ArgumentVerifier.ThrowIfNull(request, nameof(request)); diff --git a/test/Dapr.Client.Test/DaprClientTest.CreateInvokableHttpClientTest.cs b/test/Dapr.Client.Test/DaprClientTest.CreateInvokableHttpClientTest.cs new file mode 100644 index 000000000..99fbd4972 --- /dev/null +++ b/test/Dapr.Client.Test/DaprClientTest.CreateInvokableHttpClientTest.cs @@ -0,0 +1,52 @@ +// ------------------------------------------------------------------------ +// Copyright 2024 The Dapr 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 System; +using Xunit; + +namespace Dapr.Client +{ + public partial class DaprClientTest + { + [Fact] + public void CreateInvokableHttpClient_WithAppId_FromDaprClient() + { + var daprClient = new MockClient().DaprClient; + var client = daprClient.CreateInvokableHttpClient(appId: "bank"); + Assert.Equal("http://bank/", client.BaseAddress.AbsoluteUri); + } + + [Fact] + public void CreateInvokableHttpClient_InvalidAppId_FromDaprClient() + { + var daprClient = new MockClient().DaprClient; + var ex = Assert.Throws(() => + { + // The appId needs to be something that can be used as hostname in a URI. + _ = daprClient.CreateInvokableHttpClient(appId: ""); + }); + + Assert.Contains("The appId must be a valid hostname.", ex.Message); + Assert.IsType(ex.InnerException); + } + + [Fact] + public void CreateInvokableHttpClient_WithoutAppId_FromDaprClient() + { + var daprClient = new MockClient().DaprClient; + + var client = daprClient.CreateInvokableHttpClient(); + Assert.Null(client.BaseAddress); + } + } +} From 0a978458bbf71e25c22177a69a5ac9abe163ae8b Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Tue, 15 Oct 2024 18:09:53 -0500 Subject: [PATCH 21/30] Fixed security advisory updates across dependencies (transitive and direct) (#1366) Migrating whole solution to Central Package Management - several package version upgrades to address security advisories and otherwise. --------- Signed-off-by: Whit Waldo --- Directory.Packages.props | 45 +++++++++++++++++++ .../GrpcServiceSample.csproj | 10 ++--- .../BulkPublishEventExample.csproj | 6 +-- .../PublishEventExample.csproj | 6 +-- .../ServiceInvocation.csproj | 6 +-- .../StateManagement/StateManagement.csproj | 6 +-- .../WorkflowUnitTest/WorkflowUnitTest.csproj | 10 ++--- properties/dapr_managed_netcore.props | 2 +- .../Dapr.Actors.Generators.csproj | 4 +- src/Dapr.Actors/Dapr.Actors.csproj | 2 +- src/Dapr.Client/Dapr.Client.csproj | 8 ++-- .../Dapr.Extensions.Configuration.csproj | 2 +- src/Dapr.Workflow/Dapr.Workflow.csproj | 4 +- src/Directory.Build.props | 2 +- ...r.Actors.AspNetCore.IntegrationTest.csproj | 12 ++--- .../Dapr.Actors.AspNetCore.Test.csproj | 14 +++--- .../CSharpSourceGeneratorVerifier.cs | 4 +- .../Dapr.Actors.Generators.Test.csproj | 16 +++---- test/Dapr.Actors.Test/Dapr.Actors.Test.csproj | 14 +++--- .../Dapr.AspNetCore.IntegrationTest.csproj | 35 +++++++++------ .../Dapr.AspNetCore.Test.csproj | 10 ++--- test/Dapr.Client.Test/Dapr.Client.Test.csproj | 24 +++++----- .../DaprClientTest.InvokeMethodGrpcAsync.cs | 2 +- test/Dapr.Client.Test/StateApiTest.cs | 2 +- .../Dapr.E2E.Test.Actors.Generators.csproj | 10 ++--- .../Dapr.E2E.Test.App.Grpc.csproj | 2 +- .../Dapr.E2E.Test.App.csproj | 10 ++--- test/Dapr.E2E.Test/Dapr.E2E.Test.csproj | 14 +++--- .../Dapr.Extensions.Configuration.Test.csproj | 14 +++--- test/Directory.Build.props | 6 +-- 30 files changed, 178 insertions(+), 124 deletions(-) create mode 100644 Directory.Packages.props diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 000000000..990b5aeb1 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,45 @@ + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/AspNetCore/GrpcServiceSample/GrpcServiceSample.csproj b/examples/AspNetCore/GrpcServiceSample/GrpcServiceSample.csproj index 123763489..6084df013 100644 --- a/examples/AspNetCore/GrpcServiceSample/GrpcServiceSample.csproj +++ b/examples/AspNetCore/GrpcServiceSample/GrpcServiceSample.csproj @@ -10,11 +10,11 @@ - - - - - + + + + + diff --git a/examples/Client/PublishSubscribe/BulkPublishEventExample/BulkPublishEventExample.csproj b/examples/Client/PublishSubscribe/BulkPublishEventExample/BulkPublishEventExample.csproj index 3f22acaf8..b1e7647c7 100644 --- a/examples/Client/PublishSubscribe/BulkPublishEventExample/BulkPublishEventExample.csproj +++ b/examples/Client/PublishSubscribe/BulkPublishEventExample/BulkPublishEventExample.csproj @@ -16,9 +16,9 @@ - - - + + + diff --git a/examples/Client/PublishSubscribe/PublishEventExample/PublishEventExample.csproj b/examples/Client/PublishSubscribe/PublishEventExample/PublishEventExample.csproj index 2df4ec967..52b77a3e5 100644 --- a/examples/Client/PublishSubscribe/PublishEventExample/PublishEventExample.csproj +++ b/examples/Client/PublishSubscribe/PublishEventExample/PublishEventExample.csproj @@ -16,9 +16,9 @@ - - - + + + diff --git a/examples/Client/ServiceInvocation/ServiceInvocation.csproj b/examples/Client/ServiceInvocation/ServiceInvocation.csproj index e3df962a1..7b165835e 100644 --- a/examples/Client/ServiceInvocation/ServiceInvocation.csproj +++ b/examples/Client/ServiceInvocation/ServiceInvocation.csproj @@ -16,9 +16,9 @@ - - - + + + diff --git a/examples/Client/StateManagement/StateManagement.csproj b/examples/Client/StateManagement/StateManagement.csproj index e3df962a1..7b165835e 100644 --- a/examples/Client/StateManagement/StateManagement.csproj +++ b/examples/Client/StateManagement/StateManagement.csproj @@ -16,9 +16,9 @@ - - - + + + diff --git a/examples/Workflow/WorkflowUnitTest/WorkflowUnitTest.csproj b/examples/Workflow/WorkflowUnitTest/WorkflowUnitTest.csproj index 7163f4e0c..dec14a713 100644 --- a/examples/Workflow/WorkflowUnitTest/WorkflowUnitTest.csproj +++ b/examples/Workflow/WorkflowUnitTest/WorkflowUnitTest.csproj @@ -7,14 +7,14 @@ - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/properties/dapr_managed_netcore.props b/properties/dapr_managed_netcore.props index 3bafcb50c..6e8c01bfe 100644 --- a/properties/dapr_managed_netcore.props +++ b/properties/dapr_managed_netcore.props @@ -53,7 +53,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj b/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj index a69f2d1a0..370d422f1 100644 --- a/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj +++ b/src/Dapr.Actors.Generators/Dapr.Actors.Generators.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/test/Dapr.Actors.AspNetCore.IntegrationTest/Dapr.Actors.AspNetCore.IntegrationTest.csproj b/test/Dapr.Actors.AspNetCore.IntegrationTest/Dapr.Actors.AspNetCore.IntegrationTest.csproj index c44d19f61..921e2dda4 100644 --- a/test/Dapr.Actors.AspNetCore.IntegrationTest/Dapr.Actors.AspNetCore.IntegrationTest.csproj +++ b/test/Dapr.Actors.AspNetCore.IntegrationTest/Dapr.Actors.AspNetCore.IntegrationTest.csproj @@ -1,14 +1,14 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Dapr.Actors.AspNetCore.Test/Dapr.Actors.AspNetCore.Test.csproj b/test/Dapr.Actors.AspNetCore.Test/Dapr.Actors.AspNetCore.Test.csproj index 9a8b55c2f..c448e915c 100644 --- a/test/Dapr.Actors.AspNetCore.Test/Dapr.Actors.AspNetCore.Test.csproj +++ b/test/Dapr.Actors.AspNetCore.Test/Dapr.Actors.AspNetCore.Test.csproj @@ -5,16 +5,16 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - - - + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Dapr.Actors.Generators.Test/CSharpSourceGeneratorVerifier.cs b/test/Dapr.Actors.Generators.Test/CSharpSourceGeneratorVerifier.cs index 435488c2c..2b1046e1a 100644 --- a/test/Dapr.Actors.Generators.Test/CSharpSourceGeneratorVerifier.cs +++ b/test/Dapr.Actors.Generators.Test/CSharpSourceGeneratorVerifier.cs @@ -24,7 +24,9 @@ internal static class CSharpSourceGeneratorVerifier where TSourceGenerator : ISourceGenerator, new() { +#pragma warning disable CS0618 // Type or member is obsolete public class Test : CSharpSourceGeneratorTest +#pragma warning restore CS0618 // Type or member is obsolete { public Test() { @@ -78,4 +80,4 @@ protected override ParseOptions CreateParseOptions() return ((CSharpParseOptions)base.CreateParseOptions()).WithLanguageVersion(LanguageVersion); } } -} \ No newline at end of file +} diff --git a/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj b/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj index 02aaf1bb3..91c7e8b42 100644 --- a/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj +++ b/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj @@ -13,17 +13,17 @@ - - - - - - - + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj b/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj index d87ea3cd3..9ef26cd13 100644 --- a/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj +++ b/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj @@ -5,16 +5,16 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - - - + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj b/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj index ed110191f..ace894a4f 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj +++ b/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj @@ -1,19 +1,26 @@  - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + NU1903 + NU1903 + false + direct + diff --git a/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj b/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj index 32416dd8a..a76288891 100644 --- a/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj +++ b/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj @@ -1,14 +1,14 @@  - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Dapr.Client.Test/Dapr.Client.Test.csproj b/test/Dapr.Client.Test/Dapr.Client.Test.csproj index 06322f4d1..9a8d91c79 100644 --- a/test/Dapr.Client.Test/Dapr.Client.Test.csproj +++ b/test/Dapr.Client.Test/Dapr.Client.Test.csproj @@ -1,21 +1,21 @@  - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - - - - - - - - + + + + + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs index 4001e4b06..65b9b1e7d 100644 --- a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs +++ b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs @@ -197,7 +197,7 @@ public void InvokeMethodGrpcAsync_CanInvokeMethodWithNoReturnTypeAndData() .Setup(m => m.InvokeServiceAsync(It.IsAny(), It.IsAny())) .Returns(response); - FluentActions.Awaiting(async () => await client.DaprClient.InvokeMethodGrpcAsync("test", "test", request)).Should().NotThrow(); + FluentActions.Awaiting(async () => await client.DaprClient.InvokeMethodGrpcAsync("test", "test", request)).Should().NotThrowAsync(); } [Fact] diff --git a/test/Dapr.Client.Test/StateApiTest.cs b/test/Dapr.Client.Test/StateApiTest.cs index 2595fb006..0684a8db0 100644 --- a/test/Dapr.Client.Test/StateApiTest.cs +++ b/test/Dapr.Client.Test/StateApiTest.cs @@ -505,7 +505,7 @@ public async Task ExecuteStateTransactionAsync_CanSaveState() req1.Request.Etag.Value.Should().Be("testEtag"); req1.Request.Metadata.Count.Should().Be(1); req1.Request.Metadata["a"].Should().Be("b"); - req1.Request.Options.Concurrency.Should().Be(2); + req1.Request.Options.Concurrency.Should().Be(StateConcurrency.ConcurrencyLastWrite); var req2 = envelope.Operations[1]; req2.Request.Key.Should().Be("stateKey2"); diff --git a/test/Dapr.E2E.Test.Actors.Generators/Dapr.E2E.Test.Actors.Generators.csproj b/test/Dapr.E2E.Test.Actors.Generators/Dapr.E2E.Test.Actors.Generators.csproj index 6ef9c009d..cb375af01 100644 --- a/test/Dapr.E2E.Test.Actors.Generators/Dapr.E2E.Test.Actors.Generators.csproj +++ b/test/Dapr.E2E.Test.Actors.Generators/Dapr.E2E.Test.Actors.Generators.csproj @@ -9,14 +9,14 @@ - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/Dapr.E2E.Test.App.Grpc/Dapr.E2E.Test.App.Grpc.csproj b/test/Dapr.E2E.Test.App.Grpc/Dapr.E2E.Test.App.Grpc.csproj index 849870b98..9505df276 100644 --- a/test/Dapr.E2E.Test.App.Grpc/Dapr.E2E.Test.App.Grpc.csproj +++ b/test/Dapr.E2E.Test.App.Grpc/Dapr.E2E.Test.App.Grpc.csproj @@ -1,6 +1,6 @@ - + diff --git a/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj b/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj index e6ad11456..3454ac25d 100644 --- a/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj +++ b/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj @@ -7,10 +7,10 @@ - - - - - + + + + + diff --git a/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj b/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj index be3027269..fc92396a6 100644 --- a/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj +++ b/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj @@ -1,12 +1,12 @@  - - - - - - - + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj b/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj index d259f2ab1..0c7bce286 100644 --- a/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj +++ b/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj @@ -1,15 +1,15 @@  - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -25,7 +25,7 @@ - + \ No newline at end of file diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 0ce23c19e..50b029a12 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -1,9 +1,9 @@ - + net6;net7;net8 - + $(RepoRoot)bin\$(Configuration)\test\$(MSBuildProjectName)\ @@ -12,6 +12,6 @@ - + \ No newline at end of file From 236567786e06afa7165a720e37f8717bad89357a Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Wed, 16 Oct 2024 00:21:52 -0500 Subject: [PATCH 22/30] Removes floating classes and introduces Dapr.Common project (#1365) Extracting classes out to common project --------- Signed-off-by: Whit Waldo --- Directory.Packages.props | 1 + all.sln | 7 ++++ .../ConfigurationApi/ConfigurationApi.csproj | 1 + .../DistributedLock/DistributedLock.csproj | 1 + src/Dapr.Actors/Dapr.Actors.csproj | 6 +-- src/Dapr.AspNetCore/Dapr.AspNetCore.csproj | 6 +-- src/Dapr.Client/Dapr.Client.csproj | 9 ++--- .../ArgumentVerifier.cs | 2 +- src/Dapr.Common/AssemblyInfo.cs | 40 +++++++++++++++++++ src/Dapr.Common/Dapr.Common.csproj | 14 +++++++ src/{Shared => Dapr.Common}/DaprDefaults.cs | 2 - .../DaprException.cs | 3 +- .../Dapr.Extensions.Configuration.csproj | 5 +-- src/Dapr.Workflow/Dapr.Workflow.csproj | 6 +-- .../Dapr.AspNetCore.IntegrationTest.csproj | 8 +--- test/Dapr.Client.Test/Dapr.Client.Test.csproj | 1 + .../Dapr.Extensions.Configuration.Test.csproj | 1 + ...aprSecretStoreConfigurationProviderTest.cs | 1 + 18 files changed, 79 insertions(+), 35 deletions(-) rename src/{Shared => Dapr.Common}/ArgumentVerifier.cs (96%) create mode 100644 src/Dapr.Common/AssemblyInfo.cs create mode 100644 src/Dapr.Common/Dapr.Common.csproj rename src/{Shared => Dapr.Common}/DaprDefaults.cs (99%) rename src/{Dapr.Client => Dapr.Common}/DaprException.cs (96%) diff --git a/Directory.Packages.props b/Directory.Packages.props index 990b5aeb1..d85020770 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -28,6 +28,7 @@ + diff --git a/all.sln b/all.sln index 228047852..1d5b011ca 100644 --- a/all.sln +++ b/all.sln @@ -118,6 +118,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.E2E.Test.Actors.Genera EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cryptography", "examples\Client\Cryptography\Cryptography.csproj", "{C74FBA78-13E8-407F-A173-4555AEE41FF3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Common", "src\Dapr.Common\Dapr.Common.csproj", "{B445B19C-A925-4873-8CB7-8317898B6970}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -290,6 +292,10 @@ Global {C74FBA78-13E8-407F-A173-4555AEE41FF3}.Debug|Any CPU.Build.0 = Debug|Any CPU {C74FBA78-13E8-407F-A173-4555AEE41FF3}.Release|Any CPU.ActiveCfg = Release|Any CPU {C74FBA78-13E8-407F-A173-4555AEE41FF3}.Release|Any CPU.Build.0 = Release|Any CPU + {B445B19C-A925-4873-8CB7-8317898B6970}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B445B19C-A925-4873-8CB7-8317898B6970}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B445B19C-A925-4873-8CB7-8317898B6970}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B445B19C-A925-4873-8CB7-8317898B6970}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -343,6 +349,7 @@ Global {AF89083D-4715-42E6-93E9-38497D12A8A6} = {DD020B34-460F-455F-8D17-CF4A949F100B} {B5CDB0DC-B26D-48F1-B934-FE5C1C991940} = {DD020B34-460F-455F-8D17-CF4A949F100B} {C74FBA78-13E8-407F-A173-4555AEE41FF3} = {A7F41094-8648-446B-AECD-DCC2CC871F73} + {B445B19C-A925-4873-8CB7-8317898B6970} = {27C5D71D-0721-4221-9286-B94AB07B58CF} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} diff --git a/examples/Client/ConfigurationApi/ConfigurationApi.csproj b/examples/Client/ConfigurationApi/ConfigurationApi.csproj index dee6a9878..761ebb38f 100644 --- a/examples/Client/ConfigurationApi/ConfigurationApi.csproj +++ b/examples/Client/ConfigurationApi/ConfigurationApi.csproj @@ -8,6 +8,7 @@ + diff --git a/examples/Client/DistributedLock/DistributedLock.csproj b/examples/Client/DistributedLock/DistributedLock.csproj index 9c3272e6e..4c04fb907 100644 --- a/examples/Client/DistributedLock/DistributedLock.csproj +++ b/examples/Client/DistributedLock/DistributedLock.csproj @@ -3,6 +3,7 @@ + diff --git a/src/Dapr.Actors/Dapr.Actors.csproj b/src/Dapr.Actors/Dapr.Actors.csproj index 37c73e0ed..54d3487b8 100644 --- a/src/Dapr.Actors/Dapr.Actors.csproj +++ b/src/Dapr.Actors/Dapr.Actors.csproj @@ -6,13 +6,9 @@ $(PackageTags);Actors - - - - - + diff --git a/src/Dapr.AspNetCore/Dapr.AspNetCore.csproj b/src/Dapr.AspNetCore/Dapr.AspNetCore.csproj index 54996e4bc..12b512fbb 100644 --- a/src/Dapr.AspNetCore/Dapr.AspNetCore.csproj +++ b/src/Dapr.AspNetCore/Dapr.AspNetCore.csproj @@ -5,17 +5,13 @@ This package contains the reference assemblies for developing services using Dapr and AspNetCore. - - - - - + diff --git a/src/Dapr.Client/Dapr.Client.csproj b/src/Dapr.Client/Dapr.Client.csproj index e091078c0..73f758a8f 100644 --- a/src/Dapr.Client/Dapr.Client.csproj +++ b/src/Dapr.Client/Dapr.Client.csproj @@ -1,10 +1,5 @@  - - - - - @@ -26,4 +21,8 @@ + + + + diff --git a/src/Shared/ArgumentVerifier.cs b/src/Dapr.Common/ArgumentVerifier.cs similarity index 96% rename from src/Shared/ArgumentVerifier.cs rename to src/Dapr.Common/ArgumentVerifier.cs index 907543f01..62ae98b54 100644 --- a/src/Shared/ArgumentVerifier.cs +++ b/src/Dapr.Common/ArgumentVerifier.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2021 The Dapr 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/Dapr.Common/AssemblyInfo.cs b/src/Dapr.Common/AssemblyInfo.cs new file mode 100644 index 000000000..a18d03bbc --- /dev/null +++ b/src/Dapr.Common/AssemblyInfo.cs @@ -0,0 +1,40 @@ +// ------------------------------------------------------------------------ +// Copyright 2024 The Dapr 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 System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Dapr.Actors, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.Actors.Generators, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.Actors.AspNetCore, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.Client, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.AspNetCore, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.Extensions.Configuration, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.Workflow, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] + +[assembly: InternalsVisibleTo("Dapr.Actors.AspNetCore.IntegrationTest, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.Actors.AspNetCore.IntegrationTest.App, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.Actors.AspNetCore.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.Actors.Generators.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.Actors.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.AspNetCore.IntegrationTest, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.AspNetCore.IntegrationTest.App, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.AspNetCore.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.Client.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.Common.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.E2E.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.E2E.Test.Actors, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.E2E.Test.Actors.Generators, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.E2E.Test.App, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.E2E.Test.App.Grpc, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.E2E.Test.App.ReentrantActors, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] +[assembly: InternalsVisibleTo("Dapr.Extensions.Configuration.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")] diff --git a/src/Dapr.Common/Dapr.Common.csproj b/src/Dapr.Common/Dapr.Common.csproj new file mode 100644 index 000000000..ea3e8ae84 --- /dev/null +++ b/src/Dapr.Common/Dapr.Common.csproj @@ -0,0 +1,14 @@ + + + + net6;net7;net8 + enable + enable + + + + + + + + diff --git a/src/Shared/DaprDefaults.cs b/src/Dapr.Common/DaprDefaults.cs similarity index 99% rename from src/Shared/DaprDefaults.cs rename to src/Dapr.Common/DaprDefaults.cs index b738de921..575a3c148 100644 --- a/src/Shared/DaprDefaults.cs +++ b/src/Dapr.Common/DaprDefaults.cs @@ -11,8 +11,6 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; - namespace Dapr { internal static class DaprDefaults diff --git a/src/Dapr.Client/DaprException.cs b/src/Dapr.Common/DaprException.cs similarity index 96% rename from src/Dapr.Client/DaprException.cs rename to src/Dapr.Common/DaprException.cs index e7b1efaba..2b600ef3a 100644 --- a/src/Dapr.Client/DaprException.cs +++ b/src/Dapr.Common/DaprException.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2021 The Dapr 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,6 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; using System.Runtime.Serialization; namespace Dapr diff --git a/src/Dapr.Extensions.Configuration/Dapr.Extensions.Configuration.csproj b/src/Dapr.Extensions.Configuration/Dapr.Extensions.Configuration.csproj index 29fd62ec4..5cc1043d3 100644 --- a/src/Dapr.Extensions.Configuration/Dapr.Extensions.Configuration.csproj +++ b/src/Dapr.Extensions.Configuration/Dapr.Extensions.Configuration.csproj @@ -4,10 +4,6 @@ enable - - - - Dapr Secret Store configuration provider implementation for Microsoft.Extensions.Configuration. @@ -15,6 +11,7 @@ + diff --git a/src/Dapr.Workflow/Dapr.Workflow.csproj b/src/Dapr.Workflow/Dapr.Workflow.csproj index af99e62d0..992baee73 100644 --- a/src/Dapr.Workflow/Dapr.Workflow.csproj +++ b/src/Dapr.Workflow/Dapr.Workflow.csproj @@ -17,13 +17,11 @@ - - - - + + \ No newline at end of file diff --git a/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj b/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj index ace894a4f..b1b27e618 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj +++ b/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj @@ -15,15 +15,9 @@ - - NU1903 - NU1903 - false - direct - - + diff --git a/test/Dapr.Client.Test/Dapr.Client.Test.csproj b/test/Dapr.Client.Test/Dapr.Client.Test.csproj index 9a8d91c79..f5d7d8c99 100644 --- a/test/Dapr.Client.Test/Dapr.Client.Test.csproj +++ b/test/Dapr.Client.Test/Dapr.Client.Test.csproj @@ -33,6 +33,7 @@ + diff --git a/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj b/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj index 0c7bce286..0ea0adeb7 100644 --- a/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj +++ b/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj @@ -21,6 +21,7 @@ + diff --git a/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs b/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs index 9bac31352..74b66c3cb 100644 --- a/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs +++ b/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs @@ -16,6 +16,7 @@ using System.Net; using System.Threading.Tasks; using Dapr.Client; +using Dapr; using FluentAssertions; using Grpc.Net.Client; using Microsoft.Extensions.Configuration; From aa8b0fd3511fb3d39b2c5cbf67b258d250c9b1d2 Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Wed, 16 Oct 2024 00:36:20 -0500 Subject: [PATCH 23/30] Extracted Protos out to common project (#1367) Protos pulled out to separate shared project --- all.sln | 7 ++ .../GrpcServiceSample.csproj | 1 + src/Dapr.Client/Dapr.Client.csproj | 16 ++--- src/Dapr.Protos/Dapr.Protos.csproj | 22 +++++++ .../Protos/dapr/proto/common/v1/common.proto | 4 +- .../dapr/proto/runtime}/v1/appcallback.proto | 16 ++--- .../Protos/dapr/proto/runtime}/v1/dapr.proto | 66 ++++++++++++------- .../Dapr.AspNetCore.IntegrationTest.csproj | 1 + .../Dapr.AspNetCore.Test.csproj | 1 + test/Dapr.Client.Test/Dapr.Client.Test.csproj | 1 + .../Dapr.Extensions.Configuration.Test.csproj | 1 + 11 files changed, 89 insertions(+), 47 deletions(-) create mode 100644 src/Dapr.Protos/Dapr.Protos.csproj rename src/{Dapr.Client => Dapr.Protos}/Protos/dapr/proto/common/v1/common.proto (98%) rename src/{Dapr.Client/Protos/dapr/proto/dapr => Dapr.Protos/Protos/dapr/proto/runtime}/v1/appcallback.proto (98%) rename src/{Dapr.Client/Protos/dapr/proto/dapr => Dapr.Protos/Protos/dapr/proto/runtime}/v1/dapr.proto (95%) diff --git a/all.sln b/all.sln index 1d5b011ca..1a5d78efb 100644 --- a/all.sln +++ b/all.sln @@ -118,6 +118,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.E2E.Test.Actors.Genera EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cryptography", "examples\Client\Cryptography\Cryptography.csproj", "{C74FBA78-13E8-407F-A173-4555AEE41FF3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Protos", "src\Dapr.Protos\Dapr.Protos.csproj", "{DFBABB04-50E9-42F6-B470-310E1B545638}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Common", "src\Dapr.Common\Dapr.Common.csproj", "{B445B19C-A925-4873-8CB7-8317898B6970}" EndProject Global @@ -292,6 +294,10 @@ Global {C74FBA78-13E8-407F-A173-4555AEE41FF3}.Debug|Any CPU.Build.0 = Debug|Any CPU {C74FBA78-13E8-407F-A173-4555AEE41FF3}.Release|Any CPU.ActiveCfg = Release|Any CPU {C74FBA78-13E8-407F-A173-4555AEE41FF3}.Release|Any CPU.Build.0 = Release|Any CPU + {DFBABB04-50E9-42F6-B470-310E1B545638}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DFBABB04-50E9-42F6-B470-310E1B545638}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DFBABB04-50E9-42F6-B470-310E1B545638}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DFBABB04-50E9-42F6-B470-310E1B545638}.Release|Any CPU.Build.0 = Release|Any CPU {B445B19C-A925-4873-8CB7-8317898B6970}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B445B19C-A925-4873-8CB7-8317898B6970}.Debug|Any CPU.Build.0 = Debug|Any CPU {B445B19C-A925-4873-8CB7-8317898B6970}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -349,6 +355,7 @@ Global {AF89083D-4715-42E6-93E9-38497D12A8A6} = {DD020B34-460F-455F-8D17-CF4A949F100B} {B5CDB0DC-B26D-48F1-B934-FE5C1C991940} = {DD020B34-460F-455F-8D17-CF4A949F100B} {C74FBA78-13E8-407F-A173-4555AEE41FF3} = {A7F41094-8648-446B-AECD-DCC2CC871F73} + {DFBABB04-50E9-42F6-B470-310E1B545638} = {27C5D71D-0721-4221-9286-B94AB07B58CF} {B445B19C-A925-4873-8CB7-8317898B6970} = {27C5D71D-0721-4221-9286-B94AB07B58CF} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/examples/AspNetCore/GrpcServiceSample/GrpcServiceSample.csproj b/examples/AspNetCore/GrpcServiceSample/GrpcServiceSample.csproj index 6084df013..2319f6a56 100644 --- a/examples/AspNetCore/GrpcServiceSample/GrpcServiceSample.csproj +++ b/examples/AspNetCore/GrpcServiceSample/GrpcServiceSample.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Dapr.Client/Dapr.Client.csproj b/src/Dapr.Client/Dapr.Client.csproj index 73f758a8f..7d74a7bb3 100644 --- a/src/Dapr.Client/Dapr.Client.csproj +++ b/src/Dapr.Client/Dapr.Client.csproj @@ -1,28 +1,20 @@  - - - - - - This package contains the reference assemblies for developing services using Dapr. + - - - - - + + - + diff --git a/src/Dapr.Protos/Dapr.Protos.csproj b/src/Dapr.Protos/Dapr.Protos.csproj new file mode 100644 index 000000000..8a8804b22 --- /dev/null +++ b/src/Dapr.Protos/Dapr.Protos.csproj @@ -0,0 +1,22 @@ + + + + enable + enable + This package contains the reference protos used by develop services using Dapr. + + + + + + + + + + + + + + + + diff --git a/src/Dapr.Client/Protos/dapr/proto/common/v1/common.proto b/src/Dapr.Protos/Protos/dapr/proto/common/v1/common.proto similarity index 98% rename from src/Dapr.Client/Protos/dapr/proto/common/v1/common.proto rename to src/Dapr.Protos/Protos/dapr/proto/common/v1/common.proto index 4acf9159d..0eb882b89 100644 --- a/src/Dapr.Client/Protos/dapr/proto/common/v1/common.proto +++ b/src/Dapr.Protos/Protos/dapr/proto/common/v1/common.proto @@ -1,4 +1,4 @@ -/* +/* Copyright 2021 The Dapr Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ option go_package = "github.com/dapr/dapr/pkg/proto/common/v1;common"; // when Dapr runtime delivers HTTP content. // // For example, when callers calls http invoke api -// POST http://localhost:3500/v1.0/invoke//method/?query1=value1&query2=value2 +// `POST http://localhost:3500/v1.0/invoke//method/?query1=value1&query2=value2` // // Dapr runtime will parse POST as a verb and extract querystring to quersytring map. message HTTPExtension { diff --git a/src/Dapr.Client/Protos/dapr/proto/dapr/v1/appcallback.proto b/src/Dapr.Protos/Protos/dapr/proto/runtime/v1/appcallback.proto similarity index 98% rename from src/Dapr.Client/Protos/dapr/proto/dapr/v1/appcallback.proto rename to src/Dapr.Protos/Protos/dapr/proto/runtime/v1/appcallback.proto index a86040364..51dee5539 100644 --- a/src/Dapr.Client/Protos/dapr/proto/dapr/v1/appcallback.proto +++ b/src/Dapr.Protos/Protos/dapr/proto/runtime/v1/appcallback.proto @@ -1,4 +1,4 @@ -/* +/* Copyright 2021 The Dapr Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -58,7 +58,7 @@ service AppCallbackHealthCheck { // AppCallbackAlpha V1 is an optional extension to AppCallback V1 to opt // for Alpha RPCs. service AppCallbackAlpha { - // Subscribes bulk events from Pubsub + // Subscribes bulk events from Pubsub rpc OnBulkTopicEventAlpha1(TopicEventBulkRequest) returns (TopicEventBulkResponse) {} // Sends job back to the app's endpoint at trigger time. @@ -185,14 +185,14 @@ message TopicEventBulkRequestEntry { // content type of the event contained. string content_type = 4; - + // The metadata associated with the event. map metadata = 5; } // TopicEventBulkRequest represents request for bulk message message TopicEventBulkRequest { - // Unique identifier for the bulk request. + // Unique identifier for the bulk request. string id = 1; // The list of items inside this bulk request. @@ -203,10 +203,10 @@ message TopicEventBulkRequest { // The pubsub topic which publisher sent to. string topic = 4; - + // The name of the pubsub the publisher sent to. string pubsub_name = 5; - + // The type of event related to the originating occurrence. string type = 6; @@ -310,8 +310,8 @@ message TopicRoutes { message TopicRule { // The optional CEL expression used to match the event. - // If the match is not specified, then the route is considered - // the default. + // If the match is not specified, then the route is considered + // the default. string match = 1; // The path used to identify matches for this subscription. diff --git a/src/Dapr.Client/Protos/dapr/proto/dapr/v1/dapr.proto b/src/Dapr.Protos/Protos/dapr/proto/runtime/v1/dapr.proto similarity index 95% rename from src/Dapr.Client/Protos/dapr/proto/dapr/v1/dapr.proto rename to src/Dapr.Protos/Protos/dapr/proto/runtime/v1/dapr.proto index 4185fb391..ecf0f76f7 100644 --- a/src/Dapr.Client/Protos/dapr/proto/dapr/v1/dapr.proto +++ b/src/Dapr.Protos/Protos/dapr/proto/runtime/v1/dapr.proto @@ -1,4 +1,4 @@ -/* +/* Copyright 2021 The Dapr Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ import "google/protobuf/any.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/timestamp.proto"; import "dapr/proto/common/v1/common.proto"; -import "dapr/proto/dapr/v1/appcallback.proto"; +import "dapr/proto/runtime/v1/appcallback.proto"; option csharp_namespace = "Dapr.Client.Autogen.Grpc.v1"; option java_outer_classname = "DaprProtos"; @@ -61,7 +61,7 @@ service Dapr { // SubscribeTopicEventsAlpha1 subscribes to a PubSub topic and receives topic // events from it. - rpc SubscribeTopicEventsAlpha1(stream SubscribeTopicEventsRequestAlpha1) returns (stream TopicEventRequest) {} + rpc SubscribeTopicEventsAlpha1(stream SubscribeTopicEventsRequestAlpha1) returns (stream SubscribeTopicEventsResponseAlpha1) {} // Invokes binding data to specific output bindings rpc InvokeBinding(InvokeBindingRequest) returns (InvokeBindingResponse) {} @@ -428,17 +428,17 @@ message BulkPublishResponseFailedEntry { // SubscribeTopicEventsRequestAlpha1 is a message containing the details for // subscribing to a topic via streaming. // The first message must always be the initial request. All subsequent -// messages must be event responses. +// messages must be event processed responses. message SubscribeTopicEventsRequestAlpha1 { oneof subscribe_topic_events_request_type { - SubscribeTopicEventsInitialRequestAlpha1 initial_request = 1; - SubscribeTopicEventsResponseAlpha1 event_response = 2; + SubscribeTopicEventsRequestInitialAlpha1 initial_request = 1; + SubscribeTopicEventsRequestProcessedAlpha1 event_processed = 2; } } -// SubscribeTopicEventsInitialRequestAlpha1 is the initial message containing the -// details for subscribing to a topic via streaming. -message SubscribeTopicEventsInitialRequestAlpha1 { +// SubscribeTopicEventsRequestInitialAlpha1 is the initial message containing +// the details for subscribing to a topic via streaming. +message SubscribeTopicEventsRequestInitialAlpha1 { // The name of the pubsub component string pubsub_name = 1; @@ -456,9 +456,9 @@ message SubscribeTopicEventsInitialRequestAlpha1 { optional string dead_letter_topic = 4; } -// SubscribeTopicEventsResponseAlpha1 is a message containing the result of a +// SubscribeTopicEventsRequestProcessedAlpha1 is the message containing the // subscription to a topic. -message SubscribeTopicEventsResponseAlpha1 { +message SubscribeTopicEventsRequestProcessedAlpha1 { // id is the unique identifier for the subscription request. string id = 1; @@ -466,6 +466,21 @@ message SubscribeTopicEventsResponseAlpha1 { TopicEventResponse status = 2; } + +// SubscribeTopicEventsResponseAlpha1 is a message returned from daprd +// when subscribing to a topic via streaming. +message SubscribeTopicEventsResponseAlpha1 { + oneof subscribe_topic_events_response_type { + SubscribeTopicEventsResponseInitialAlpha1 initial_response = 1; + TopicEventRequest event_message = 2; + } +} + +// SubscribeTopicEventsResponseInitialAlpha1 is the initial response from daprd +// when subscribing to a topic. +message SubscribeTopicEventsResponseInitialAlpha1 {} + + // InvokeBindingRequest is the message to send data to output bindings message InvokeBindingRequest { // The name of the output binding to invoke. @@ -478,6 +493,7 @@ message InvokeBindingRequest { // // Common metadata property: // - ttlInSeconds : the time to live in seconds for the message. + // // If set in the binding definition will cause all messages to // have a default time to live. The message ttl overrides any value // in the binding definition. @@ -824,11 +840,11 @@ message TryLockRequest { // // The reason why we don't make it automatically generated is: // 1. If it is automatically generated,there must be a 'my_lock_owner_id' field in the response. - // This name is so weird that we think it is inappropriate to put it into the api spec + // This name is so weird that we think it is inappropriate to put it into the api spec // 2. If we change the field 'my_lock_owner_id' in the response to 'lock_owner',which means the current lock owner of this lock, - // we find that in some lock services users can't get the current lock owner.Actually users don't need it at all. + // we find that in some lock services users can't get the current lock owner.Actually users don't need it at all. // 3. When reentrant lock is needed,the existing lock_owner is required to identify client and check "whether this client can reenter this lock". - // So this field in the request shouldn't be removed. + // So this field in the request shouldn't be removed. string lock_owner = 3 [json_name = "lockOwner"]; // Required. The time before expiry.The time unit is second. @@ -865,7 +881,7 @@ message SubtleGetKeyRequest { // JSON (JSON Web Key) as string JSON = 1; } - + // Name of the component string component_name = 1 [json_name="componentName"]; // Name (or name/version) of the key to use in the key vault @@ -1047,7 +1063,7 @@ message EncryptRequestOptions { // If true, the encrypted document does not contain a key reference. // In that case, calls to the Decrypt method must provide a key reference (name or name/version). // Defaults to false. - bool omit_decryption_key_name = 11 [json_name="omitDecryptionKeyName"]; + bool omit_decryption_key_name = 11 [json_name="omitDecryptionKeyName"]; // Key reference to embed in the encrypted document (name or name/version). // This is helpful if the reference of the key used to decrypt the document is different from the one used to encrypt it. // If unset, uses the reference of the key used to encrypt the document (this is the default behavior). @@ -1196,14 +1212,14 @@ message Job { // "0 15 3 * * *" - every day at 03:15 // // Period string expressions: - // Entry | Description | Equivalent To - // ----- | ----------- | ------------- - // @every | Run every (e.g. '@every 1h30m') | N/A - // @yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 0 1 1 * - // @monthly | Run once a month, midnight, first of month | 0 0 0 1 * * - // @weekly | Run once a week, midnight on Sunday | 0 0 0 * * 0 - // @daily (or @midnight) | Run once a day, midnight | 0 0 0 * * * - // @hourly | Run once an hour, beginning of hour | 0 0 * * * * + // Entry | Description | Equivalent To + // ----- | ----------- | ------------- + // @every `` | Run every `` (e.g. '@every 1h30m') | N/A + // @yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 0 1 1 * + // @monthly | Run once a month, midnight, first of month | 0 0 0 1 * * + // @weekly | Run once a week, midnight on Sunday | 0 0 0 * * 0 + // @daily (or @midnight) | Run once a day, midnight | 0 0 0 * * * + // @hourly | Run once an hour, beginning of hour | 0 0 * * * * optional string schedule = 2 [json_name = "schedule"]; // repeats is the optional number of times in which the job should be @@ -1258,4 +1274,4 @@ message DeleteJobRequest { // DeleteJobResponse is the message response to delete the job by name. message DeleteJobResponse { // Empty -} +} \ No newline at end of file diff --git a/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj b/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj index b1b27e618..d51dc70e8 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj +++ b/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj @@ -17,6 +17,7 @@ + diff --git a/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj b/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj index a76288891..9135e63d4 100644 --- a/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj +++ b/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj @@ -21,6 +21,7 @@ + \ No newline at end of file diff --git a/test/Dapr.Client.Test/Dapr.Client.Test.csproj b/test/Dapr.Client.Test/Dapr.Client.Test.csproj index f5d7d8c99..f0bea601f 100644 --- a/test/Dapr.Client.Test/Dapr.Client.Test.csproj +++ b/test/Dapr.Client.Test/Dapr.Client.Test.csproj @@ -33,6 +33,7 @@ + diff --git a/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj b/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj index 0ea0adeb7..ef6cfbcee 100644 --- a/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj +++ b/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj @@ -23,6 +23,7 @@ + From 23e8df02953c2cf4ad8bc7b5023978f16a92f0e1 Mon Sep 17 00:00:00 2001 From: Shubhdeep Singh Date: Thu, 17 Oct 2024 02:11:46 +0530 Subject: [PATCH 24/30] Improvement of the dotnet-contributing files (#1330) Add link about Dapr bot to contribution documentation --- CONTRIBUTING.md | 10 ++++++++++ .../en/dotnet-sdk-contributing/dotnet-contributing.md | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f47877cbd..7712340a5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -123,3 +123,13 @@ A non-exclusive list of code that must be places in `vendor/`: ## Code of Conduct This project has adopted the [Contributor Covenant Code of Conduct](https://github.com/dapr/community/blob/master/CODE-OF-CONDUCT.md) + + + +## GitHub Dapr Bot Commands + +Checkout the [daprbot documentation](https://docs.dapr.io/contributing/daprbot/) for Github commands you can run in this repo for common tasks. For example, you can comment `/assign` on an issue to assign it to yourself. + + + + diff --git a/daprdocs/content/en/dotnet-sdk-contributing/dotnet-contributing.md b/daprdocs/content/en/dotnet-sdk-contributing/dotnet-contributing.md index a4e546ffa..6664191d6 100644 --- a/daprdocs/content/en/dotnet-sdk-contributing/dotnet-contributing.md +++ b/daprdocs/content/en/dotnet-sdk-contributing/dotnet-contributing.md @@ -21,3 +21,7 @@ The `daprdocs` directory contains the markdown files that are rendered into the - All rules in the [docs guide]({{< ref contributing-docs.md >}}) should be followed in addition to these. - All files and directories should be prefixed with `dotnet-` to ensure all file/directory names are globally unique across all Dapr documentation. + +## GitHub Dapr Bot Commands + +Checkout the [daprbot documentation](https://docs.dapr.io/contributing/daprbot/) for Github commands you can run in this repo for common tasks. For example, you can comment `/assign` on an issue to assign it to yourself. From d5c32f4ecb579e99b184a19c6e727a0eb31d38f6 Mon Sep 17 00:00:00 2001 From: Ilias Date: Thu, 17 Oct 2024 23:39:39 +0100 Subject: [PATCH 25/30] Support case insensitive cloudevent payloads and forward cloudevent props s headers (#1153) * forward cloudevent props Signed-off-by: Ilias Politsopoulos * refactor middleware Signed-off-by: Ilias Politsopoulos * add cloud event property filters Signed-off-by: Ilias Politsopoulos * update string check Signed-off-by: Ilias Politsopoulos * forward cloudevent props Signed-off-by: Ilias Politsopoulos * refactor middleware Signed-off-by: Ilias Politsopoulos * add cloud event property filters Signed-off-by: Ilias Politsopoulos * update checks Signed-off-by: Ilias Politsopoulos --------- Signed-off-by: Whit Waldo Co-authored-by: Whit Waldo --- .../Controllers/SampleController.cs | 45 ++++-- .../AspNetCore/ControllerSample/Startup.cs | 8 +- .../CloudEventPropertyNames.cs | 9 ++ src/Dapr.AspNetCore/CloudEventsMiddleware.cs | 116 +++++++++++--- .../CloudEventsMiddlewareOptions.cs | 42 ++++- .../CloudEventsMiddlewareTest.cs | 144 ++++++++++++++++++ 6 files changed, 332 insertions(+), 32 deletions(-) create mode 100644 src/Dapr.AspNetCore/CloudEventPropertyNames.cs diff --git a/examples/AspNetCore/ControllerSample/Controllers/SampleController.cs b/examples/AspNetCore/ControllerSample/Controllers/SampleController.cs index 485614150..5b339288c 100644 --- a/examples/AspNetCore/ControllerSample/Controllers/SampleController.cs +++ b/examples/AspNetCore/ControllerSample/Controllers/SampleController.cs @@ -11,6 +11,8 @@ // limitations under the License. // ------------------------------------------------------------------------ +using System.Linq; + namespace ControllerSample.Controllers { using System; @@ -43,6 +45,7 @@ public SampleController(ILogger logger) /// State store name. /// public const string StoreName = "statestore"; + private readonly ILogger logger; /// @@ -72,6 +75,11 @@ public ActionResult Get([FromState(StoreName)] StateEntry acco [HttpPost("deposit")] public async Task> Deposit(Transaction transaction, [FromServices] DaprClient daprClient) { + // Example reading cloudevent properties from the headers + var headerEntries = Request.Headers.Aggregate("", (current, header) => current + ($"------- Header: {header.Key} : {header.Value}" + Environment.NewLine)); + + logger.LogInformation(headerEntries); + logger.LogInformation("Enter deposit"); var state = await daprClient.GetStateEntryAsync(StoreName, transaction.Id); state.Value ??= new Account() { Id = transaction.Id, }; @@ -83,7 +91,7 @@ public async Task> Deposit(Transaction transaction, [FromS } state.Value.Balance += transaction.Amount; - logger.LogInformation("Balance for Id {0} is {1}",state.Value.Id, state.Value.Balance); + logger.LogInformation("Balance for Id {0} is {1}", state.Value.Id, state.Value.Balance); await state.SaveAsync(); return state.Value; } @@ -98,22 +106,23 @@ public async Task> Deposit(Transaction transaction, [FromS [Topic("pubsub", "multideposit", "amountDeadLetterTopic", false)] [BulkSubscribe("multideposit", 500, 2000)] [HttpPost("multideposit")] - public async Task> MultiDeposit([FromBody] BulkSubscribeMessage> - bulkMessage, [FromServices] DaprClient daprClient) + public async Task> MultiDeposit([FromBody] + BulkSubscribeMessage> + bulkMessage, [FromServices] DaprClient daprClient) { logger.LogInformation("Enter bulk deposit"); - + List entries = new List(); foreach (var entry in bulkMessage.Entries) - { + { try { var transaction = entry.Event.Data; var state = await daprClient.GetStateEntryAsync(StoreName, transaction.Id); state.Value ??= new Account() { Id = transaction.Id, }; - logger.LogInformation("Id is {0}, the amount to be deposited is {1}", + logger.LogInformation("Id is {0}, the amount to be deposited is {1}", transaction.Id, transaction.Amount); if (transaction.Amount < 0m) @@ -124,12 +133,16 @@ public async Task> MultiDeposit([FromBody state.Value.Balance += transaction.Amount; logger.LogInformation("Balance is {0}", state.Value.Balance); await state.SaveAsync(); - entries.Add(new BulkSubscribeAppResponseEntry(entry.EntryId, BulkSubscribeAppResponseStatus.SUCCESS)); - } catch (Exception e) { + entries.Add( + new BulkSubscribeAppResponseEntry(entry.EntryId, BulkSubscribeAppResponseStatus.SUCCESS)); + } + catch (Exception e) + { logger.LogError(e.Message); entries.Add(new BulkSubscribeAppResponseEntry(entry.EntryId, BulkSubscribeAppResponseStatus.RETRY)); } } + return new BulkSubscribeAppResponse(entries); } @@ -165,6 +178,7 @@ public async Task> Withdraw(Transaction transaction, [From { return this.NotFound(); } + if (transaction.Amount < 0m) { return BadRequest(new { statusCode = 400, message = "bad request" }); @@ -185,7 +199,8 @@ public async Task> Withdraw(Transaction transaction, [From /// "pubsub", the first parameter into the Topic attribute, is name of the default pub/sub configured by the Dapr CLI. [Topic("pubsub", "withdraw", "event.type ==\"withdraw.v2\"", 1)] [HttpPost("withdraw.v2")] - public async Task> WithdrawV2(TransactionV2 transaction, [FromServices] DaprClient daprClient) + public async Task> WithdrawV2(TransactionV2 transaction, + [FromServices] DaprClient daprClient) { logger.LogInformation("Enter withdraw.v2"); if (transaction.Channel == "mobile" && transaction.Amount > 10000) @@ -214,12 +229,15 @@ public async Task> WithdrawV2(TransactionV2 transaction, [ /// "pubsub", the first parameter into the Topic attribute, is name of the default pub/sub configured by the Dapr CLI. [Topic("pubsub", "rawDeposit", true)] [HttpPost("rawDeposit")] - public async Task> RawDeposit([FromBody] JsonDocument rawTransaction, [FromServices] DaprClient daprClient) + public async Task> RawDeposit([FromBody] JsonDocument rawTransaction, + [FromServices] DaprClient daprClient) { var transactionString = rawTransaction.RootElement.GetProperty("data_base64").GetString(); - logger.LogInformation($"Enter deposit: {transactionString} - {Encoding.UTF8.GetString(Convert.FromBase64String(transactionString))}"); + logger.LogInformation( + $"Enter deposit: {transactionString} - {Encoding.UTF8.GetString(Convert.FromBase64String(transactionString))}"); var transactionJson = JsonSerializer.Deserialize(Convert.FromBase64String(transactionString)); - var transaction = JsonSerializer.Deserialize(transactionJson.RootElement.GetProperty("data").GetRawText()); + var transaction = + JsonSerializer.Deserialize(transactionJson.RootElement.GetProperty("data").GetRawText()); var state = await daprClient.GetStateEntryAsync(StoreName, transaction.Id); state.Value ??= new Account() { Id = transaction.Id, }; logger.LogInformation("Id is {0}, the amount to be deposited is {1}", transaction.Id, transaction.Amount); @@ -239,7 +257,8 @@ public async Task> RawDeposit([FromBody] JsonDocument rawT /// Method for returning a BadRequest result which will cause Dapr sidecar to throw an RpcException /// [HttpPost("throwException")] - public async Task> ThrowException(Transaction transaction, [FromServices] DaprClient daprClient) + public async Task> ThrowException(Transaction transaction, + [FromServices] DaprClient daprClient) { logger.LogInformation("Enter ThrowException"); var task = Task.Delay(10); diff --git a/examples/AspNetCore/ControllerSample/Startup.cs b/examples/AspNetCore/ControllerSample/Startup.cs index 64cfba512..ddc6d1c5f 100644 --- a/examples/AspNetCore/ControllerSample/Startup.cs +++ b/examples/AspNetCore/ControllerSample/Startup.cs @@ -11,8 +11,11 @@ // limitations under the License. // ------------------------------------------------------------------------ + +using Dapr; using Dapr.AspNetCore; + namespace ControllerSample { using Microsoft.AspNetCore.Builder; @@ -63,7 +66,10 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseRouting(); - app.UseCloudEvents(); + app.UseCloudEvents(new CloudEventsMiddlewareOptions + { + ForwardCloudEventPropertiesAsHeaders = true + }); app.UseAuthorization(); diff --git a/src/Dapr.AspNetCore/CloudEventPropertyNames.cs b/src/Dapr.AspNetCore/CloudEventPropertyNames.cs new file mode 100644 index 000000000..87e496004 --- /dev/null +++ b/src/Dapr.AspNetCore/CloudEventPropertyNames.cs @@ -0,0 +1,9 @@ +namespace Dapr +{ + internal static class CloudEventPropertyNames + { + public const string Data = "data"; + public const string DataContentType = "datacontenttype"; + public const string DataBase64 = "data_base64"; + } +} diff --git a/src/Dapr.AspNetCore/CloudEventsMiddleware.cs b/src/Dapr.AspNetCore/CloudEventsMiddleware.cs index 24c89cfed..eac526c26 100644 --- a/src/Dapr.AspNetCore/CloudEventsMiddleware.cs +++ b/src/Dapr.AspNetCore/CloudEventsMiddleware.cs @@ -11,6 +11,9 @@ // limitations under the License. // ------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Linq; + namespace Dapr { using System; @@ -27,6 +30,15 @@ namespace Dapr internal class CloudEventsMiddleware { private const string ContentType = "application/cloudevents+json"; + + // These cloudevent properties are either containing the body of the message or + // are included in the headers by other components of Dapr earlier in the pipeline + private static readonly string[] ExcludedPropertiesFromHeaders = + { + CloudEventPropertyNames.DataContentType, CloudEventPropertyNames.Data, + CloudEventPropertyNames.DataBase64, "pubsubname", "traceparent" + }; + private readonly RequestDelegate next; private readonly CloudEventsMiddlewareOptions options; @@ -52,7 +64,7 @@ public Task InvokeAsync(HttpContext httpContext) // The philosophy here is that we don't report an error for things we don't support, because // that would block someone from implementing their own support for it. We only report an error // when something we do support isn't correct. - if (!this.MatchesContentType(httpContext, out var charSet)) + if (!MatchesContentType(httpContext, out var charSet)) { return this.next(httpContext); } @@ -69,7 +81,8 @@ private async Task ProcessBodyAsync(HttpContext httpContext, string charSet) } else { - using (var reader = new HttpRequestStreamReader(httpContext.Request.Body, Encoding.GetEncoding(charSet))) + using (var reader = + new HttpRequestStreamReader(httpContext.Request.Body, Encoding.GetEncoding(charSet))) { var text = await reader.ReadToEndAsync(); json = JsonSerializer.Deserialize(text); @@ -83,17 +96,43 @@ private async Task ProcessBodyAsync(HttpContext httpContext, string charSet) string contentType; // Check whether to use data or data_base64 as per https://github.com/cloudevents/spec/blob/v1.0.1/json-format.md#31-handling-of-data - var isDataSet = json.TryGetProperty("data", out var data); - var isBinaryDataSet = json.TryGetProperty("data_base64", out var binaryData); + // Get the property names by OrdinalIgnoreCase comparison to support case insensitive JSON as the Json Serializer for AspCore already supports it by default. + var jsonPropNames = json.EnumerateObject().ToArray(); + + var dataPropName = jsonPropNames + .Select(d => d.Name) + .FirstOrDefault(d => d.Equals(CloudEventPropertyNames.Data, StringComparison.OrdinalIgnoreCase)); + + var dataBase64PropName = jsonPropNames + .Select(d => d.Name) + .FirstOrDefault(d => + d.Equals(CloudEventPropertyNames.DataBase64, StringComparison.OrdinalIgnoreCase)); + + var isDataSet = false; + var isBinaryDataSet = false; + JsonElement data = default; + + if (dataPropName != null) + { + isDataSet = true; + data = json.TryGetProperty(dataPropName, out var dataJsonElement) ? dataJsonElement : data; + } + + if (dataBase64PropName != null) + { + isBinaryDataSet = true; + data = json.TryGetProperty(dataBase64PropName, out var dataJsonElement) ? dataJsonElement : data; + } if (isDataSet && isBinaryDataSet) { httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; return; } - else if (isDataSet) + + if (isDataSet) { - contentType = this.GetDataContentType(json, out var isJson); + contentType = GetDataContentType(json, out var isJson); // If the value is anything other than a JSON string, treat it as JSON. Cloud Events requires // non-JSON text to be enclosed in a JSON string. @@ -109,8 +148,8 @@ private async Task ProcessBodyAsync(HttpContext httpContext, string charSet) { // Rehydrate body from contents of the string var text = data.GetString(); - using var writer = new HttpResponseStreamWriter(body, Encoding.UTF8); - writer.Write(text); + await using var writer = new HttpResponseStreamWriter(body, Encoding.UTF8); + await writer.WriteAsync(text); } body.Seek(0L, SeekOrigin.Begin); @@ -120,10 +159,10 @@ private async Task ProcessBodyAsync(HttpContext httpContext, string charSet) // As per the spec, if the implementation determines that the type of data is Binary, // the value MUST be represented as a JSON string expression containing the Base64 encoded // binary value, and use the member name data_base64 to store it inside the JSON object. - var decodedBody = binaryData.GetBytesFromBase64(); + var decodedBody = data.GetBytesFromBase64(); body = new MemoryStream(decodedBody); body.Seek(0L, SeekOrigin.Begin); - contentType = this.GetDataContentType(json, out _); + contentType = GetDataContentType(json, out _); } else { @@ -131,6 +170,8 @@ private async Task ProcessBodyAsync(HttpContext httpContext, string charSet) contentType = null; } + ForwardCloudEventPropertiesAsHeaders(httpContext, jsonPropNames); + originalBody = httpContext.Request.Body; originalContentType = httpContext.Request.ContentType; @@ -148,16 +189,57 @@ private async Task ProcessBodyAsync(HttpContext httpContext, string charSet) } } - private string GetDataContentType(JsonElement json, out bool isJson) + private void ForwardCloudEventPropertiesAsHeaders( + HttpContext httpContext, + IEnumerable jsonPropNames) + { + if (!options.ForwardCloudEventPropertiesAsHeaders) + { + return; + } + + var filteredPropertyNames = jsonPropNames + .Where(d => !ExcludedPropertiesFromHeaders.Contains(d.Name, StringComparer.OrdinalIgnoreCase)); + + if (options.IncludedCloudEventPropertiesAsHeaders != null) + { + filteredPropertyNames = filteredPropertyNames + .Where(d => options.IncludedCloudEventPropertiesAsHeaders + .Contains(d.Name, StringComparer.OrdinalIgnoreCase)); + } + else if (options.ExcludedCloudEventPropertiesFromHeaders != null) + { + filteredPropertyNames = filteredPropertyNames + .Where(d => !options.ExcludedCloudEventPropertiesFromHeaders + .Contains(d.Name, StringComparer.OrdinalIgnoreCase)); + } + + foreach (var jsonProperty in filteredPropertyNames) + { + httpContext.Request.Headers.TryAdd($"Cloudevent.{jsonProperty.Name.ToLowerInvariant()}", + jsonProperty.Value.GetRawText().Trim('\"')); + } + } + + private static string GetDataContentType(JsonElement json, out bool isJson) { + var dataContentTypePropName = json + .EnumerateObject() + .Select(d => d.Name) + .FirstOrDefault(d => + d.Equals(CloudEventPropertyNames.DataContentType, + StringComparison.OrdinalIgnoreCase)); + string contentType; - if (json.TryGetProperty("datacontenttype", out var dataContentType) && - dataContentType.ValueKind == JsonValueKind.String && - MediaTypeHeaderValue.TryParse(dataContentType.GetString(), out var parsed)) + + if (dataContentTypePropName != null + && json.TryGetProperty(dataContentTypePropName, out var dataContentType) + && dataContentType.ValueKind == JsonValueKind.String + && MediaTypeHeaderValue.TryParse(dataContentType.GetString(), out var parsed)) { contentType = dataContentType.GetString(); - isJson = - parsed.MediaType.Equals( "application/json", StringComparison.Ordinal) || + isJson = + parsed.MediaType.Equals("application/json", StringComparison.Ordinal) || parsed.Suffix.EndsWith("+json", StringComparison.Ordinal); // Since S.T.Json always outputs utf-8, we may need to normalize the data content type @@ -179,7 +261,7 @@ private string GetDataContentType(JsonElement json, out bool isJson) return contentType; } - private bool MatchesContentType(HttpContext httpContext, out string charSet) + private static bool MatchesContentType(HttpContext httpContext, out string charSet) { if (httpContext.Request.ContentType == null) { diff --git a/src/Dapr.AspNetCore/CloudEventsMiddlewareOptions.cs b/src/Dapr.AspNetCore/CloudEventsMiddlewareOptions.cs index 251a939a7..84e68adb5 100644 --- a/src/Dapr.AspNetCore/CloudEventsMiddlewareOptions.cs +++ b/src/Dapr.AspNetCore/CloudEventsMiddlewareOptions.cs @@ -29,9 +29,49 @@ public class CloudEventsMiddlewareOptions /// instead of the expected JSON-decoded value of Hello, "world!". /// /// - /// Setting this property to true restores the previous invalid behavior for compatiblity. + /// Setting this property to true restores the previous invalid behavior for compatibility. /// /// public bool SuppressJsonDecodingOfTextPayloads { get; set; } + + /// + /// Gets or sets a value that will determine whether the CloudEvent properties will be forwarded as Request Headers. + /// + /// + /// + /// Setting this property to true will forward all the CloudEvent properties as Request Headers. + /// For more fine grained control of which properties are forwarded you can use either or . + /// + /// + /// Property names will always be prefixed with 'Cloudevent.' and be lower case in the following format:"Cloudevent.type" + /// + /// + /// ie. A CloudEvent property "type": "Example.Type" will be added as "Cloudevent.type": "Example.Type" request header. + /// + /// + public bool ForwardCloudEventPropertiesAsHeaders { get; set; } + + /// + /// Gets or sets an array of CloudEvent property names that will be forwarded as Request Headers if is set to true. + /// + /// + /// + /// Note: Setting this will only forwarded the listed property names. + /// + /// + /// ie: ["type", "subject"] + /// + /// + public string[] IncludedCloudEventPropertiesAsHeaders { get; set; } + + /// + /// Gets or sets an array of CloudEvent property names that will not be forwarded as Request Headers if is set to true. + /// + /// + /// + /// ie: ["type", "subject"] + /// + /// + public string[] ExcludedCloudEventPropertiesFromHeaders { get; set; } } } diff --git a/test/Dapr.AspNetCore.Test/CloudEventsMiddlewareTest.cs b/test/Dapr.AspNetCore.Test/CloudEventsMiddlewareTest.cs index 904f6648f..c8a5ff402 100644 --- a/test/Dapr.AspNetCore.Test/CloudEventsMiddlewareTest.cs +++ b/test/Dapr.AspNetCore.Test/CloudEventsMiddlewareTest.cs @@ -84,7 +84,151 @@ public async Task InvokeAsync_ReplacesBodyJson(string dataContentType, string ch await pipeline.Invoke(context); } + + [Theory] + [InlineData(null, null)] // assumes application/json + utf8 + [InlineData("application/json", null)] // assumes utf8 + [InlineData("application/json", "utf-8")] + [InlineData("application/json", "UTF-8")] + [InlineData("application/person+json", "UTF-16")] // arbitrary content type and charset + public async Task InvokeAsync_ReplacesPascalCasedBodyJson(string dataContentType, string charSet) + { + var encoding = charSet == null ? null : Encoding.GetEncoding(charSet); + var app = new ApplicationBuilder(null); + app.UseCloudEvents(); + // Do verification in the scope of the middleware + app.Run(httpContext => + { + httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json"); + ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}"); + return Task.CompletedTask; + }); + + var pipeline = app.Build(); + + var context = new DefaultHttpContext(); + context.Request.ContentType = charSet == null ? "application/cloudevents+json" : $"application/cloudevents+json;charset={charSet}"; + context.Request.Body = dataContentType == null ? + MakeBody("{ \"Data\": { \"name\":\"jimmy\" } }", encoding) : + MakeBody($"{{ \"DataContentType\": \"{dataContentType}\", \"Data\": {{ \"name\":\"jimmy\" }} }}", encoding); + + await pipeline.Invoke(context); + } + + [Theory] + [InlineData(null, null)] // assumes application/json + utf8 + [InlineData("application/json", null)] // assumes utf8 + [InlineData("application/json", "utf-8")] + [InlineData("application/json", "UTF-8")] + [InlineData("application/person+json", "UTF-16")] // arbitrary content type and charset + public async Task InvokeAsync_ForwardsJsonPropertiesAsHeaders(string dataContentType, string charSet) + { + var encoding = charSet == null ? null : Encoding.GetEncoding(charSet); + var app = new ApplicationBuilder(null); + app.UseCloudEvents(new CloudEventsMiddlewareOptions + { + ForwardCloudEventPropertiesAsHeaders = true + }); + + // Do verification in the scope of the middleware + app.Run(httpContext => + { + httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json"); + ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}"); + + httpContext.Request.Headers.Should().ContainKey("Cloudevent.type").WhichValue.Should().BeEquivalentTo("Test.Type"); + httpContext.Request.Headers.Should().ContainKey("Cloudevent.subject").WhichValue.Should().BeEquivalentTo("Test.Subject"); + return Task.CompletedTask; + }); + + var pipeline = app.Build(); + + var context = new DefaultHttpContext(); + context.Request.ContentType = charSet == null ? "application/cloudevents+json" : $"application/cloudevents+json;charset={charSet}"; + context.Request.Body = dataContentType == null ? + MakeBody("{ \"type\": \"Test.Type\", \"subject\": \"Test.Subject\", \"data\": { \"name\":\"jimmy\" } }", encoding) : + MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"type\":\"Test.Type\", \"subject\": \"Test.Subject\", \"data\": {{ \"name\":\"jimmy\" }} }}", encoding); + + await pipeline.Invoke(context); + } + + [Theory] + [InlineData(null, null)] // assumes application/json + utf8 + [InlineData("application/json", null)] // assumes utf8 + [InlineData("application/json", "utf-8")] + [InlineData("application/json", "UTF-8")] + [InlineData("application/person+json", "UTF-16")] // arbitrary content type and charset + public async Task InvokeAsync_ForwardsIncludedJsonPropertiesAsHeaders(string dataContentType, string charSet) + { + var encoding = charSet == null ? null : Encoding.GetEncoding(charSet); + var app = new ApplicationBuilder(null); + app.UseCloudEvents(new CloudEventsMiddlewareOptions + { + ForwardCloudEventPropertiesAsHeaders = true, + IncludedCloudEventPropertiesAsHeaders = new []{"type"} + }); + + // Do verification in the scope of the middleware + app.Run(httpContext => + { + httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json"); + ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}"); + + httpContext.Request.Headers.Should().ContainKey("Cloudevent.type").WhichValue.Should().BeEquivalentTo("Test.Type"); + httpContext.Request.Headers.Should().NotContainKey("Cloudevent.subject"); + return Task.CompletedTask; + }); + + var pipeline = app.Build(); + + var context = new DefaultHttpContext(); + context.Request.ContentType = charSet == null ? "application/cloudevents+json" : $"application/cloudevents+json;charset={charSet}"; + context.Request.Body = dataContentType == null ? + MakeBody("{ \"type\": \"Test.Type\", \"subject\": \"Test.Subject\", \"data\": { \"name\":\"jimmy\" } }", encoding) : + MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"type\":\"Test.Type\", \"subject\": \"Test.Subject\", \"data\": {{ \"name\":\"jimmy\" }} }}", encoding); + + await pipeline.Invoke(context); + } + + [Theory] + [InlineData(null, null)] // assumes application/json + utf8 + [InlineData("application/json", null)] // assumes utf8 + [InlineData("application/json", "utf-8")] + [InlineData("application/json", "UTF-8")] + [InlineData("application/person+json", "UTF-16")] // arbitrary content type and charset + public async Task InvokeAsync_DoesNotForwardExcludedJsonPropertiesAsHeaders(string dataContentType, string charSet) + { + var encoding = charSet == null ? null : Encoding.GetEncoding(charSet); + var app = new ApplicationBuilder(null); + app.UseCloudEvents(new CloudEventsMiddlewareOptions + { + ForwardCloudEventPropertiesAsHeaders = true, + ExcludedCloudEventPropertiesFromHeaders = new []{"type"} + }); + + // Do verification in the scope of the middleware + app.Run(httpContext => + { + httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json"); + ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}"); + + httpContext.Request.Headers.Should().NotContainKey("Cloudevent.type"); + httpContext.Request.Headers.Should().ContainKey("Cloudevent.subject").WhichValue.Should().BeEquivalentTo("Test.Subject"); + return Task.CompletedTask; + }); + + var pipeline = app.Build(); + + var context = new DefaultHttpContext(); + context.Request.ContentType = charSet == null ? "application/cloudevents+json" : $"application/cloudevents+json;charset={charSet}"; + context.Request.Body = dataContentType == null ? + MakeBody("{ \"type\": \"Test.Type\", \"subject\": \"Test.Subject\", \"data\": { \"name\":\"jimmy\" } }", encoding) : + MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"type\":\"Test.Type\", \"subject\": \"Test.Subject\", \"data\": {{ \"name\":\"jimmy\" }} }}", encoding); + + await pipeline.Invoke(context); + } + [Fact] public async Task InvokeAsync_ReplacesBodyNonJsonData() { From 1e148874bb3778d254d9a31f3a4ec6c33caafc0a Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Fri, 18 Oct 2024 02:56:12 -0500 Subject: [PATCH 26/30] Updating actor serialization documentation (#1371) * Changed headers, updated introduction to reflect the difference in serialization between either type and added a brief section to detail the use of System.Text.Json for weakly-typed Dapr actor clients and to point to official documentation on it --------- Signed-off-by: Whit Waldo --- .../dotnet-actors-serialization.md | 278 +++++++++++++++++- 1 file changed, 263 insertions(+), 15 deletions(-) diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-serialization.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-serialization.md index abbeb437d..787a7e41f 100644 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-serialization.md +++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-actors/dotnet-actors-serialization.md @@ -5,15 +5,263 @@ linkTitle: "Actor serialization" weight: 300000 description: Necessary steps to serialize your types using remoted Actors in .NET --- +# Actor Serialization -The Dapr actor package enables you to use Dapr virtual actors within a .NET application with strongly-typed remoting, but if you intend to send and receive strongly-typed data from your methods, there are a few key ground rules to understand. In this guide, you will learn how to configure your classes and records so they are properly serialized and deserialized at runtime. +The Dapr actor package enables you to use Dapr virtual actors within a .NET application with either a weakly- or strongly-typed client. Each utilizes a different serialization approach. This document will review the differences and convey a few key ground rules to understand in either scenario. -# Data Contract Serialization -When Dapr's virtual actors are invoked via the remoting proxy, your data is serialized using a serialization engine called the [Data Contract Serializer](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/serializable-types) implemented by the [DataContractSerializer](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.datacontractserializer) class, which converts your C# types to and from XML documents. When sending or receiving primitives (like strings or ints), this serialization happens transparently and there's no requisite preparation needed on your part. However, when working with complex types such as those you create, there are some important rules to take into consideration so this process works smoothly. +Please be advised that it is not a supported scenario to use the weakly- or strongly typed actor clients interchangeably because of these different serialization approaches. The data persisted using one Actor client will not be accessible using the other Actor client, so it is important to pick one and use it consistently throughout your application. -This serialization framework is not specific to Dapr and is separately maintained by the .NET team within the [.NET Github repository](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs). +## Weakly-typed Dapr Actor client +In this section, you will learn how to configure your C# types so they are properly serialized and deserialized at runtime when using a weakly-typed actor client. These clients use string-based names of methods with request and response payloads that are serialized using the System.Text.Json serializer. Please note that this serialization framework is not specific to Dapr and is separately maintained by the .NET team within the [.NET GitHub repository](https://github.com/dotnet/runtime/tree/main/src/libraries/System.Text.Json). -## Serializable Types +When using the weakly-typed Dapr Actor client to invoke methods from your various actors, it's not necessary to independently serialize or deserialize the method payloads as this will happen transparently on your behalf by the SDK. + +The client will use the latest version of System.Text.Json available for the version of .NET you're building against and serialization is subject to all the inherent capabilities provided in the [associated .NET documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/overview). + +The serializer will be configured to use the `JsonSerializerOptions.Web` [default options](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/configure-options?pivots=dotnet-8-0#web-defaults-for-jsonserializeroptions) unless overridden with a custom options configuration which means the following are applied: +- Deserialization of the property name is performed in a case-insensitive manner +- Serialization of the property name is performed using [camel casing](https://en.wikipedia.org/wiki/Camel_case) unless the property is overridden with a `[JsonPropertyName]` attribute +- Deserialization will read numeric values from number and/or string values + +### Basic Serialization +In the following example, we present a simple class named Doodad though it could just as well be a record as well. + +```csharp +public class Doodad +{ + public Guid Id { get; set; } + public string Name { get; set; } + public int Count { get; set; } +} +``` + +By default, this will serialize using the names of the members as used in the type and whatever values it was instantiated with: + +```json +{"id": "a06ced64-4f42-48ad-84dd-46ae6a7e333d", "name": "DoodadName", "count": 5} +``` + +### Override Serialized Property Name +The default property names can be overridden by applying the `[JsonPropertyName]` attribute to desired properties. + +Generally, this isn't going to be necessary for types you're persisting to the actor state as you're not intended to read or write them independent of Dapr-associated functionality, but +the following is provided just to clearly illustrate that it's possible. + +#### Override Property Names on Classes +Here's an example demonstrating the use of `JsonPropertyName` to change the name for the first property following serialization. Note that the last usage of `JsonPropertyName` on the `Count` property +matches what it would be expected to serialize to. This is largely just to demonstrate that applying this attribute won't negatively impact anything - in fact, it might be preferable if you later +decide to change the default serialization options but still need to consistently access the properties previously serialized before that change as `JsonPropertyName` will override those options. + +```csharp +public class Doodad +{ + [JsonPropertyName("identifier")] + public Guid Id { get; set; } + public string Name { get; set; } + [JsonPropertyName("count")] + public int Count { get; set; } +} +``` + +This would serialize to the following: + +```json +{"identifier": "a06ced64-4f42-48ad-84dd-46ae6a7e333d", "name": "DoodadName", "count": 5} +``` + +#### Override Property Names on Records +Let's try doing the same thing with a record from C# 12 or later: + +```csharp +public record Thingy(string Name, [JsonPropertyName("count")] int Count); +``` + +Because the argument passed in a primary constructor (introduced in C# 12) can be applied to either a property or field within a record, using the `[JsonPropertyName]` attribute may +require specifying that you intend the attribute to apply to a property and not a field in some ambiguous cases. Should this be necessary, you'd indicate as much in the primary constructor with: + +```csharp +public record Thingy(string Name, [property: JsonPropertyName("count")] int Count); +``` + +If `[property: ]` is applied to the `[JsonPropertyName]` attribute where it's not necessary, it will not negatively impact serialization or deserialization as the operation will +proceed normally as though it were a property (as it typically would if not marked as such). + +### Enumeration types +Enumerations, including flat enumerations are serializable to JSON, but the value persisted may surprise you. Again, it's not expected that the developer should ever engage +with the serialized data independently of Dapr, but the following information may at least help in diagnosing why a seemingly mild version migration isn't working as expected. + +Take the following `enum` type providing the various seasons in the year: + +```csharp +public enum Season +{ + Spring, + Summer, + Fall, + Winter +} +``` + +We'll go ahead and use a separate demonstration type that references our `Season` and simultaneously illustrate how this works with records: + +```csharp +public record Engagement(string Name, Season TimeOfYear); +``` + +Given the following initialized instance: + +```csharp +var myEngagement = new Engagement("Ski Trip", Season.Winter); +``` + +This would serialize to the following JSON: +```json +{"name": "Ski Trip", "season": 3} +``` + +That might be unexpected that our `Season.Winter` value was represented as a `3`, but this is because the serializer is going to automatically use numeric representations +of the enum values starting with zero for the first value and incrementing the numeric value for each additional value available. Again, if a migration were taking place and +a developer had flipped the order of the enums, this would affect a breaking change in your solution as the serialized numeric values would point to different values when deserialized. + +Rather, there is a `JsonConverter` available with `System.Text.Json` that will instead opt to use a string-based value instead of the numeric value. The `[JsonConverter]` attribute needs +to be applied to be enum type itself to enable this, but will then be realized in any downstream serialization or deserialization operation that references the enum. + +```csharp +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum Season +{ + Spring, + Summer, + Fall, + Winter +} +``` + +Using the same values from our `myEngagement` instance above, this would produce the following JSON instead: + +```json +{"name": "Ski Trip", "season": "Winter"} +``` + +As a result, the enum members can be shifted around without fear of introducing errors during deserialization. + +#### Custom Enumeration Values + +The System.Text.Json serialization platform doesn't, out of the box, support the use of `[EnumMember]` to allow you to change the value of enum that's used during serialization or deserialization, but +there are scenarios where this could be useful. Again, assume that you're tasking with refactoring the solution to apply some better names to your various +enums. You're using the `JsonStringEnumConverter` detailed above so you're saving the name of the enum to value instead of a numeric value, but if you change +the enum name, that will introduce a breaking change as the name will no longer match what's in state. + +Do note that if you opt into using this approach, you should decorate all your enum members with the `[EnumMeber]` attribute so that the values are consistently applied for each enum value instead +of haphazardly. Nothing will validate this at build or runtime, but it is considered a best practice operation. + +How can you specify the precise value persisted while still changing the name of the enum member in this scenario? Use a custom `JsonConverter` with an extension method that can pull the value +out of the attached `[EnumMember]` attributes where provided. Add the following to your solution: + +```csharp +public sealed class EnumMemberJsonConverter : JsonConverter where T : struct, Enum +{ + /// Reads and converts the JSON to type . + /// The reader. + /// The type to convert. + /// An object that specifies serialization options to use. + /// The converted value. + public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + // Get the string value from the JSON reader + var value = reader.GetString(); + + // Loop through all the enum values + foreach (var enumValue in Enum.GetValues()) + { + // Get the value from the EnumMember attribute, if any + var enumMemberValue = GetValueFromEnumMember(enumValue); + + // If the values match, return the enum value + if (value == enumMemberValue) + { + return enumValue; + } + } + + // If no match found, throw an exception + throw new JsonException($"Invalid value for {typeToConvert.Name}: {value}"); + } + + /// Writes a specified value as JSON. + /// The writer to write to. + /// The value to convert to JSON. + /// An object that specifies serialization options to use. + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + // Get the value from the EnumMember attribute, if any + var enumMemberValue = GetValueFromEnumMember(value); + + // Write the value to the JSON writer + writer.WriteStringValue(enumMemberValue); + } + + private static string GetValueFromEnumMember(T value) + { + MemberInfo[] member = typeof(T).GetMember(value.ToString(), BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public); + if (member.Length == 0) + return value.ToString(); + object[] customAttributes = member.GetCustomAttributes(typeof(EnumMemberAttribute), false); + if (customAttributes.Length != 0) + { + EnumMemberAttribute enumMemberAttribute = (EnumMemberAttribute)customAttributes; + if (enumMemberAttribute != null && enumMemberAttribute.Value != null) + return enumMemberAttribute.Value; + } + return value.ToString(); + } +} +``` + +Now let's add a sample enumerator. We'll set a value that uses the lower-case version of each enum member to demonstrate this. Don't forget to decorate the enum with the `JsonConverter` +attribute and reference our custom converter in place of the numeral-to-string converter used in the last section. + +```csharp +[JsonConverter(typeof(EnumMemberJsonConverter))] +public enum Season +{ + [EnumMember(Value="spring")] + Spring, + [EnumMember(Value="summer")] + Summer, + [EnumMember(Value="fall")] + Fall, + [EnumMember(Value="winter")] + Winter +} +``` + +Let's use our sample record from before. We'll also add a `[JsonPropertyName]` attribute just to augment the demonstration: +```csharp +public record Engagement([property: JsonPropertyName("event")] string Name, Season TimeOfYear); +``` + +And finally, let's initialize a new instance of this: + +```csharp +var myEngagement = new Engagement("Conference", Season.Fall); +``` + +This time, serialization will take into account the values from the attached `[EnumMember]` attribute providing us a mechanism to refactor our application without necessitating +a complex versioning scheme for our existing enum values in the state. + +```json +{"event": "Conference", "season": "fall"} +``` + +## Strongly-typed Dapr Actor client +In this section, you will learn how to configure your classes and records so they are properly serialized and deserialized at runtime when using a strongly-typed actor client. These clients are implemented using .NET interfaces and are not compatible with Dapr Actors written using other languages. + +This actor client serializes data using an engine called the [Data Contract Serializer](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/serializable-types) which converts your C# types to and from XML documents. This serialization framework is not specific to Dapr and is separately maintained by the .NET team within the [.NET GitHub repository](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSerializer.cs). + +When sending or receiving primitives (like strings or ints), this serialization happens transparently and there's no requisite preparation needed on your part. However, when working with complex types such as those you create, there are some important rules to take into consideration so this process works smoothly. + +### Serializable Types There are several important considerations to keep in mind when using the Data Contract Serializer: - By default, all types, read/write properties (after construction) and fields marked as publicly visible are serialized @@ -23,14 +271,14 @@ There are several important considerations to keep in mind when using the Data C - Serialization is supported for types that use other complex types that are not themselves marked with the DataContractAttribute attribute through the use of the KnownTypesAttribute attribute - If a type is marked with the DataContractAttribute attribute, all members you wish to serialize and deserialize must be decorated with the DataMemberAttribute attribute as well or they'll be set to their default values -## How does deserialization work? +### How does deserialization work? The approach used for deserialization depends on whether or not the type is decorated with the [DataContractAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.datacontractattribute) attribute. If this attribute isn't present, an instance of the type is created using the parameterless constructor. Each of the properties and fields are then mapped into the type using their respective setters and the instance is returned to the caller. If the type _is_ marked with `[DataContract]`, the serializer instead uses reflection to read the metadata of the type and determine which properties or fields should be included based on whether or not they're marked with the DataMemberAttribute attribute as it's performed on an opt-in basis. It then allocates an uninitialized object in memory (avoiding the use of any constructors, parameterless or not) and then sets the value directly on each mapped property or field, even if private or uses init-only setters. Serialization callbacks are invoked as applicable throughout this process and then the object is returned to the caller. Use of the serialization attributes is highly recommended as they grant more flexibility to override names and namespaces and generally use more of the modern C# functionality. While the default serializer can be relied on for primitive types, it's not recommended for any of your own types, whether they be classes, structs or records. It's recommended that if you decorate a type with the DataContractAttribute attribute, you also explicitly decorate each of the members you want to serialize or deserialize with the DataMemberAttribute attribute as well. -### .NET Classes +#### .NET Classes Classes are fully supported in the Data Contract Serializer provided that that other rules detailed on this page and the [Data Contract Serializer](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/serializable-types) documentation are also followed. The most important thing to remember here is that you must either have a public parameterless constructor or you must decorate it with the appropriate attributes. Let's review some examples to really clarify what will and won't work. @@ -153,7 +401,7 @@ When this is serialized, because we're changing the names of the serialized memb ``` -#### Classes in C# 12 - Primary Constructors +##### Classes in C# 12 - Primary Constructors C# 12 brought us primary constructors on classes. Use of a primary constructor means the compiler will be prevented from creating the default implicit parameterless constructor. While a primary constructor on a class doesn't generate any public properties, it does mean that if you pass this primary constructor any arguments or have non-primitive types in your class, you'll either need to specify your own parameterless constructor or use the serialization attributes. Here's an example where we're using the primary constructor to inject an ILogger to a field and add our own parameterless constructor without the need for any attributes. @@ -198,7 +446,7 @@ public class Doodad(ILogger _logger) } ``` -### .NET Structs +#### .NET Structs Structs are supported by the Data Contract serializer provided that they are marked with the DataContractAttribute attribute and the members you wish to serialize are marked with the DataMemberAttribute attribute. Further, to support deserialization, the struct will also need to have a parameterless constructor. This works even if you define your own parameterless constructor as enabled in C# 10. ```csharp @@ -210,7 +458,7 @@ public struct Doodad } ``` -### .NET Records +#### .NET Records Records were introduced in C# 9 and follow precisely the same rules as classes when it comes to serialization. We recommend that you should decorate all your records with the DataContractAttribute attribute and members you wish to serialize with DataMemberAttribute attributes so you don't experience any deserialization issues using this or other newer C# functionalities. Because record classes use init-only setters for properties by default and encourage the use of the primary constructor, applying these attributes to your types ensures that the serializer can properly otherwise accommodate your types as-is. Typically records are presented as a simple one-line statement using the new primary constructor concept: @@ -238,7 +486,7 @@ public record Doodad( [property: DataMember] int Count) ``` -### Supported Primitive Types +#### Supported Primitive Types There are several types built into .NET that are considered primitive and eligible for serialization without additional effort on the part of the developer: - [Byte](https://learn.microsoft.com/en-us/dotnet/api/system.byte) @@ -267,7 +515,7 @@ There are additional types that aren't actually primitives but have similar buil Again, if you want to pass these types around via your actor methods, no additional consideration is necessary as they'll be serialized and deserialized without issue. Further, types that are themselves marked with the (SerializeableAttribute)[https://learn.microsoft.com/en-us/dotnet/api/system.serializableattribute] attribute will be serialized. -### Enumeration Types +#### Enumeration Types Enumerations, including flag enumerations are serializable if appropriately marked. The enum members you wish to be serialized must be marked with the [EnumMemberAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.enummemberattribute) attribute in order to be serialized. Passing a custom value into the optional Value argument on this attribute will allow you to specify the value used for the member in the serialized document instead of having the serializer derive it from the name of the member. The enum type does not require that the type be decorated with the `DataContractAttribute` attribute - only that the members you wish to serialize be decorated with the `EnumMemberAttribute` attributes. @@ -283,15 +531,15 @@ public enum Colors } ``` -### Collection Types +#### Collection Types With regards to the data contact serializer, all collection types that implement the [IEnumerable](https://learn.microsoft.com/en-us/dotnet/api/system.collections.ienumerable) interface including arays and generic collections are considered collections. Those types that implement [IDictionary](https://learn.microsoft.com/en-us/dotnet/api/system.collections.idictionary) or the generic [IDictionary](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.idictionary-2) are considered dictionary collections; all others are list collections. Not unlike other complex types, collection types must have a parameterless constructor available. Further, they must also have a method called Add so they can be properly serialized and deserialized. The types used by these collection types must themselves be marked with the `DataContractAttribute` attribute or otherwise be serializable as described throughout this document. -### Data Contract Versioning +#### Data Contract Versioning As the data contract serializer is only used in Dapr with respect to serializing the values in the .NET SDK to and from the Dapr actor instances via the proxy methods, there's little need to consider versioning of data contracts as the data isn't being persisted between application versions using the same serializer. For those interested in learning more about data contract versioning visit [here](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/data-contract-versioning). -### Known Types +#### Known Types Nesting your own complex types is easily accommodated by marking each of the types with the [DataContractAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.datacontractattribute) attribute. This informs the serializer as to how deserialization should be performed. But what if you're working with polymorphic types and one of your members is a base class or interface with derived classes or other implementations? Here, you'll use the [KnownTypeAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.knowntypeattribute) attribute to give a hint to the serializer about how to proceed. From 5548c670f480a9eb7c13efb35899ef959636f5dd Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Fri, 18 Oct 2024 03:20:01 -0500 Subject: [PATCH 27/30] Prioritize retrieval of environment variables from IConfiguration instead of directly (#1363) * Implemented against Dapr.Client.AspNetCore and Dapr.Client Signed-off-by: Whit Waldo * SImplified DaprWorkflow DI registration and updated to use IConfiguration preference. Needs testing. Signed-off-by: Whit Waldo * Added missing copyright header Signed-off-by: Whit Waldo * Updated actor registration to prefer the updated IConfiguration-based approach for pulling the HTTP endpoint and API token Signed-off-by: Whit Waldo * Adopted accepted proposal's guidelines for favoring different environment variables for determining the sidecar endpoint. Added notes to explain this in the code going forward. Signed-off-by: Whit Waldo * Made some lines a little more concise, added hostname default to DaprDefaults to use when building endpoints. Signed-off-by: Whit Waldo * Fixed and updated unit tests Signed-off-by: Whit Waldo * Updated to put endpoint resolution mechanism in DaprDefaults within Dapr.Common - updating projects and unit tests Signed-off-by: Whit Waldo * Updated packages to fix security advisory https://github.com/advisories/GHSA-447r-wph3-92pm Signed-off-by: Whit Waldo * Updated Workflow builder to use DaprDefaults with IConfiguration Signed-off-by: Whit Waldo * Updating global.json Signed-off-by: Whit Waldo * Tweaked global.json comment Signed-off-by: Whit Waldo * Adding braces per nit Signed-off-by: Whit Waldo * Consolidated both registration extension methods to remove duplication Signed-off-by: Whit Waldo --------- Signed-off-by: Whit Waldo --- Directory.Packages.props | 3 + all.sln | 7 + global.json | 6 +- .../ActorsServiceCollectionExtensions.cs | 28 +- .../Dapr.Actors.AspNetCore.csproj | 1 + src/Dapr.Actors/Client/ActorProxyOptions.cs | 2 +- src/Dapr.Actors/Dapr.Actors.csproj | 1 + .../Runtime/ActorRuntimeOptions.cs | 22 +- .../DaprAuthenticationOptions.cs | 3 +- .../DaprServiceCollectionExtensions.cs | 101 +-- src/Dapr.Client/DaprClient.cs | 11 +- src/Dapr.Client/DaprClientBuilder.cs | 2 +- src/Dapr.Client/InvocationHandler.cs | 2 +- src/Dapr.Common/Dapr.Common.csproj | 2 + src/Dapr.Common/DaprDefaults.cs | 131 ++-- .../DaprWorkflowClientBuilderFactory.cs | 95 +++ .../WorkflowServiceCollectionExtensions.cs | 135 +--- .../Dapr.Actors.Generators.Test.csproj | 1 + .../DaprServiceCollectionExtensionsTest.cs | 25 +- test/Dapr.Common.Test/Dapr.Common.Test.csproj | 23 + test/Dapr.Common.Test/DaprDefaultTest.cs | 582 ++++++++++++++++++ 21 files changed, 918 insertions(+), 265 deletions(-) create mode 100644 src/Dapr.Workflow/DaprWorkflowClientBuilderFactory.cs create mode 100644 test/Dapr.Common.Test/Dapr.Common.Test.csproj create mode 100644 test/Dapr.Common.Test/DaprDefaultTest.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index d85020770..842c82aa8 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,6 +5,7 @@ + @@ -26,6 +27,7 @@ + @@ -39,6 +41,7 @@ + diff --git a/all.sln b/all.sln index 1a5d78efb..85ed848a4 100644 --- a/all.sln +++ b/all.sln @@ -122,6 +122,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Protos", "src\Dapr.Pro EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Common", "src\Dapr.Common\Dapr.Common.csproj", "{B445B19C-A925-4873-8CB7-8317898B6970}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Common.Test", "test\Dapr.Common.Test\Dapr.Common.Test.csproj", "{CDB47863-BEBD-4841-A807-46D868962521}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -302,6 +304,10 @@ Global {B445B19C-A925-4873-8CB7-8317898B6970}.Debug|Any CPU.Build.0 = Debug|Any CPU {B445B19C-A925-4873-8CB7-8317898B6970}.Release|Any CPU.ActiveCfg = Release|Any CPU {B445B19C-A925-4873-8CB7-8317898B6970}.Release|Any CPU.Build.0 = Release|Any CPU + {CDB47863-BEBD-4841-A807-46D868962521}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CDB47863-BEBD-4841-A807-46D868962521}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CDB47863-BEBD-4841-A807-46D868962521}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CDB47863-BEBD-4841-A807-46D868962521}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -357,6 +363,7 @@ Global {C74FBA78-13E8-407F-A173-4555AEE41FF3} = {A7F41094-8648-446B-AECD-DCC2CC871F73} {DFBABB04-50E9-42F6-B470-310E1B545638} = {27C5D71D-0721-4221-9286-B94AB07B58CF} {B445B19C-A925-4873-8CB7-8317898B6970} = {27C5D71D-0721-4221-9286-B94AB07B58CF} + {CDB47863-BEBD-4841-A807-46D868962521} = {DD020B34-460F-455F-8D17-CF4A949F100B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} diff --git a/global.json b/global.json index 980f4652d..fe53f92ae 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,7 @@ { - "_comment": "This policy allows the 7.0.101 SDK or patches in that family.", + "_comment": "This policy allows the 8.0.100 SDK or patches in that family.", "sdk": { - "version": "7.0.101", - "rollForward": "latestMajor" + "version": "8.0.100", + "rollForward": "minor" } } \ No newline at end of file diff --git a/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs b/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs index a7f5d04a2..11f05f4c1 100644 --- a/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs +++ b/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2021 The Dapr 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,13 @@ // limitations under the License. // ------------------------------------------------------------------------ +#nullable enable + using System; +using Dapr; using Dapr.Actors.Client; using Dapr.Actors.Runtime; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -30,12 +34,9 @@ public static class ActorsServiceCollectionExtensions /// /// The . /// A delegate used to configure actor options and register actor types. - public static void AddActors(this IServiceCollection services, Action configure) + public static void AddActors(this IServiceCollection? services, Action? configure) { - if (services is null) - { - throw new ArgumentNullException(nameof(services)); - } + ArgumentNullException.ThrowIfNull(services, nameof(services)); // Routing and health checks are required dependencies. services.AddRouting(); @@ -45,6 +46,8 @@ public static void AddActors(this IServiceCollection services, Action(s => { var options = s.GetRequiredService>().Value; + ConfigureActorOptions(s, options); + var loggerFactory = s.GetRequiredService(); var activatorFactory = s.GetRequiredService(); var proxyFactory = s.GetRequiredService(); @@ -54,6 +57,8 @@ public static void AddActors(this IServiceCollection services, Action(s => { var options = s.GetRequiredService>().Value; + ConfigureActorOptions(s, options); + var factory = new ActorProxyFactory() { DefaultOptions = @@ -72,5 +77,16 @@ public static void AddActors(this IServiceCollection services, Action(configure); } } + + private static void ConfigureActorOptions(IServiceProvider serviceProvider, ActorRuntimeOptions options) + { + var configuration = serviceProvider.GetService(); + options.DaprApiToken = !string.IsNullOrWhiteSpace(options.DaprApiToken) + ? options.DaprApiToken + : DaprDefaults.GetDefaultDaprApiToken(configuration); + options.HttpEndpoint = !string.IsNullOrWhiteSpace(options.HttpEndpoint) + ? options.HttpEndpoint + : DaprDefaults.GetDefaultHttpEndpoint(); + } } } diff --git a/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj b/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj index 82c5863db..ef57e76fe 100644 --- a/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj +++ b/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj @@ -11,5 +11,6 @@ + diff --git a/src/Dapr.Actors/Client/ActorProxyOptions.cs b/src/Dapr.Actors/Client/ActorProxyOptions.cs index 665a1dced..2afce852c 100644 --- a/src/Dapr.Actors/Client/ActorProxyOptions.cs +++ b/src/Dapr.Actors/Client/ActorProxyOptions.cs @@ -45,7 +45,7 @@ public JsonSerializerOptions JsonSerializerOptions /// /// The Dapr Api Token that is added to the header for all requests. /// - public string DaprApiToken { get; set; } = DaprDefaults.GetDefaultDaprApiToken(); + public string DaprApiToken { get; set; } = DaprDefaults.GetDefaultDaprApiToken(null); /// /// Gets or sets the HTTP endpoint URI used to communicate with the Dapr sidecar. diff --git a/src/Dapr.Actors/Dapr.Actors.csproj b/src/Dapr.Actors/Dapr.Actors.csproj index 54d3487b8..bcb8d830f 100644 --- a/src/Dapr.Actors/Dapr.Actors.csproj +++ b/src/Dapr.Actors/Dapr.Actors.csproj @@ -13,6 +13,7 @@ + diff --git a/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs b/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs index 62eaceea6..21c302018 100644 --- a/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs +++ b/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs @@ -11,6 +11,8 @@ // limitations under the License. // ------------------------------------------------------------------------ +#nullable enable + using System; using System.Text.Json; @@ -34,7 +36,7 @@ public sealed class ActorRuntimeOptions }; private bool useJsonSerialization = false; private JsonSerializerOptions jsonSerializerOptions = JsonSerializerDefaults.Web; - private string daprApiToken = DaprDefaults.GetDefaultDaprApiToken(); + private string daprApiToken = string.Empty; private int? remindersStoragePartitions = null; /// @@ -180,19 +182,14 @@ public JsonSerializerOptions JsonSerializerOptions set { - if (value is null) - { - throw new ArgumentNullException(nameof(JsonSerializerOptions), $"{nameof(ActorRuntimeOptions)}.{nameof(JsonSerializerOptions)} cannot be null"); - } - - this.jsonSerializerOptions = value; + this.jsonSerializerOptions = value ?? throw new ArgumentNullException(nameof(JsonSerializerOptions), $"{nameof(ActorRuntimeOptions)}.{nameof(JsonSerializerOptions)} cannot be null"); } } /// /// The to add to the headers in requests to Dapr runtime /// - public string DaprApiToken + public string? DaprApiToken { get { @@ -201,12 +198,7 @@ public string DaprApiToken set { - if (value is null) - { - throw new ArgumentNullException(nameof(DaprApiToken), $"{nameof(ActorRuntimeOptions)}.{nameof(DaprApiToken)} cannot be null"); - } - - this.daprApiToken = value; + this.daprApiToken = value ?? throw new ArgumentNullException(nameof(DaprApiToken), $"{nameof(ActorRuntimeOptions)}.{nameof(DaprApiToken)} cannot be null"); } } @@ -241,6 +233,6 @@ public int? RemindersStoragePartitions /// corresponding environment variables. /// /// - public string HttpEndpoint { get; set; } = DaprDefaults.GetDefaultHttpEndpoint(); + public string? HttpEndpoint { get; set; } } } diff --git a/src/Dapr.AspNetCore/DaprAuthenticationOptions.cs b/src/Dapr.AspNetCore/DaprAuthenticationOptions.cs index 4e073d06c..b12d4d14e 100644 --- a/src/Dapr.AspNetCore/DaprAuthenticationOptions.cs +++ b/src/Dapr.AspNetCore/DaprAuthenticationOptions.cs @@ -11,7 +11,6 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; using Microsoft.AspNetCore.Authentication; namespace Dapr.AspNetCore @@ -29,6 +28,6 @@ public class DaprAuthenticationOptions : AuthenticationSchemeOptions /// Gets or sets the App API token. /// By default, the token will be read from the APP_API_TOKEN environment variable. /// - public string Token { get; set; } = DaprDefaults.GetDefaultAppApiToken(); + public string Token { get; set; } = DaprDefaults.GetDefaultAppApiToken(null); } } diff --git a/src/Dapr.AspNetCore/DaprServiceCollectionExtensions.cs b/src/Dapr.AspNetCore/DaprServiceCollectionExtensions.cs index 388015b80..52e9110be 100644 --- a/src/Dapr.AspNetCore/DaprServiceCollectionExtensions.cs +++ b/src/Dapr.AspNetCore/DaprServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2021 The Dapr Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,55 +11,76 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Microsoft.Extensions.DependencyInjection -{ - using System; - using System.Linq; - using Dapr.Client; - using Extensions; +#nullable enable + +namespace Microsoft.Extensions.DependencyInjection; + +using System; +using Dapr; +using Dapr.Client; +using Extensions; +using Configuration; +/// +/// Provides extension methods for . +/// +public static class DaprServiceCollectionExtensions +{ /// - /// Provides extension methods for . + /// Adds Dapr client services to the provided . This does not include integration + /// with ASP.NET Core MVC. Use the AddDapr() extension method on IMvcBuilder to register MVC integration. /// - public static class DaprServiceCollectionExtensions + /// The . + /// + public static void AddDaprClient(this IServiceCollection services, Action? configure = null) { - /// - /// Adds Dapr client services to the provided . This does not include integration - /// with ASP.NET Core MVC. Use the AddDapr() extension method on IMvcBuilder to register MVC integration. - /// - /// The . - /// - public static void AddDaprClient(this IServiceCollection services, Action configure = null) + ArgumentNullException.ThrowIfNull(services, nameof(services)); + + services.TryAddSingleton(serviceProvider => { - ArgumentNullException.ThrowIfNull(services, nameof(services)); + var builder = CreateDaprClientBuilder(serviceProvider); + configure?.Invoke(builder); + return builder.Build(); + }); + } - services.TryAddSingleton(_ => - { - var builder = new DaprClientBuilder(); - configure?.Invoke(builder); + /// + /// Adds Dapr client services to the provided . This does not include integration + /// with ASP.NET Core MVC. Use the AddDapr() extension method on IMvcBuilder to register MVC integration. + /// + /// The . + /// + public static void AddDaprClient(this IServiceCollection services, + Action configure) + { + ArgumentNullException.ThrowIfNull(services, nameof(services)); - return builder.Build(); - }); - } - - /// - /// Adds Dapr client services to the provided . This does not include integration - /// with ASP.NET Core MVC. Use the AddDapr() extension method on IMvcBuilder to register MVC integration. - /// - /// The . - /// - public static void AddDaprClient(this IServiceCollection services, - Action configure) + services.TryAddSingleton(serviceProvider => { - ArgumentNullException.ThrowIfNull(services, nameof(services)); + var builder = CreateDaprClientBuilder(serviceProvider); + configure?.Invoke(serviceProvider, builder); + return builder.Build(); + }); + } + + private static DaprClientBuilder CreateDaprClientBuilder(IServiceProvider serviceProvider) + { + var builder = new DaprClientBuilder(); + var configuration = serviceProvider.GetService(); + + // Set the HTTP endpoint, if provided, else use the default endpoint + builder.UseHttpEndpoint(DaprDefaults.GetDefaultHttpEndpoint(configuration)); - services.TryAddSingleton(serviceProvider => - { - var builder = new DaprClientBuilder(); - configure?.Invoke(serviceProvider, builder); + // Set the gRPC endpoint, if provided + builder.UseGrpcEndpoint(DaprDefaults.GetDefaultGrpcEndpoint(configuration)); - return builder.Build(); - }); + // Set the API token, if provided + var apiToken = DaprDefaults.GetDefaultDaprApiToken(configuration); + if (!string.IsNullOrWhiteSpace(apiToken)) + { + builder.UseDaprApiToken(apiToken); } + + return builder; } } diff --git a/src/Dapr.Client/DaprClient.cs b/src/Dapr.Client/DaprClient.cs index 9f107578f..43c640a69 100644 --- a/src/Dapr.Client/DaprClient.cs +++ b/src/Dapr.Client/DaprClient.cs @@ -139,17 +139,14 @@ public static HttpClient CreateInvokeHttpClient(string appId = null, string dapr public static CallInvoker CreateInvocationInvoker(string appId, string daprEndpoint = null, string daprApiToken = null) { var channel = GrpcChannel.ForAddress(daprEndpoint ?? DaprDefaults.GetDefaultGrpcEndpoint()); - return channel.Intercept(new InvocationInterceptor(appId, daprApiToken ?? DaprDefaults.GetDefaultDaprApiToken())); + return channel.Intercept(new InvocationInterceptor(appId, daprApiToken ?? DaprDefaults.GetDefaultDaprApiToken(null))); } internal static KeyValuePair? GetDaprApiTokenHeader(string apiToken) { - if (string.IsNullOrWhiteSpace(apiToken)) - { - return null; - } - - return new KeyValuePair("dapr-api-token", apiToken); + return string.IsNullOrWhiteSpace(apiToken) + ? null + : new KeyValuePair("dapr-api-token", apiToken); } /// diff --git a/src/Dapr.Client/DaprClientBuilder.cs b/src/Dapr.Client/DaprClientBuilder.cs index 50a4979d1..68315c45b 100644 --- a/src/Dapr.Client/DaprClientBuilder.cs +++ b/src/Dapr.Client/DaprClientBuilder.cs @@ -40,7 +40,7 @@ public DaprClientBuilder() }; this.JsonSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web); - this.DaprApiToken = DaprDefaults.GetDefaultDaprApiToken(); + this.DaprApiToken = DaprDefaults.GetDefaultDaprApiToken(null); } // property exposed for testing purposes diff --git a/src/Dapr.Client/InvocationHandler.cs b/src/Dapr.Client/InvocationHandler.cs index 1b55436aa..36fd6b77f 100644 --- a/src/Dapr.Client/InvocationHandler.cs +++ b/src/Dapr.Client/InvocationHandler.cs @@ -51,7 +51,7 @@ public class InvocationHandler : DelegatingHandler public InvocationHandler() { this.parsedEndpoint = new Uri(DaprDefaults.GetDefaultHttpEndpoint(), UriKind.Absolute); - this.apiToken = DaprDefaults.GetDefaultDaprApiToken(); + this.apiToken = DaprDefaults.GetDefaultDaprApiToken(null); } /// diff --git a/src/Dapr.Common/Dapr.Common.csproj b/src/Dapr.Common/Dapr.Common.csproj index ea3e8ae84..31af3952c 100644 --- a/src/Dapr.Common/Dapr.Common.csproj +++ b/src/Dapr.Common/Dapr.Common.csproj @@ -9,6 +9,8 @@ + + diff --git a/src/Dapr.Common/DaprDefaults.cs b/src/Dapr.Common/DaprDefaults.cs index 575a3c148..85a4b18c8 100644 --- a/src/Dapr.Common/DaprDefaults.cs +++ b/src/Dapr.Common/DaprDefaults.cs @@ -11,6 +11,8 @@ // limitations under the License. // ------------------------------------------------------------------------ +using Microsoft.Extensions.Configuration; + namespace Dapr { internal static class DaprDefaults @@ -20,82 +22,111 @@ internal static class DaprDefaults private static string daprApiToken = string.Empty; private static string appApiToken = string.Empty; + public const string DaprApiTokenName = "DAPR_API_TOKEN"; + public const string AppApiTokenName = "APP_API_TOKEN"; + public const string DaprHttpEndpointName = "DAPR_HTTP_ENDPOINT"; + public const string DaprHttpPortName = "DAPR_HTTP_PORT"; + public const string DaprGrpcEndpointName = "DAPR_GRPC_ENDPOINT"; + public const string DaprGrpcPortName = "DAPR_GRPC_PORT"; + + public const string DefaultDaprScheme = "http"; + public const string DefaultDaprHost = "localhost"; + public const int DefaultHttpPort = 3500; + public const int DefaultGrpcPort = 50001; + /// /// Get the value of environment variable DAPR_API_TOKEN /// + /// The optional to pull the value from. /// The value of environment variable DAPR_API_TOKEN - public static string GetDefaultDaprApiToken() - { - // Lazy-init is safe because this is just populating the default - // We don't plan to support the case where the user changes environment variables - // for a running process. - if (string.IsNullOrEmpty(daprApiToken)) - { - // Treat empty the same as null since it's an environment variable - var value = Environment.GetEnvironmentVariable("DAPR_API_TOKEN"); - daprApiToken = string.IsNullOrEmpty(value) ? string.Empty : value; - } - - return daprApiToken; - } + public static string GetDefaultDaprApiToken(IConfiguration? configuration) => + GetResourceValue(configuration, DaprApiTokenName) ?? string.Empty; /// /// Get the value of environment variable APP_API_TOKEN /// + /// The optional to pull the value from. /// The value of environment variable APP_API_TOKEN - public static string GetDefaultAppApiToken() - { - if (string.IsNullOrEmpty(appApiToken)) - { - var value = Environment.GetEnvironmentVariable("APP_API_TOKEN"); - appApiToken = string.IsNullOrEmpty(value) ? string.Empty : value; - } - - return appApiToken; - } + public static string GetDefaultAppApiToken(IConfiguration? configuration) => + GetResourceValue(configuration, AppApiTokenName) ?? string.Empty; /// /// Get the value of HTTP endpoint based off environment variables /// + /// The optional to pull the value from. /// The value of HTTP endpoint based off environment variables - public static string GetDefaultHttpEndpoint() + public static string GetDefaultHttpEndpoint(IConfiguration? configuration = null) { - if (string.IsNullOrEmpty(httpEndpoint)) - { - var endpoint = Environment.GetEnvironmentVariable("DAPR_HTTP_ENDPOINT"); - if (!string.IsNullOrEmpty(endpoint)) { - httpEndpoint = endpoint; - return httpEndpoint; - } - - var port = Environment.GetEnvironmentVariable("DAPR_HTTP_PORT"); - port = string.IsNullOrEmpty(port) ? "3500" : port; - httpEndpoint = $"http://127.0.0.1:{port}"; - } + //Prioritize pulling from the IConfiguration and fallback to the environment variable if not populated + var endpoint = GetResourceValue(configuration, DaprHttpEndpointName); + var port = GetResourceValue(configuration, DaprHttpPortName); + + //Use the default HTTP port if we're unable to retrieve/parse the provided port + int? parsedGrpcPort = string.IsNullOrWhiteSpace(port) ? DefaultHttpPort : int.Parse(port); - return httpEndpoint; + return BuildEndpoint(endpoint, parsedGrpcPort.Value); } /// /// Get the value of gRPC endpoint based off environment variables /// + /// The optional to pull the value from. /// The value of gRPC endpoint based off environment variables - public static string GetDefaultGrpcEndpoint() + public static string GetDefaultGrpcEndpoint(IConfiguration? configuration = null) { - if (string.IsNullOrEmpty(grpcEndpoint)) - { - var endpoint = Environment.GetEnvironmentVariable("DAPR_GRPC_ENDPOINT"); - if (!string.IsNullOrEmpty(endpoint)) { - grpcEndpoint = endpoint; - return grpcEndpoint; - } + //Prioritize pulling from the IConfiguration and fallback to the environment variable if not populated + var endpoint = GetResourceValue(configuration, DaprGrpcEndpointName); + var port = GetResourceValue(configuration, DaprGrpcPortName); + + //Use the default gRPC port if we're unable to retrieve/parse the provided port + int? parsedGrpcPort = string.IsNullOrWhiteSpace(port) ? DefaultGrpcPort : int.Parse(port); + + return BuildEndpoint(endpoint, parsedGrpcPort.Value); + } + + /// + /// Builds the Dapr endpoint. + /// + /// The endpoint value. + /// The endpoint port value, whether pulled from configuration/envvar or the default. + /// A constructed endpoint value. + private static string BuildEndpoint(string? endpoint, int endpointPort) + { + var endpointBuilder = new UriBuilder { Scheme = DefaultDaprScheme, Host = DefaultDaprHost }; //Port depends on endpoint - var port = Environment.GetEnvironmentVariable("DAPR_GRPC_PORT"); - port = string.IsNullOrEmpty(port) ? "50001" : port; - grpcEndpoint = $"http://127.0.0.1:{port}"; + if (!string.IsNullOrWhiteSpace(endpoint)) //If the endpoint is set, it doesn't matter if the port is + { + //Extract the scheme, host and port from the endpoint and replace defaults + var uri = new Uri(endpoint); + endpointBuilder.Scheme = uri.Scheme; + endpointBuilder.Host = uri.Host; + endpointBuilder.Port = uri.Port; } + else + { + //Should only set the port if the endpoint isn't populated + endpointBuilder.Port = endpointPort; + } + + return endpointBuilder.ToString(); + } + + /// + /// Retrieves the specified value prioritizing pulling it from , falling back + /// to an environment variable, and using an empty string as a default. + /// + /// An instance of an . + /// The name of the value to retrieve. + /// The value of the resource. + private static string? GetResourceValue(IConfiguration? configuration, string name) + { + //Attempt to retrieve first from the configuration + var configurationValue = configuration?[name]; + if (configurationValue is not null) + return configurationValue; - return grpcEndpoint; + //Fall back to the environment variable with the same name or default to an empty string + return Environment.GetEnvironmentVariable(name); } } } diff --git a/src/Dapr.Workflow/DaprWorkflowClientBuilderFactory.cs b/src/Dapr.Workflow/DaprWorkflowClientBuilderFactory.cs new file mode 100644 index 000000000..7a854cf05 --- /dev/null +++ b/src/Dapr.Workflow/DaprWorkflowClientBuilderFactory.cs @@ -0,0 +1,95 @@ +// ------------------------------------------------------------------------ +// Copyright 2024 The Dapr 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 System; +using System.Net.Http; +using Grpc.Net.Client; +using Microsoft.DurableTask.Client; +using Microsoft.DurableTask.Worker; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +#nullable enable + +namespace Dapr.Workflow; + +/// +/// A factory for building a . +/// +internal sealed class DaprWorkflowClientBuilderFactory +{ + private readonly IConfiguration _configuration; + private readonly IHttpClientFactory _httpClientFactory; + private readonly IServiceCollection _services; + + /// + /// Constructor used to inject the required types into the factory. + /// + public DaprWorkflowClientBuilderFactory(IConfiguration configuration, IHttpClientFactory httpClientFactory, IServiceCollection services) + { + _configuration = configuration; + _httpClientFactory = httpClientFactory; + _services = services; + } + + /// + /// Responsible for building the client itself. + /// + /// + public void CreateClientBuilder(Action configure) + { + _services.AddDurableTaskClient(builder => + { + var apiToken = DaprDefaults.GetDefaultDaprApiToken(_configuration); + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(_configuration); + + var httpClient = _httpClientFactory.CreateClient(); + + if (!string.IsNullOrWhiteSpace(apiToken)) + { + httpClient.DefaultRequestHeaders.Add( "Dapr-Api-Token", apiToken); + } + + builder.UseGrpc(GrpcChannel.ForAddress(grpcEndpoint, new GrpcChannelOptions { HttpClient = httpClient })); + builder.RegisterDirectly(); + }); + + _services.AddDurableTaskWorker(builder => + { + WorkflowRuntimeOptions options = new(); + configure?.Invoke(options); + + var apiToken = DaprDefaults.GetDefaultDaprApiToken(_configuration); + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(_configuration); + + if (!string.IsNullOrEmpty(grpcEndpoint)) + { + var httpClient = _httpClientFactory.CreateClient(); + + if (!string.IsNullOrWhiteSpace(apiToken)) + { + httpClient.DefaultRequestHeaders.Add("Dapr-Api-Token", apiToken); + } + + builder.UseGrpc( + GrpcChannel.ForAddress(grpcEndpoint, new GrpcChannelOptions { HttpClient = httpClient })); + } + else + { + builder.UseGrpc(); + } + + builder.AddTasks(registry => options.AddWorkflowsAndActivitiesToRegistry(registry)); + }); + } +} diff --git a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs index ca514f221..3c19583aa 100644 --- a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs +++ b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs @@ -14,20 +14,14 @@ namespace Dapr.Workflow { using System; - using Grpc.Net.Client; - using Microsoft.DurableTask.Client; - using Microsoft.DurableTask.Worker; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; - using System.Net.Http; - using Dapr; /// /// Contains extension methods for using Dapr Workflow with dependency injection. /// public static class WorkflowServiceCollectionExtensions { - /// /// Adds Dapr Workflow support to the service collection. /// @@ -43,6 +37,7 @@ public static IServiceCollection AddDaprWorkflow( } serviceCollection.TryAddSingleton(); + serviceCollection.AddHttpClient(); #pragma warning disable CS0618 // Type or member is obsolete - keeping around temporarily - replaced by DaprWorkflowClient serviceCollection.TryAddSingleton(); @@ -50,135 +45,17 @@ public static IServiceCollection AddDaprWorkflow( serviceCollection.AddHostedService(); serviceCollection.TryAddSingleton(); serviceCollection.AddDaprClient(); - serviceCollection.AddDaprWorkflowClient(); - + serviceCollection.AddOptions().Configure(configure); - serviceCollection.AddDurableTaskWorker(builder => + serviceCollection.AddSingleton(c => { - WorkflowRuntimeOptions options = new(); - configure?.Invoke(options); - - if (TryGetGrpcAddress(out string address)) - { - var daprApiToken = DaprDefaults.GetDefaultDaprApiToken(); - if (!string.IsNullOrEmpty(daprApiToken)) - { - var client = new HttpClient(); - client.DefaultRequestHeaders.Add("Dapr-Api-Token", daprApiToken); - builder.UseGrpc(CreateChannel(address, client)); - } - else - { - builder.UseGrpc(address); - } - - } - else - { - builder.UseGrpc(); - } - - builder.AddTasks(registry => options.AddWorkflowsAndActivitiesToRegistry(registry)); + var factory = c.GetRequiredService(); + factory.CreateClientBuilder(configure); + return new object(); //Placeholder as actual registration is performed inside factory }); return serviceCollection; } - - /// - /// Adds Dapr Workflow client support to the service collection. - /// - /// - /// Use this extension method if you want to use in your app - /// but don't wish to define any workflows or activities. - /// - /// The . - public static IServiceCollection AddDaprWorkflowClient(this IServiceCollection services) - { - services.TryAddSingleton(); - services.AddDurableTaskClient(builder => - { - if (TryGetGrpcAddress(out string address)) - { - var daprApiToken = DaprDefaults.GetDefaultDaprApiToken(); - if (!string.IsNullOrEmpty(daprApiToken)) - { - var client = new HttpClient(); - client.DefaultRequestHeaders.Add("Dapr-Api-Token", daprApiToken); - builder.UseGrpc(CreateChannel(address, client)); - } - else - { - builder.UseGrpc(address); - } - - } - else - { - builder.UseGrpc(); - } - - builder.RegisterDirectly(); - }); - - return services; - } - - static bool TryGetGrpcAddress(out string address) - { - // TODO: Ideally we should be using DaprDefaults.cs for this. However, there are two blockers: - // 1. DaprDefaults.cs uses 127.0.0.1 instead of localhost, which prevents testing with Dapr on WSL2 and the app on Windows - // 2. DaprDefaults.cs doesn't compile when the project has C# nullable reference types enabled. - // If the above issues are fixed (ensuring we don't regress anything) we should switch to using the logic in DaprDefaults.cs. - var daprEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(); - if (!String.IsNullOrEmpty(daprEndpoint)) { - address = daprEndpoint; - return true; - } - - var daprPortStr = Environment.GetEnvironmentVariable("DAPR_GRPC_PORT"); - if (int.TryParse(daprPortStr, out int daprGrpcPort)) - { - // There is a bug in the Durable Task SDK that requires us to change the format of the address - // depending on the version of .NET that we're targeting. For now, we work around this manually. -#if NET6_0_OR_GREATER - address = $"http://localhost:{daprGrpcPort}"; -#else - address = $"localhost:{daprGrpcPort}"; -#endif - return true; - } - - address = string.Empty; - return false; - } - - static GrpcChannel CreateChannel(string address, HttpClient client) - { - - GrpcChannelOptions options = new() { HttpClient = client}; - var daprEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(); - if (!String.IsNullOrEmpty(daprEndpoint)) { - return GrpcChannel.ForAddress(daprEndpoint, options); - } - - var daprPortStr = Environment.GetEnvironmentVariable("DAPR_GRPC_PORT"); - if (int.TryParse(daprPortStr, out int daprGrpcPort)) - { - // If there is no address passed in, we default to localhost - if (String.IsNullOrEmpty(address)) - { - // There is a bug in the Durable Task SDK that requires us to change the format of the address - // depending on the version of .NET that we're targeting. For now, we work around this manually. - #if NET6_0_OR_GREATER - address = $"http://localhost:{daprGrpcPort}"; - #else - address = $"localhost:{daprGrpcPort}"; - #endif - } - - } - return GrpcChannel.ForAddress(address, options); - } } } diff --git a/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj b/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj index 91c7e8b42..80a79cafe 100644 --- a/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj +++ b/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj @@ -27,6 +27,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + diff --git a/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs b/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs index a82948cf3..4a340e22a 100644 --- a/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs +++ b/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2021 The Dapr 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 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; +#nullable enable + +using System; using System.Text.Json; using Dapr.Client; using Microsoft.Extensions.DependencyInjection; @@ -43,15 +45,16 @@ public void AddDaprClient_RegistersDaprClientOnlyOnce() var serviceProvider = services.BuildServiceProvider(); - DaprClientGrpc daprClient = serviceProvider.GetService() as DaprClientGrpc; + DaprClientGrpc? daprClient = serviceProvider.GetService() as DaprClientGrpc; - Assert.True(daprClient.JsonSerializerOptions.PropertyNameCaseInsensitive); + Assert.NotNull(daprClient); + Assert.True(daprClient?.JsonSerializerOptions.PropertyNameCaseInsensitive); } [Fact] public void AddDaprClient_RegistersUsingDependencyFromIServiceProvider() { - + var services = new ServiceCollection(); services.AddSingleton(); services.AddDaprClient((provider, builder) => @@ -66,13 +69,15 @@ public void AddDaprClient_RegistersUsingDependencyFromIServiceProvider() }); var serviceProvider = services.BuildServiceProvider(); - - DaprClientGrpc client = serviceProvider.GetRequiredService() as DaprClientGrpc; - + + DaprClientGrpc? client = serviceProvider.GetRequiredService() as DaprClientGrpc; + //Registers with case-insensitive as true by default, but we set as false above - Assert.False(client.JsonSerializerOptions.PropertyNameCaseInsensitive); + Assert.NotNull(client); + Assert.False(client?.JsonSerializerOptions.PropertyNameCaseInsensitive); } + #if NET8_0_OR_GREATER [Fact] public void AddDaprClient_WithKeyedServices() @@ -90,7 +95,7 @@ public void AddDaprClient_WithKeyedServices() Assert.NotNull(daprClient); } #endif - + private class TestConfigurationProvider { public bool GetCaseSensitivity() => false; diff --git a/test/Dapr.Common.Test/Dapr.Common.Test.csproj b/test/Dapr.Common.Test/Dapr.Common.Test.csproj new file mode 100644 index 000000000..6e34c3a7a --- /dev/null +++ b/test/Dapr.Common.Test/Dapr.Common.Test.csproj @@ -0,0 +1,23 @@ + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/test/Dapr.Common.Test/DaprDefaultTest.cs b/test/Dapr.Common.Test/DaprDefaultTest.cs new file mode 100644 index 000000000..ef4d0da3c --- /dev/null +++ b/test/Dapr.Common.Test/DaprDefaultTest.cs @@ -0,0 +1,582 @@ +using System; +using Microsoft.Extensions.Configuration; +using Xunit; + +namespace Dapr.Common.Test; + +public class DaprDefaultTest +{ + [Fact] + public void ShouldBuildHttpEndpointAndPortUsingPrefixedConfiguration() + { + const string endpointVarName = "test_DAPR_HTTP_ENDPOINT"; + const string portVarName = "test_DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + const string prefix = "test_"; + + Environment.SetEnvironmentVariable(endpointVarName, "https://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, null); //Will use 443 from the endpoint instead + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(prefix); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("https://dapr.io:443/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointAndPortUsingConfiguration() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, null); //Will use 443 from the endpoint instead + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("https://dapr.io:443/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointUsingPrefixedConfiguration() + { + const string endpointVarName = "test_DAPR_HTTP_ENDPOINT"; + const string portVarName = "test_DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + const string prefix = "test_"; + + Environment.SetEnvironmentVariable(endpointVarName, "https://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "2569"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(prefix); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("https://dapr.io:443/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointUsingConfiguration() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "2569"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("https://dapr.io:443/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointUsingOnlyPortConfiguration() + { + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(portVarName, "2569"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("http://localhost:2569/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointUsingEnvVarValues() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "http://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "2569"); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("http://dapr.io:80/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointUsingMixedValues() + { + const string endpointVarName = "test_DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "2569"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + configurationBuilder.AddEnvironmentVariables("test_"); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("https://dapr.io:443/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldDefaultToEmptyHttpEndpoint() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, null); + Environment.SetEnvironmentVariable(portVarName, null); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("http://localhost:3500/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldDefaultToLocalhostWithPort() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, null); + Environment.SetEnvironmentVariable(portVarName, "7256"); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("http://localhost:7256/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldDefaultToLocalhostWithDefaultPort() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, null); + Environment.SetEnvironmentVariable(portVarName, null); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("http://localhost:3500/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointAndPortUsingPrefixedConfiguration() + { + const string endpointVarName = "test_DAPR_GRPC_ENDPOINT"; + const string portVarName = "test_DAPR_GRPC_PORT"; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_GrpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + const string prefix = "test_"; + + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "2570"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(prefix); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_GrpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointAndPortUsingConfiguration() + { + const string endpointVarName = DaprDefaults.DaprGrpcEndpointName; + const string portVarName = DaprDefaults.DaprGrpcPortName; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_GrpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io", EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable(portVarName, null, EnvironmentVariableTarget.Process); //Will use 443 from the endpoint value instead + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_GrpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointUsingPrefixedConfiguration() + { + const string endpointVarName = "test_DAPR_GRPC_ENDPOINT"; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + + try + { + const string prefix = "test_"; + + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(prefix); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointUsingConfiguration() + { + const string endpointVarName = "DAPR_GRPC_ENDPOINT"; + const string portVarName = "DAPR_GRPC_PORT"; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_GrpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io"); + Environment.SetEnvironmentVariable(portVarName, null); //Will use 443 from the endpoint value instead + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_GrpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointAndPortUsingEnvVarValues() + { + const string endpointVarName = "DAPR_GRPC_ENDPOINT"; + const string portVarName = "DAPR_GRPC_PORT"; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_GrpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "4744"); //Will use 443 from the endpoint value instead + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_GrpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointUsingEnvVarValues() + { + const string endpointVarName = "DAPR_GRPC_ENDPOINT"; + const string portVarName = "DAPR_GRPC_PORT"; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_GrpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io"); + Environment.SetEnvironmentVariable(portVarName, null); //Will use 443 from the endpoint value instead + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_GrpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointDefaultToLocalhostWithPort() + { + const string endpointVarName = DaprDefaults.DaprGrpcEndpointName; + const string portVarName = DaprDefaults.DaprGrpcPortName; + var original_grpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_grpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, null); + Environment.SetEnvironmentVariable(portVarName, "7256"); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal($"{DaprDefaults.DefaultDaprScheme}://{DaprDefaults.DefaultDaprHost}:7256/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_grpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_grpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointDefaultToLocalhostWithDefaultPort() + { + const string endpointVarName = DaprDefaults.DaprGrpcEndpointName; + const string portVarName = DaprDefaults.DaprGrpcPortName; + var original_grpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_grpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, null); + Environment.SetEnvironmentVariable(portVarName, null); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal($"{DaprDefaults.DefaultDaprScheme}://{DaprDefaults.DefaultDaprHost}:{DaprDefaults.DefaultGrpcPort}/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_grpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_grpcPort); + } + } + + [Fact] + public void ShouldBuildApiTokenUsingConfiguration() + { + const string envVarName = DaprDefaults.DaprApiTokenName; + var original_ApiToken = Environment.GetEnvironmentVariable(envVarName); + + try + { + const string apiToken = "abc123"; + Environment.SetEnvironmentVariable(envVarName, apiToken); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var testApiToken = DaprDefaults.GetDefaultDaprApiToken(configuration); + Assert.Equal(apiToken, testApiToken); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(envVarName, original_ApiToken); + } + } + + [Fact] + public void ShouldBuildApiTokenUsingPrefixedConfiguration() + { + + const string envVarName = $"test_{DaprDefaults.DaprApiTokenName}"; + var original_ApiToken = Environment.GetEnvironmentVariable(envVarName); + + try + { + const string prefix = "test_"; + + const string apiToken = "abc123"; + Environment.SetEnvironmentVariable(envVarName, apiToken); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(prefix); + var configuration = configurationBuilder.Build(); + + var testApiToken = DaprDefaults.GetDefaultDaprApiToken(configuration); + Assert.Equal(apiToken, testApiToken); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(envVarName, original_ApiToken); + } + } + + [Fact] + public void ShouldBuildApiTokenWithEnvVarWhenConfigurationNotAvailable() + { + const string envVarName = DaprDefaults.DaprApiTokenName; + var original_ApiToken = Environment.GetEnvironmentVariable(envVarName); + const string apiToken = "abc123"; + Environment.SetEnvironmentVariable(envVarName, apiToken); + + try + { + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var testApiToken = DaprDefaults.GetDefaultDaprApiToken(configuration); + Assert.Equal(apiToken, testApiToken); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(envVarName, original_ApiToken); + } + } +} From 5f21620ecf675b3be8476607945cc161af045a79 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> Date: Tue, 22 Oct 2024 11:27:26 +0200 Subject: [PATCH 28/30] cleanup: Removed Serilog nuget from Directory.Packages.props (#1376) * Removed Serilog nuget from Directory.Packages.props Signed-off-by: Manuel Menegazzo * Update Directory.Packages.props Signed-off-by: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> --------- Signed-off-by: Manuel Menegazzo Signed-off-by: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> --- Directory.Packages.props | 20 +++++++++---------- .../Dapr.E2E.Test.App.csproj | 3 +-- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 842c82aa8..3c1459b5d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,11 +3,10 @@ true true - - + @@ -16,10 +15,10 @@ - + - + @@ -30,20 +29,19 @@ - + - + - + - - + - + - \ No newline at end of file + diff --git a/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj b/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj index 3454ac25d..162a1306a 100644 --- a/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj +++ b/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj @@ -8,9 +8,8 @@ - - + From ee8be673374f7ff0c65b9ff4f506de874f6a2ad3 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com> Date: Tue, 22 Oct 2024 11:33:13 +0200 Subject: [PATCH 29/30] Removed sample folder (#1375) Signed-off-by: Manuel Menegazzo Co-authored-by: Whit Waldo --- all.sln | 7 +---- samples/Client/README.md | 56 ---------------------------------------- 2 files changed, 1 insertion(+), 62 deletions(-) delete mode 100644 samples/Client/README.md diff --git a/all.sln b/all.sln index 85ed848a4..1dd0ab3c5 100644 --- a/all.sln +++ b/all.sln @@ -1,5 +1,5 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 +# 17 VisualStudioVersion = 17.3.32929.385 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Actors", "src\Dapr.Actors\Dapr.Actors.csproj", "{C2DB4B64-B7C3-4FED-8753-C040F677C69A}" @@ -14,11 +14,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Client", "src\Dapr.Cli EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.AspNetCore", "src\Dapr.AspNetCore\Dapr.AspNetCore.csproj", "{08D602F6-7C11-4653-B70B-B56333BF6FD2}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{B2DB41EE-45F5-447B-95E8-38E1E8B70C4E}" - ProjectSection(SolutionItems) = preProject - samples\.editorconfig = samples\.editorconfig - EndProjectSection -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{DD020B34-460F-455F-8D17-CF4A949F100B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.Client.Test", "test\Dapr.Client.Test\Dapr.Client.Test.csproj", "{383609C1-F43F-49EB-85E4-1964EE7F0F14}" diff --git a/samples/Client/README.md b/samples/Client/README.md deleted file mode 100644 index 2bb738d89..000000000 --- a/samples/Client/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Dapr Client examples - -The following examples will show you how to: - -- Invoke services -- Publish events -- Use the state store to get, set, and delete data - -## Prerequisites - -* [.NET 6+](https://dotnet.microsoft.com/download) -* [Dapr CLI](https://github.com/dapr/cli) -* [Dapr DotNet SDK](https://github.com/dapr/dotnet-sdk) - -## Running the Sample - -To run the sample locally run this command in DaprClient directory: -```sh -dapr run --app-id DaprClient -- dotnet run -``` - -Running the following command will output a list of the samples included. -```sh -dapr run --app-id DaprClient -- dotnet run -``` - -Press Ctrl+C to exit, and then run the command again and provide a sample number to run the samples. - -For example run this command to run the 0th sample from the list produced earlier. -```sh -dapr run --app-id DaprClient -- dotnet run 0 -``` - -Samples that use HTTP-based service invocation will require running the [RoutingService](../../AspNetCore/RoutingSample). - -Samples that use gRPC-based service invocation will require running [GrpcService](../../AspNetCore/GrpcServiceSample). - -## Invoking Services - -See: `InvokeServiceHttpClientExample.cs` for an example of using `HttpClient` to invoke another service through Dapr. - -See: `InvokeServiceHttpExample.cs` for an example using the `DaprClient` to invoke another service through Dapr. - -See: `InvokeServiceGrpcExample.cs` for an example using the `DaprClient` to invoke a service using gRPC through Dapr. - -## Publishing Pub/Sub Events - -See: `PublishEventExample.cs` for an example using the `DaprClient` to publish a pub/sub event. - -## Working with the State Store - -See: `StateStoreExample.cs` for an example of using `DaprClient` for basic state store operations like get, set, and delete. - -See: `StateStoreETagsExample.cs` for an example of using `DaprClient` for optimistic concurrency control with the state store. - -See: `StateStoreTransactionsExample.cs` for an example of using `DaprClient` for transactional state store operations that affect multiple keys. From 94b97e224f92ab4d4667390a00ef00d5c61d6747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20C=C3=A2mara?= <52082556+RafaelJCamara@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:27:39 +0200 Subject: [PATCH 30/30] Remove unused variables (#1314) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove unused variables Signed-off-by: Rafael Camara Signed-off-by: Rafael Câmara <52082556+RafaelJCamara@users.noreply.github.com> Co-authored-by: Whit Waldo --- examples/Actor/ActorClient/Program.cs | 2 +- examples/AspNetCore/RoutingSample/Startup.cs | 1 - .../GeneratedActor/ActorClient/Program.cs | 2 +- .../WorkflowUnitTest/OrderProcessingTests.cs | 2 +- .../ActorClientGenerator.cs | 1 - src/Dapr.Actors/Runtime/ActorManager.cs | 3 +- .../HostingTests.cs | 2 +- .../Dapr.Actors.Test/ActorCodeBuilderTests.cs | 2 +- test/Dapr.Actors.Test/ActorIdTests.cs | 5 +- .../Runtime/ActorManagerTests.cs | 2 +- .../Runtime/ActorRuntimeOptionsTests.cs | 2 - .../Serialization/ActorIdJsonConverterTest.cs | 15 ++- .../ControllerIntegrationTest.cs | 3 - .../DaprClientBuilderTest.cs | 6 +- test/Dapr.Client.Test/DaprApiTokenTest.cs | 4 +- .../DaprClientTest.InvokeMethodGrpcAsync.cs | 11 +- .../DistributedLockApiTest.cs | 1 - .../InvocationHandlerTests.cs | 6 +- test/Dapr.Client.Test/InvokeBindingApiTest.cs | 2 +- test/Dapr.Client.Test/PublishEventApiTest.cs | 2 - test/Dapr.Client.Test/StateApiTest.cs | 23 ++-- test/Dapr.Client.Test/TryLockResponseTest.cs | 2 +- .../Clients/GeneratedClientTests.cs | 2 +- test/Dapr.E2E.Test.App/Startup.cs | 2 +- .../Actors/E2ETests.ExceptionTests.cs | 3 +- ...aprSecretStoreConfigurationProviderTest.cs | 102 +++++++++--------- 26 files changed, 94 insertions(+), 114 deletions(-) diff --git a/examples/Actor/ActorClient/Program.cs b/examples/Actor/ActorClient/Program.cs index f6ca26f53..950869b2b 100644 --- a/examples/Actor/ActorClient/Program.cs +++ b/examples/Actor/ActorClient/Program.cs @@ -85,7 +85,7 @@ public static async Task Main(string[] args) var nonRemotingProxy = ActorProxy.Create(actorId, "DemoActor"); await nonRemotingProxy.InvokeMethodAsync("TestNoArgumentNoReturnType"); await nonRemotingProxy.InvokeMethodAsync("SaveData", data); - var res = await nonRemotingProxy.InvokeMethodAsync("GetData"); + await nonRemotingProxy.InvokeMethodAsync("GetData"); Console.WriteLine("Registering the timer and reminder"); await proxy.RegisterTimer(); diff --git a/examples/AspNetCore/RoutingSample/Startup.cs b/examples/AspNetCore/RoutingSample/Startup.cs index 29bb488fe..3d71e9e1f 100644 --- a/examples/AspNetCore/RoutingSample/Startup.cs +++ b/examples/AspNetCore/RoutingSample/Startup.cs @@ -221,7 +221,6 @@ await JsonSerializer.SerializeAsync(context.Response.Body, async Task ViewErrorMessage(HttpContext context) { - var client = context.RequestServices.GetRequiredService(); var transaction = await JsonSerializer.DeserializeAsync(context.Request.Body, serializerOptions); logger.LogInformation("The amount cannot be negative: {0}", transaction.Amount); diff --git a/examples/GeneratedActor/ActorClient/Program.cs b/examples/GeneratedActor/ActorClient/Program.cs index 87f714907..0b858214a 100644 --- a/examples/GeneratedActor/ActorClient/Program.cs +++ b/examples/GeneratedActor/ActorClient/Program.cs @@ -23,7 +23,7 @@ var client = new ClientActorClient(proxy); -var state = await client.GetStateAsync(cancellationTokenSource.Token); +await client.GetStateAsync(cancellationTokenSource.Token); await client.SetStateAsync(new ClientState("Hello, World!"), cancellationTokenSource.Token); diff --git a/examples/Workflow/WorkflowUnitTest/OrderProcessingTests.cs b/examples/Workflow/WorkflowUnitTest/OrderProcessingTests.cs index e38a0c940..3093e5c7a 100644 --- a/examples/Workflow/WorkflowUnitTest/OrderProcessingTests.cs +++ b/examples/Workflow/WorkflowUnitTest/OrderProcessingTests.cs @@ -64,7 +64,7 @@ public async Task TestInsufficientInventory() .Returns(Task.FromResult(inventoryResult)); // Run the workflow directly - OrderResult result = await new OrderProcessingWorkflow().RunAsync(mockContext.Object, order); + await new OrderProcessingWorkflow().RunAsync(mockContext.Object, order); // Verify that ReserveInventoryActivity was called with a specific input mockContext.Verify( diff --git a/src/Dapr.Actors.Generators/ActorClientGenerator.cs b/src/Dapr.Actors.Generators/ActorClientGenerator.cs index 349d80188..f95fc4224 100644 --- a/src/Dapr.Actors.Generators/ActorClientGenerator.cs +++ b/src/Dapr.Actors.Generators/ActorClientGenerator.cs @@ -112,7 +112,6 @@ public void Execute(GeneratorExecutionContext context) { try { - var actorInterfaceTypeName = interfaceSymbol.Name; var fullyQualifiedActorInterfaceTypeName = interfaceSymbol.ToString(); var attributeData = interfaceSymbol.GetAttributes().Single(a => a.AttributeClass?.Equals(generateActorClientAttributeSymbol, SymbolEqualityComparer.Default) == true); diff --git a/src/Dapr.Actors/Runtime/ActorManager.cs b/src/Dapr.Actors/Runtime/ActorManager.cs index 80049d65f..a641440cf 100644 --- a/src/Dapr.Actors/Runtime/ActorManager.cs +++ b/src/Dapr.Actors/Runtime/ActorManager.cs @@ -229,7 +229,6 @@ internal async Task FireTimerAsync(ActorId actorId, Stream requestBodyStream, Ca // Create a Func to be invoked by common method. async Task RequestFunc(Actor actor, CancellationToken ct) { - var actorTypeName = actor.Host.ActorTypeInfo.ActorTypeName; var actorType = actor.Host.ActorTypeInfo.ImplementationType; var methodInfo = actor.GetMethodInfoUsingReflection(actorType, timerData.Callback); @@ -241,7 +240,7 @@ async Task RequestFunc(Actor actor, CancellationToken ct) return default; } - var result = await this.DispatchInternalAsync(actorId, this.timerMethodContext, RequestFunc, cancellationToken); + await this.DispatchInternalAsync(actorId, this.timerMethodContext, RequestFunc, cancellationToken); } internal async Task ActivateActorAsync(ActorId actorId) diff --git a/test/Dapr.Actors.AspNetCore.IntegrationTest/HostingTests.cs b/test/Dapr.Actors.AspNetCore.IntegrationTest/HostingTests.cs index 7f96c66e6..bf3757ce1 100644 --- a/test/Dapr.Actors.AspNetCore.IntegrationTest/HostingTests.cs +++ b/test/Dapr.Actors.AspNetCore.IntegrationTest/HostingTests.cs @@ -42,7 +42,7 @@ public void MapActorsHandlers_WithoutAddActors_Throws() // NOTE: in 3.1 TestServer.CreateClient triggers the failure, in 5.0 it's Host.Start using var host = CreateHost(); var server = host.GetTestServer(); - var client = server.CreateClient(); + server.CreateClient(); }); Assert.Equal( diff --git a/test/Dapr.Actors.Test/ActorCodeBuilderTests.cs b/test/Dapr.Actors.Test/ActorCodeBuilderTests.cs index 6bb3c827d..1b4a3fb26 100644 --- a/test/Dapr.Actors.Test/ActorCodeBuilderTests.cs +++ b/test/Dapr.Actors.Test/ActorCodeBuilderTests.cs @@ -32,7 +32,7 @@ public class ActorCodeBuilderTests [Fact] public void TestBuildActorProxyGenerator() { - ActorProxyGenerator proxyGenerator = ActorCodeBuilder.GetOrCreateProxyGenerator(typeof(ITestActor)); + ActorCodeBuilder.GetOrCreateProxyGenerator(typeof(ITestActor)); } [Fact] diff --git a/test/Dapr.Actors.Test/ActorIdTests.cs b/test/Dapr.Actors.Test/ActorIdTests.cs index 668bd0fbf..c54f4c351 100644 --- a/test/Dapr.Actors.Test/ActorIdTests.cs +++ b/test/Dapr.Actors.Test/ActorIdTests.cs @@ -115,10 +115,7 @@ public class ActorIdTests [InlineData(" ")] public void Initialize_New_ActorId_Object_With_Null_Or_Whitespace_Id(string id) { - Assert.Throws(() => - { - ActorId actorId = new ActorId(id); - }); + Assert.Throws(() => new ActorId(id)); } [Theory] diff --git a/test/Dapr.Actors.Test/Runtime/ActorManagerTests.cs b/test/Dapr.Actors.Test/Runtime/ActorManagerTests.cs index 6b92c7e18..b27e9afe3 100644 --- a/test/Dapr.Actors.Test/Runtime/ActorManagerTests.cs +++ b/test/Dapr.Actors.Test/Runtime/ActorManagerTests.cs @@ -164,7 +164,7 @@ public async Task DeactivateActorAsync_ExceptionDuringDeactivation_ActorIsRemove var id = ActorId.CreateRandom(); await manager.ActivateActorAsync(id); - Assert.True(manager.TryGetActorAsync(id, out var actor)); + Assert.True(manager.TryGetActorAsync(id, out _)); await Assert.ThrowsAsync(async () => { diff --git a/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs b/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs index 4fe991970..b68eda5ce 100644 --- a/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs +++ b/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs @@ -26,8 +26,6 @@ public void TestRegisterActor_SavesActivator() { var actorType = typeof(TestActor); var actorTypeInformation = ActorTypeInformation.Get(actorType, actorTypeName: null); - var host = ActorHost.CreateForTest(); - var actor = new TestActor(host); var activator = Mock.Of(); diff --git a/test/Dapr.Actors.Test/Serialization/ActorIdJsonConverterTest.cs b/test/Dapr.Actors.Test/Serialization/ActorIdJsonConverterTest.cs index fe33eca54..9c4f22193 100644 --- a/test/Dapr.Actors.Test/Serialization/ActorIdJsonConverterTest.cs +++ b/test/Dapr.Actors.Test/Serialization/ActorIdJsonConverterTest.cs @@ -50,9 +50,9 @@ public void CanDeserializeActorId() { var id = ActorId.CreateRandom().GetId(); var document = $@" -{{ - ""actor"": ""{id}"" -}}"; + {{ + ""actor"": ""{id}"" + }}"; var deserialized = JsonSerializer.Deserialize(document); @@ -62,11 +62,10 @@ public void CanDeserializeActorId() [Fact] public void CanDeserializeNullActorId() { - var id = ActorId.CreateRandom().GetId(); - var document = $@" -{{ - ""actor"": null -}}"; + const string document = @" + { + ""actor"": null + }"; var deserialized = JsonSerializer.Deserialize(document); diff --git a/test/Dapr.AspNetCore.IntegrationTest/ControllerIntegrationTest.cs b/test/Dapr.AspNetCore.IntegrationTest/ControllerIntegrationTest.cs index 364cd8448..7735ec4fb 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/ControllerIntegrationTest.cs +++ b/test/Dapr.AspNetCore.IntegrationTest/ControllerIntegrationTest.cs @@ -67,7 +67,6 @@ public async Task ModelBinder_GetFromStateEntryWithKeyNotInStateStore_ReturnsNul using (var factory = new AppWebApplicationFactory()) { var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var daprClient = factory.DaprClient; var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/controllerwithoutstateentry/test"); var response = await httpClient.SendAsync(request); @@ -142,7 +141,6 @@ public async Task ModelBinder_GetFromStateEntryWithStateEntry_WithKeyNotInStateS using (var factory = new AppWebApplicationFactory()) { var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var daprClient = factory.DaprClient; var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/controllerwithstateentry/test"); var response = await httpClient.SendAsync(request); @@ -159,7 +157,6 @@ public async Task ModelBinder_CanGetOutOfTheWayWhenTheresNoBinding() using (var factory = new AppWebApplicationFactory()) { var httpClient = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions { HandleCookies = false }); - var daprClient = factory.DaprClient; var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/echo-user?name=jimmy"); var response = await httpClient.SendAsync(request); diff --git a/test/Dapr.AspNetCore.Test/DaprClientBuilderTest.cs b/test/Dapr.AspNetCore.Test/DaprClientBuilderTest.cs index 52d0b7000..bd807ebae 100644 --- a/test/Dapr.AspNetCore.Test/DaprClientBuilderTest.cs +++ b/test/Dapr.AspNetCore.Test/DaprClientBuilderTest.cs @@ -11,7 +11,7 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; +using System; using System.Text.Json; using Dapr.Client; using Grpc.Core; @@ -44,7 +44,7 @@ public void DaprClientBuilder_UsesPropertyNameCaseHandlingAsSpecified() public void DaprClientBuilder_UsesThrowOperationCanceledOnCancellation_ByDefault() { var builder = new DaprClientBuilder(); - var daprClient = builder.Build(); + Assert.True(builder.GrpcChannelOptions.ThrowOperationCanceledOnCancellation); } @@ -52,7 +52,7 @@ public void DaprClientBuilder_UsesThrowOperationCanceledOnCancellation_ByDefault public void DaprClientBuilder_DoesNotOverrideUserGrpcChannelOptions() { var builder = new DaprClientBuilder(); - var daprClient = builder.UseGrpcChannelOptions(new GrpcChannelOptions()).Build(); + builder.UseGrpcChannelOptions(new GrpcChannelOptions()).Build(); Assert.False(builder.GrpcChannelOptions.ThrowOperationCanceledOnCancellation); } diff --git a/test/Dapr.Client.Test/DaprApiTokenTest.cs b/test/Dapr.Client.Test/DaprApiTokenTest.cs index 3726e0321..cf9f422bc 100644 --- a/test/Dapr.Client.Test/DaprApiTokenTest.cs +++ b/test/Dapr.Client.Test/DaprApiTokenTest.cs @@ -35,7 +35,7 @@ public async Task DaprCall_WithApiTokenSet() request.Dismiss(); // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); + await request.GetRequestEnvelopeAsync(); request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); headerValues.Count().Should().Be(1); @@ -56,7 +56,7 @@ public async Task DaprCall_WithoutApiToken() request.Dismiss(); // Get Request and validate - var envelope = await request.GetRequestEnvelopeAsync(); + await request.GetRequestEnvelopeAsync(); request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); headerValues.Should().BeNull(); diff --git a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs index 65b9b1e7d..6ccbbe4c4 100644 --- a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs +++ b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs @@ -88,8 +88,7 @@ public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithReturnTypeAndData_Thr Data = Any.Pack(data), }; - var response = - client.Call() + await client.Call() .SetResponse(invokeResponse) .Build(); @@ -153,8 +152,7 @@ public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithReturnTypeNoData_Thro Data = Any.Pack(data), }; - var response = - client.Call() + await client.Call() .SetResponse(invokeResponse) .Build(); @@ -210,8 +208,7 @@ public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithNoReturnTypeAndData_T Data = Any.Pack(data), }; - var response = - client.Call() + await client.Call() .SetResponse(invokeResponse) .Build(); @@ -294,7 +291,7 @@ public async Task InvokeMethodGrpcAsync_WithReturnTypeAndData() // Validate Response var invokedResponse = await request.CompleteWithMessageAsync(response); - invokeResponse.Name.Should().Be(invokeResponse.Name); + invokedResponse.Name.Should().Be(invokeResponse.Name); } [Fact] diff --git a/test/Dapr.Client.Test/DistributedLockApiTest.cs b/test/Dapr.Client.Test/DistributedLockApiTest.cs index def7d8f8f..a030586c5 100644 --- a/test/Dapr.Client.Test/DistributedLockApiTest.cs +++ b/test/Dapr.Client.Test/DistributedLockApiTest.cs @@ -60,7 +60,6 @@ public async Task TryLockAsync_WithAllValues_ArgumentVerifierException() string lockOwner = "owner1"; Int32 expiryInSeconds = 0; // Get response and validate - var invokeResponse = new ArgumentException(); await Assert.ThrowsAsync(async () => await client.Lock(storeName, resourceId, lockOwner, expiryInSeconds)); } diff --git a/test/Dapr.Client.Test/InvocationHandlerTests.cs b/test/Dapr.Client.Test/InvocationHandlerTests.cs index 3dac84113..b171ca399 100644 --- a/test/Dapr.Client.Test/InvocationHandlerTests.cs +++ b/test/Dapr.Client.Test/InvocationHandlerTests.cs @@ -156,7 +156,7 @@ public async Task SendAsync_RewritesUri() }; var request = new HttpRequestMessage(HttpMethod.Post, uri); - var response = await CallSendAsync(handler, request); + await CallSendAsync(handler, request); Assert.Equal("https://localhost:5000/v1.0/invoke/bank/method/accounts/17?", capture.RequestUri?.OriginalString); Assert.Null(capture.DaprApiToken); @@ -181,7 +181,7 @@ public async Task SendAsync_RewritesUri_AndAppId() }; var request = new HttpRequestMessage(HttpMethod.Post, uri); - var response = await CallSendAsync(handler, request); + await CallSendAsync(handler, request); Assert.Equal("https://localhost:5000/v1.0/invoke/Bank/method/accounts/17?", capture.RequestUri?.OriginalString); Assert.Null(capture.DaprApiToken); @@ -205,7 +205,7 @@ public async Task SendAsync_RewritesUri_AndAddsApiToken() }; var request = new HttpRequestMessage(HttpMethod.Post, uri); - var response = await CallSendAsync(handler, request); + await CallSendAsync(handler, request); Assert.Equal("https://localhost:5000/v1.0/invoke/bank/method/accounts/17?", capture.RequestUri?.OriginalString); Assert.Equal("super-duper-secure", capture.DaprApiToken); diff --git a/test/Dapr.Client.Test/InvokeBindingApiTest.cs b/test/Dapr.Client.Test/InvokeBindingApiTest.cs index 30c9d48ba..c2ada7a11 100644 --- a/test/Dapr.Client.Test/InvokeBindingApiTest.cs +++ b/test/Dapr.Client.Test/InvokeBindingApiTest.cs @@ -209,7 +209,7 @@ public async Task InvokeBindingAsync_WrapsJsonException() return await daprClient.InvokeBindingAsync("test", "test", new InvokeRequest() { RequestParameter = "Hello " }); }); - var envelope = await request.GetRequestEnvelopeAsync(); + await request.GetRequestEnvelopeAsync(); var ex = await Assert.ThrowsAsync(async () => { await request.CompleteWithMessageAsync(response); diff --git a/test/Dapr.Client.Test/PublishEventApiTest.cs b/test/Dapr.Client.Test/PublishEventApiTest.cs index 77d6ee905..e3e21dd2b 100644 --- a/test/Dapr.Client.Test/PublishEventApiTest.cs +++ b/test/Dapr.Client.Test/PublishEventApiTest.cs @@ -142,7 +142,6 @@ public async Task PublishEventAsync_CanPublishTopicWithNoContent() request.Dismiss(); var envelope = await request.GetRequestEnvelopeAsync(); - var jsonFromRequest = envelope.Data.ToStringUtf8(); envelope.PubsubName.Should().Be(TestPubsubName); envelope.Topic.Should().Be("test"); @@ -214,7 +213,6 @@ public async Task PublishEventAsync_CanPublishCloudEventWithNoContent() { await using var client = TestClient.CreateForDaprClient(); - var publishData = new PublishData() { PublishObjectParameter = "testparam" }; var cloudEvent = new CloudEvent { Source = new Uri("urn:testsource"), diff --git a/test/Dapr.Client.Test/StateApiTest.cs b/test/Dapr.Client.Test/StateApiTest.cs index 0684a8db0..b240605a1 100644 --- a/test/Dapr.Client.Test/StateApiTest.cs +++ b/test/Dapr.Client.Test/StateApiTest.cs @@ -839,8 +839,7 @@ public async Task TrySaveStateAsync_ValidateNonETagErrorThrowsException() { var client = new MockClient(); - var response = client.CallStateApi() - .Build(); + await client.CallStateApi().Build(); var rpcException = new RpcException(new Status(StatusCode.Internal, "Network Error")); @@ -861,8 +860,8 @@ public async Task TrySaveStateAsync_ValidateETagRelatedExceptionReturnsFalse() { var client = new MockClient(); - var response = client.CallStateApi() - .Build(); + await client.CallStateApi() + .Build(); var rpcException = new RpcException(new Status(StatusCode.Aborted, $"failed saving state in state store testStore")); // Setup the mock client to throw an Rpc Exception with the expected details info @@ -879,8 +878,8 @@ public async Task TrySaveStateAsync_NullEtagThrowsArgumentException() { var client = new MockClient(); - var response = client.CallStateApi() - .Build(); + await client.CallStateApi() + .Build(); await FluentActions.Awaiting(async () => await client.DaprClient.TrySaveStateAsync("test", "test", "testValue", null)) .Should().ThrowAsync(); @@ -907,8 +906,8 @@ public async Task TryDeleteStateAsync_ValidateNonETagErrorThrowsException() { var client = new MockClient(); - var response = client.CallStateApi() - .Build(); + await client.CallStateApi() + .Build(); var rpcException = new RpcException(new Status(StatusCode.Internal, "Network Error")); @@ -929,8 +928,8 @@ public async Task TryDeleteStateAsync_NullEtagThrowsArgumentException() { var client = new MockClient(); - var response = client.CallStateApi() - .Build(); + await client.CallStateApi() + .Build(); await FluentActions.Awaiting(async () => await client.DaprClient.TryDeleteStateAsync("test", "test", null)) .Should().ThrowAsync(); @@ -957,8 +956,8 @@ public async Task TryDeleteStateAsync_ValidateETagRelatedExceptionReturnsFalse() { var client = new MockClient(); - var response = client.CallStateApi() - .Build(); + await client.CallStateApi() + .Build(); var rpcException = new RpcException(new Status(StatusCode.Aborted, $"failed deleting state with key test")); // Setup the mock client to throw an Rpc Exception with the expected details info diff --git a/test/Dapr.Client.Test/TryLockResponseTest.cs b/test/Dapr.Client.Test/TryLockResponseTest.cs index f94f949db..3420dc233 100644 --- a/test/Dapr.Client.Test/TryLockResponseTest.cs +++ b/test/Dapr.Client.Test/TryLockResponseTest.cs @@ -47,7 +47,7 @@ public async Task TryLockAsync_WithAllValues_ValidateRequest() Success = true }; - var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); + await request.CompleteWithMessageAsync(invokeResponse); //testing unlocking diff --git a/test/Dapr.E2E.Test.Actors.Generators/Clients/GeneratedClientTests.cs b/test/Dapr.E2E.Test.Actors.Generators/Clients/GeneratedClientTests.cs index 6079b5df7..a089a2e82 100644 --- a/test/Dapr.E2E.Test.Actors.Generators/Clients/GeneratedClientTests.cs +++ b/test/Dapr.E2E.Test.Actors.Generators/Clients/GeneratedClientTests.cs @@ -100,7 +100,7 @@ public async Task TestGeneratedClientAsync() var client = new ClientActorClient(actorProxy); - var result = await client.GetStateAsync(cancellationTokenSource.Token); + await client.GetStateAsync(cancellationTokenSource.Token); await client.SetStateAsync(new ClientState("updated state"), cancellationTokenSource.Token); } diff --git a/test/Dapr.E2E.Test.App/Startup.cs b/test/Dapr.E2E.Test.App/Startup.cs index bfca60f91..7e7484d54 100644 --- a/test/Dapr.E2E.Test.App/Startup.cs +++ b/test/Dapr.E2E.Test.App/Startup.cs @@ -120,7 +120,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { var logger = new LoggerConfiguration().WriteTo.File("log.txt").CreateLogger(); - var loggerFactory = LoggerFactory.Create(builder => + LoggerFactory.Create(builder => { builder.AddSerilog(logger); }); diff --git a/test/Dapr.E2E.Test/Actors/E2ETests.ExceptionTests.cs b/test/Dapr.E2E.Test/Actors/E2ETests.ExceptionTests.cs index 986a2c4f0..fc036d15d 100644 --- a/test/Dapr.E2E.Test/Actors/E2ETests.ExceptionTests.cs +++ b/test/Dapr.E2E.Test/Actors/E2ETests.ExceptionTests.cs @@ -25,8 +25,7 @@ public partial class E2ETests : IAsyncLifetime public async Task ActorCanProvideExceptionDetails() { using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - var actorIds = new ActorId(Guid.NewGuid().ToString()); - + var proxy = this.ProxyFactory.CreateActorProxy(ActorId.CreateRandom(), "ExceptionActor"); await WaitForActorRuntimeAsync(proxy, cts.Token); ActorMethodInvocationException ex = await Assert.ThrowsAsync(async () => await proxy.ExceptionExample()); diff --git a/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs b/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs index 74b66c3cb..fd323f989 100644 --- a/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs +++ b/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs @@ -41,11 +41,11 @@ public void AddDaprSecretStore_UsingDescriptors_WithoutStore_ReportsError() .Build(); var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore(null, new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore(null, new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) + .Build(); + }); Assert.Contains("store", ex.Message); } @@ -58,11 +58,11 @@ public void AddDaprSecretStore_UsingDescriptors_WithEmptyStore_ReportsError() .Build(); var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore(string.Empty, new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore(string.Empty, new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) + .Build(); + }); Assert.Contains("The value cannot be null or empty", ex.Message); } @@ -75,11 +75,11 @@ public void AddDaprSecretStore_UsingDescriptors_WithoutSecretDescriptors_Reports .Build(); var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore("store", (DaprSecretDescriptor[])null, daprClient) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore("store", (DaprSecretDescriptor[])null, daprClient) + .Build(); + }); Assert.Contains("secretDescriptors", ex.Message); } @@ -88,11 +88,11 @@ public void AddDaprSecretStore_UsingDescriptors_WithoutSecretDescriptors_Reports public void AddDaprSecretStore_UsingDescriptors_WithoutClient_ReportsError() { var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, null) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, null) + .Build(); + }); Assert.Contains("client", ex.Message); } @@ -105,11 +105,11 @@ public void AddDaprSecretStore_UsingDescriptors_WithZeroSecretDescriptors_Report .Build(); var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore("store", new DaprSecretDescriptor[] { }, daprClient) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore("store", new DaprSecretDescriptor[] { }, daprClient) + .Build(); + }); Assert.Contains("No secret descriptor was provided", ex.Message); } @@ -132,11 +132,11 @@ public void AddDaprSecretStore_UsingDescriptors_DuplicateSecret_ReportsError() .Build(); var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient) + .Build(); + }); Assert.Contains("Please remove any duplicates from your secret store.", ex.Message); } @@ -310,7 +310,7 @@ public void LoadSecrets_FromSecretStoreRequiredAndDoesNotExist_ShouldThrowExcept var ex = Assert.Throws(() => { - var config = CreateBuilder() + CreateBuilder() .AddDaprSecretStore(storeName, secretDescriptors, daprClient) .Build(); }); @@ -327,11 +327,11 @@ public void AddDaprSecretStore_WithoutStore_ReportsError() .Build(); var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore(null, daprClient) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore(null, daprClient) + .Build(); + }); Assert.Contains("store", ex.Message); } @@ -344,11 +344,11 @@ public void AddDaprSecretStore_WithEmptyStore_ReportsError() .Build(); var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore(string.Empty, daprClient) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore(string.Empty, daprClient) + .Build(); + }); Assert.Contains("The value cannot be null or empty", ex.Message); } @@ -357,11 +357,11 @@ public void AddDaprSecretStore_WithEmptyStore_ReportsError() public void AddDaprSecretStore_WithoutClient_ReportsError() { var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore("store", null) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore("store", null) + .Build(); + }); Assert.Contains("client", ex.Message); } @@ -384,11 +384,11 @@ public void AddDaprSecretStore_DuplicateSecret_ReportsError() .Build(); var ex = Assert.Throws(() => - { - var config = CreateBuilder() - .AddDaprSecretStore("store", daprClient) - .Build(); - }); + { + CreateBuilder() + .AddDaprSecretStore("store", daprClient) + .Build(); + }); Assert.Contains("Please remove any duplicates from your secret store.", ex.Message); }