diff --git a/.github/holopin.yml b/.github/holopin.yml new file mode 100644 index 000000000..44a7f0c8a --- /dev/null +++ b/.github/holopin.yml @@ -0,0 +1,6 @@ +organization: dapr +defaultSticker: clmjkxscc122740fl0mkmb7egi +stickers: + - + id: clmjkxscc122740fl0mkmb7egi + alias: ghc2023 diff --git a/.github/workflows/itests.yml b/.github/workflows/itests.yml index 870264f40..8a299bd59 100644 --- a/.github/workflows/itests.yml +++ b/.github/workflows/itests.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - dotnet-version: ['6.0', '7.0'] + dotnet-version: ['6.0', '7.0', '8.0'] include: - dotnet-version: '6.0' display-name: '.NET 6.0' @@ -31,17 +31,21 @@ jobs: framework: 'net7' prefix: 'net7' install-version: '7.0.x' + - dotnet-version: '8.0' + display-name: '.NET 8.0' + framework: 'net8' + prefix: 'net8' + install-version: '8.0.x' env: NUPKG_OUTDIR: bin/Release/nugets GOVER: 1.20.3 GOOS: linux GOARCH: amd64 GOPROXY: https://proxy.golang.org - DAPR_CLI_VER: 1.9.1 - DAPR_RUNTIME_VER: 1.10.5 - DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/3dacfb672d55f1436c249057aaebbe597e1066f3/install/install.sh + DAPR_CLI_VER: 1.12.0 + DAPR_RUNTIME_VER: 1.12.0 + DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/release-1.12/install/install.sh DAPR_CLI_REF: '' - DAPR_REF: '4181de0edc65fc98a836ae7abc6042c575c8fae5' steps: - name: Set up Dapr CLI run: wget -q ${{ env.DAPR_INSTALL_URL }} -O - | /bin/bash -s ${{ env.DAPR_CLI_VER }} @@ -101,11 +105,11 @@ jobs: uses: actions/setup-dotnet@v1 with: dotnet-version: ${{ matrix.install-version }} - - name: Setup .NET 7.0 # net7 is always required. + - name: Setup .NET 8.0 # net8 is always required. uses: actions/setup-dotnet@v1 - if: ${{ matrix.install-version != '7.0.x' }} + if: ${{ matrix.install-version != '8.0.x' }} with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Build # disable deterministic builds, just for test run. Deterministic builds break coverage for some reason run: dotnet build --configuration release /p:GITHUB_ACTIONS=false diff --git a/.github/workflows/sdk_build.yml b/.github/workflows/sdk_build.yml index 1c4e1a60e..4fde80610 100644 --- a/.github/workflows/sdk_build.yml +++ b/.github/workflows/sdk_build.yml @@ -26,7 +26,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Build run: dotnet build --configuration release - name: Generate Packages @@ -42,7 +42,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - dotnet-version: ['6.0', '7.0'] + dotnet-version: ['6.0', '7.0', '8.0'] include: - dotnet-version: '6.0' install-3: false @@ -56,6 +56,12 @@ jobs: framework: 'net7' prefix: 'net7' install-version: '7.0.x' + - dotnet-version: '8.0' + install-3: false + display-name: '.NET 8.0' + framework: 'net8' + prefix: 'net8' + install-version: '8.0.x' steps: - uses: actions/checkout@v1 - name: Parse release version @@ -64,11 +70,11 @@ jobs: uses: actions/setup-dotnet@v1 with: dotnet-version: ${{ matrix.install-version }} - - name: Setup .NET 7.0 # net7 is always required. + - name: Setup .NET 8.0 # net8 is always required. uses: actions/setup-dotnet@v1 - if: ${{ matrix.install-version != '7.0.x' }} + if: ${{ matrix.install-version != '8.0.x' }} with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Build # disable deterministic builds, just for test run. Deterministic builds break coverage for some reason run: dotnet build --configuration release /p:GITHUB_ACTIONS=false diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflow-howto.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflow-howto.md index 650cdec38..f6d18bc58 100644 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflow-howto.md +++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-workflow/dotnet-workflow-howto.md @@ -83,7 +83,7 @@ Run the following command to start a workflow. {{% codetab %}} ```bash -curl -i -X POST http://localhost:3500/v1.0-alpha1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=12345678 \ +curl -i -X POST http://localhost:3500/v1.0-beta1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=12345678 \ -H "Content-Type: application/json" \ -d '{"Name": "Paperclips", "TotalCost": 99.95, "Quantity": 1}' ``` @@ -93,7 +93,7 @@ curl -i -X POST http://localhost:3500/v1.0-alpha1/workflows/dapr/OrderProcessing {{% codetab %}} ```powershell -curl -i -X POST http://localhost:3500/v1.0-alpha1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=12345678 ` +curl -i -X POST http://localhost:3500/v1.0-beta1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=12345678 ` -H "Content-Type: application/json" ` -d '{"Name": "Paperclips", "TotalCost": 99.95, "Quantity": 1}' ``` @@ -111,7 +111,7 @@ If successful, you should see a response like the following: Send an HTTP request to get the status of the workflow that was started: ```bash -curl -i -X GET http://localhost:3500/v1.0-alpha1/workflows/dapr/12345678 +curl -i -X GET http://localhost:3500/v1.0-beta1/workflows/dapr/12345678 ``` The workflow is designed to take several seconds to complete. If the workflow hasn't completed when you issue the HTTP request, you'll see the following JSON response (formatted for readability) with workflow status as `RUNNING`: diff --git a/examples/Actor/ActorClient/Program.cs b/examples/Actor/ActorClient/Program.cs index aeee28386..5d7f06fb2 100644 --- a/examples/Actor/ActorClient/Program.cs +++ b/examples/Actor/ActorClient/Program.cs @@ -18,7 +18,6 @@ namespace ActorClient using System.Threading.Tasks; using Dapr.Actors; using Dapr.Actors.Client; - using Dapr.Actors.Communication; using IDemoActorInterface; /// @@ -69,7 +68,7 @@ public static async Task Main(string[] args) } catch (ActorMethodInvocationException ex) { - if (ex.InnerException is NotImplementedException) + if (ex.InnerException is ActorInvokeException invokeEx && invokeEx.ActualExceptionType is "System.NotImplementedException") { Console.WriteLine($"Got Correct Exception from actor method invocation."); } @@ -111,7 +110,7 @@ public static async Task Main(string[] args) await Task.Delay(5000); Console.WriteLine("Getting details of the registered reminder"); reminder = await proxy.GetReminder(); - Console.WriteLine($"Received reminder is {reminder}."); + Console.WriteLine($"Received reminder is {reminder?.ToString() ?? "None"} (expecting None)."); Console.WriteLine("Registering reminder with ttl and repetitions, i.e. reminder stops when either condition is met - The reminder will repeat 2 times."); await proxy.RegisterReminderWithTtlAndRepetitions(TimeSpan.FromSeconds(5), 2); Console.WriteLine("Getting details of the registered reminder"); diff --git a/examples/Actor/DemoActor/DemoActor.cs b/examples/Actor/DemoActor/DemoActor.cs index 0ab633fcd..62c100f79 100644 --- a/examples/Actor/DemoActor/DemoActor.cs +++ b/examples/Actor/DemoActor/DemoActor.cs @@ -85,9 +85,18 @@ public async Task RegisterReminderWithTtlAndRepetitions(TimeSpan ttl, int repeti await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1), repetitions, ttl); } - public async Task GetReminder() - { - return await this.GetReminderAsync("TestReminder"); + public async Task GetReminder() + { + var reminder = await this.GetReminderAsync("TestReminder"); + + return reminder is not null + ? new ActorReminderData + { + Name = reminder.Name, + Period = reminder.Period, + DueTime = reminder.DueTime + } + : null; } public Task UnregisterReminder() diff --git a/examples/Actor/IDemoActor/IDemoActor.cs b/examples/Actor/IDemoActor/IDemoActor.cs index adec6df68..c2926a048 100644 --- a/examples/Actor/IDemoActor/IDemoActor.cs +++ b/examples/Actor/IDemoActor/IDemoActor.cs @@ -100,7 +100,7 @@ public interface IDemoActor : IActor /// /// The name of the reminder. /// A task that returns the reminder after completion. - Task GetReminder(); + Task GetReminder(); /// /// Unregisters the registered timer. @@ -132,4 +132,18 @@ public override string ToString() return $"PropertyA: {propAValue}, PropertyB: {propBValue}"; } } + + public class ActorReminderData + { + public string Name { get; set; } + + public TimeSpan DueTime { get; set; } + + public TimeSpan Period { get; set; } + + public override string ToString() + { + return $"Name: {this.Name}, DueTime: {this.DueTime}, Period: {this.Period}"; + } + } } diff --git a/examples/Workflow/README.md b/examples/Workflow/README.md index e119465c3..1af855767 100644 --- a/examples/Workflow/README.md +++ b/examples/Workflow/README.md @@ -11,14 +11,22 @@ This Dapr workflow example shows how to create a Dapr workflow (`Workflow`) and ## Optional Setup -Dapr workflow, as well as this example program, now support authentication through the use of API tokens. For more information on this, view the following document: [API Token](https://github.com/dapr/dotnet-sdk/docs/api-token.md) +Dapr workflow, as well as this example program, now support authentication through the use of API tokens. For more information on this, view the following document: [API Token](https://github.com/dapr/dotnet-sdk/blob/master/docs/api-tokens.md) ## Projects in sample This sample contains a single [WorkflowConsoleApp](./WorkflowConsoleApp) .NET project. -It utilizes the workflow SDK as well as the workflow management API for starting and querying workflows instances. -The main `Program.cs` file contains the main setup of the app, including the registration of the workflow and workflow activities. -The workflow definition is found in the `Workflows` directory and the workflow activity definitions are found in the `Activities` directory. +It utilizes the workflow SDK as well as the workflow management API for simulating inventory management and sale of goods in a store. +The main `Program.cs` file contains the main setup of the app, the registration of the workflow and its activities, and interaction with the user. The workflow definition is found in the `Workflows` directory and the workflow activity definitions are found in the `Activities` directory. + +There are five activities in the directory that could be called by the workflows: +- `NotifyActivity`: printing logs as notifications +- `ProcessPaymentActivity`: printing logs and delaying for simulating payment processing +- `RequestApprovalActivity`: printing logs to indicate that the order has been approved +- `ReserveInventoryActivity`: checking if there are enough items for purchase +- `UpdateInventoryActivity`: updating the statestore according to purchasing + +The `OrderProcessingWorkflow.cs` in `Workflows` directory implements the running logic of the workflow. Based on the purchase stage and outcome, it calls different activities and waits for the corresponding events to trigger interaction with the user. This sample also contains a [WorkflowUnitTest](./WorkflowUnitTest) .NET project that utilizes [xUnit](https://xunit.net/) and [Moq](https://github.com/moq/moq) to test the workflow logic. It works by creating an instance of the `OrderProcessingWorkflow` (defined in the `WorkflowConsoleApp` project), mocking activity calls, and testing the inputs and outputs. @@ -53,7 +61,7 @@ For the workflow API option, two identical `curl` commands are shown, one for Li Make note of the "1234" in the commands below. This represents the unique identifier for the workflow run and can be replaced with any identifier of your choosing. ```bash -curl -i -X POST http://localhost:3500/v1.0-alpha1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=1234 \ +curl -i -X POST http://localhost:3500/v1.0-beta1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=1234 \ -H "Content-Type: application/json" \ -d '{"Name": "Paperclips", "TotalCost": 99.95, "Quantity": 1}' ``` @@ -61,7 +69,7 @@ curl -i -X POST http://localhost:3500/v1.0-alpha1/workflows/dapr/OrderProcessing On Windows (PowerShell): ```powershell -curl -i -X POST http://localhost:3500/v1.0-alpha1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=1234 ` +curl -i -X POST http://localhost:3500/v1.0-beta1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=1234 ` -H "Content-Type: application/json" ` -d '{"Name": "Paperclips", "TotalCost": 99.95, "Quantity": 1}' ``` @@ -75,7 +83,7 @@ If successful, you should see a response like the following: Next, send an HTTP request to get the status of the workflow that was started: ```bash -curl -i -X GET http://localhost:3500/v1.0-alpha1/workflows/dapr/1234 +curl -i -X GET http://localhost:3500/v1.0-beta1/workflows/dapr/1234 ``` The workflow is designed to take several seconds to complete. If the workflow hasn't completed yet when you issue the previous command, you should see the following JSON response (formatted for readability): @@ -124,4 +132,4 @@ info: WorkflowConsoleApp.Activities.NotifyActivity[0] Order 1234 processed successfully! ``` -If you have Zipkin configured for Dapr locally on your machine, then you can view the workflow trace spans in the Zipkin web UI (typically at http://localhost:9411/zipkin/). +If you have Zipkin configured for Dapr locally on your machine, then you can view the workflow trace spans in the Zipkin web UI (typically at http://localhost:9411/zipkin/). \ No newline at end of file diff --git a/examples/Workflow/WorkflowConsoleApp/Program.cs b/examples/Workflow/WorkflowConsoleApp/Program.cs index 9aae2427e..055b1b4c1 100644 --- a/examples/Workflow/WorkflowConsoleApp/Program.cs +++ b/examples/Workflow/WorkflowConsoleApp/Program.cs @@ -81,7 +81,14 @@ // Start the input loop using (daprClient) { - while (true) + bool quit = false; + Console.CancelKeyPress += delegate + { + quit = true; + Console.WriteLine("Shutting down the example."); + }; + + while (!quit) { // Get the name of the item to order and make sure we have inventory string items = string.Join(", ", baseInventory.Select(i => i.Name)); diff --git a/examples/Workflow/WorkflowConsoleApp/WorkflowConsoleApp.csproj b/examples/Workflow/WorkflowConsoleApp/WorkflowConsoleApp.csproj index 0c40eea0c..25c03a419 100644 --- a/examples/Workflow/WorkflowConsoleApp/WorkflowConsoleApp.csproj +++ b/examples/Workflow/WorkflowConsoleApp/WorkflowConsoleApp.csproj @@ -8,7 +8,6 @@ Exe net6 enable - latest 612,618 diff --git a/examples/Workflow/WorkflowConsoleApp/demo.http b/examples/Workflow/WorkflowConsoleApp/demo.http index 48b849a88..669cefeb7 100644 --- a/examples/Workflow/WorkflowConsoleApp/demo.http +++ b/examples/Workflow/WorkflowConsoleApp/demo.http @@ -1,17 +1,17 @@ ### Start order processing workflow - replace xxx with any id you like -POST http://localhost:3500/v1.0-alpha1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=xxx +POST http://localhost:3500/v1.0-beta1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=xxx Content-Type: application/json {"Name": "Paperclips", "TotalCost": 99.95, "Quantity": 1} ### Start order processing workflow - replace xxx with any id you like -POST http://localhost:3500/v1.0-alpha1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=xxx +POST http://localhost:3500/v1.0-beta1/workflows/dapr/OrderProcessingWorkflow/start?instanceID=xxx Content-Type: application/json {"Name": "Cars", "TotalCost": 10000, "Quantity": 30} ### Query dapr sidecar - replace xxx with id from the workflow you've created above -GET http://localhost:3500/v1.0-alpha1/workflows/dapr/xxx +GET http://localhost:3500/v1.0-beta1/workflows/dapr/xxx ### Terminate the workflow - replace xxx with id from the workflow you've created above -POST http://localhost:3500/v1.0-alpha1/workflows/dapr/xxx/terminate \ No newline at end of file +POST http://localhost:3500/v1.0-beta1/workflows/dapr/xxx/terminate \ No newline at end of file diff --git a/examples/Workflow/WorkflowUnitTest/WorkflowUnitTest.csproj b/examples/Workflow/WorkflowUnitTest/WorkflowUnitTest.csproj index 9c4a74a17..4ce0c9801 100644 --- a/examples/Workflow/WorkflowUnitTest/WorkflowUnitTest.csproj +++ b/examples/Workflow/WorkflowUnitTest/WorkflowUnitTest.csproj @@ -3,7 +3,6 @@ net6.0 enable - 10 false diff --git a/properties/dapr_managed_netcore.props b/properties/dapr_managed_netcore.props index 59bb68c9a..3bafcb50c 100644 --- a/properties/dapr_managed_netcore.props +++ b/properties/dapr_managed_netcore.props @@ -3,7 +3,7 @@ Debug - 9.0 + 10.0 true 4 false diff --git a/src/Dapr.Actors.AspNetCore/ActorsEndpointRouteBuilderExtensions.cs b/src/Dapr.Actors.AspNetCore/ActorsEndpointRouteBuilderExtensions.cs index e7937bbbf..55d161d9a 100644 --- a/src/Dapr.Actors.AspNetCore/ActorsEndpointRouteBuilderExtensions.cs +++ b/src/Dapr.Actors.AspNetCore/ActorsEndpointRouteBuilderExtensions.cs @@ -109,7 +109,7 @@ private static IEndpointConventionBuilder MapActorMethodEndpoint(this IEndpointR if (header != string.Empty) { // exception case - context.Response.Headers.Add(Constants.ErrorResponseHeaderName, header); // add error header + context.Response.Headers[Constants.ErrorResponseHeaderName] = header; // add error header } await context.Response.Body.WriteAsync(body, 0, body.Length); // add response message body @@ -118,7 +118,7 @@ private static IEndpointConventionBuilder MapActorMethodEndpoint(this IEndpointR { var (header, body) = CreateExceptionResponseMessage(ex); - context.Response.Headers.Add(Constants.ErrorResponseHeaderName, header); + context.Response.Headers[Constants.ErrorResponseHeaderName] = header; await context.Response.Body.WriteAsync(body, 0, body.Length); } finally diff --git a/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj b/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj index cfd7d8123..1114b7828 100644 --- a/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj +++ b/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj @@ -1,7 +1,4 @@  - - net6 - This package contains the reference assemblies for developing Actor services using Dapr. diff --git a/src/Dapr.AspNetCore/Dapr.AspNetCore.csproj b/src/Dapr.AspNetCore/Dapr.AspNetCore.csproj index a50133677..c61fc5abc 100644 --- a/src/Dapr.AspNetCore/Dapr.AspNetCore.csproj +++ b/src/Dapr.AspNetCore/Dapr.AspNetCore.csproj @@ -1,9 +1,5 @@  - - net6 - - This package contains the reference assemblies for developing services using Dapr and AspNetCore. diff --git a/src/Dapr.AspNetCore/DaprAuthenticationHandler.cs b/src/Dapr.AspNetCore/DaprAuthenticationHandler.cs index 822694c3b..dc21b5926 100644 --- a/src/Dapr.AspNetCore/DaprAuthenticationHandler.cs +++ b/src/Dapr.AspNetCore/DaprAuthenticationHandler.cs @@ -25,6 +25,14 @@ internal class DaprAuthenticationHandler : AuthenticationHandler options, + ILoggerFactory logger, + UrlEncoder encoder) : base(options, logger, encoder) + { + } +#else public DaprAuthenticationHandler( IOptionsMonitor options, ILoggerFactory logger, @@ -32,6 +40,7 @@ public DaprAuthenticationHandler( ISystemClock clock) : base(options, logger, encoder, clock) { } +#endif protected override Task HandleAuthenticateAsync() { diff --git a/src/Dapr.Client/Dapr.Client.csproj b/src/Dapr.Client/Dapr.Client.csproj index 45dd168fe..a3fd5b082 100644 --- a/src/Dapr.Client/Dapr.Client.csproj +++ b/src/Dapr.Client/Dapr.Client.csproj @@ -1,9 +1,5 @@  - - net6 - - diff --git a/src/Dapr.Client/DaprApiException.cs b/src/Dapr.Client/DaprApiException.cs index e7af8947c..75fc2cf7f 100644 --- a/src/Dapr.Client/DaprApiException.cs +++ b/src/Dapr.Client/DaprApiException.cs @@ -92,6 +92,9 @@ public DaprApiException(string message, Exception inner, string errorCode, bool /// /// The object that contains serialized object data of the exception being thrown. /// The object that contains contextual information about the source or destination. The context parameter is reserved for future use and can be null. +#if NET8_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] // add this attribute to the serialization ctor +#endif protected DaprApiException(SerializationInfo info, StreamingContext context) : base(info, context) { @@ -115,6 +118,9 @@ protected DaprApiException(SerializationInfo info, StreamingContext context) public bool IsTransient { get; } = false; /// +#if NET8_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] // add this attribute to GetObjectData +#endif public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); diff --git a/src/Dapr.Client/DaprClient.cs b/src/Dapr.Client/DaprClient.cs index bb14c647a..691c6a616 100644 --- a/src/Dapr.Client/DaprClient.cs +++ b/src/Dapr.Client/DaprClient.cs @@ -293,7 +293,7 @@ public abstract Task InvokeBindingAsync( /// /// Creates an that can be used to perform service invocation for the - /// application idenfied by and invokes the method specified by + /// application identified by and invokes the method specified by /// with the POST HTTP method. /// /// The Dapr application id to invoke the method on. @@ -306,7 +306,7 @@ public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodN /// /// Creates an that can be used to perform service invocation for the - /// application idenfied by and invokes the method specified by + /// application identified by and invokes the method specified by /// with the HTTP method specified by . /// /// The to use for the invocation request. @@ -317,7 +317,7 @@ public HttpRequestMessage CreateInvokeMethodRequest(string appId, string methodN /// /// Creates an that can be used to perform service invocation for the - /// application idenfied by and invokes the method specified by + /// application identified by and invokes the method specified by /// with the POST HTTP method and a JSON serialized request body specified by . /// /// The type of the data that will be JSON serialized and provided as the request body. @@ -332,7 +332,7 @@ public HttpRequestMessage CreateInvokeMethodRequest(string appId, stri /// /// Creates an that can be used to perform service invocation for the - /// application idenfied by and invokes the method specified by + /// application identified by and invokes the method specified by /// with the HTTP method specified by and a JSON serialized request body specified by /// . /// @@ -445,7 +445,7 @@ public HttpRequestMessage CreateInvokeMethodRequest(string appId, stri public abstract Task InvokeMethodAsync(HttpRequestMessage request, CancellationToken cancellationToken = default); /// - /// Perform service invocation for the application idenfied by and invokes the method + /// Perform service invocation for the application identified by and invokes the method /// specified by with the POST HTTP method and an empty request body. /// If the response has a non-success status code an exception will be thrown. /// @@ -463,7 +463,7 @@ public Task InvokeMethodAsync( } /// - /// Perform service invocation for the application idenfied by and invokes the method + /// Perform service invocation for the application identified by and invokes the method /// specified by with the HTTP method specified by /// and an empty request body. If the response has a non-success status code an exception will be thrown. /// @@ -483,7 +483,7 @@ public Task InvokeMethodAsync( } /// - /// Perform service invocation for the application idenfied by and invokes the method + /// Perform service invocation for the application identified by and invokes the method /// specified by with the POST HTTP method /// and a JSON serialized request body specified by . If the response has a non-success /// status code an exception will be thrown. @@ -505,7 +505,7 @@ public Task InvokeMethodAsync( } /// - /// Perform service invocation for the application idenfied by and invokes the method + /// 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 . If the response has a non-success /// status code an exception will be thrown. @@ -529,7 +529,7 @@ public Task InvokeMethodAsync( } /// - /// Perform service invocation for the application idenfied by and invokes the method + /// Perform service invocation for the application identified by and invokes the method /// specified by with the POST HTTP method /// and an empty request body. If the response has a success /// status code the body will be deserialized using JSON to a value of type ; @@ -550,7 +550,7 @@ public Task InvokeMethodAsync( } /// - /// Perform service invocation for the application idenfied by and invokes the method + /// Perform service invocation for the application identified by and invokes the method /// specified by with the HTTP method specified by /// and an empty request body. If the response has a success /// status code the body will be deserialized using JSON to a value of type ; @@ -573,7 +573,7 @@ public Task InvokeMethodAsync( } /// - /// Perform service invocation for the application idenfied by and invokes the method + /// Perform service invocation for the application identified by and invokes the method /// specified by with the POST HTTP method /// and a JSON serialized request body specified by . If the response has a success /// status code the body will be deserialized using JSON to a value of type ; @@ -597,7 +597,7 @@ public Task InvokeMethodAsync( } /// - /// Perform service invocation for the application idenfied by and invokes the method + /// 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 . If the response has a success /// status code the body will be deserialized using JSON to a value of type ; @@ -623,7 +623,7 @@ public Task InvokeMethodAsync( } /// - /// Perform service invocation using gRPC semantics for the application idenfied by and invokes the method + /// Perform service invocation using gRPC semantics for the application identified by and invokes the method /// specified by with an empty request body. /// If the response has a non-success status code an exception will be thrown. /// @@ -637,7 +637,7 @@ public abstract Task InvokeMethodGrpcAsync( CancellationToken cancellationToken = default); /// - /// Perform service invocation using gRPC semantics for the application idenfied by and invokes the method + /// Perform service invocation using gRPC semantics for the application identified by and invokes the method /// specified by with a Protobuf serialized request body specified by . /// If the response has a non-success status code an exception will be thrown. /// @@ -655,7 +655,7 @@ public abstract Task InvokeMethodGrpcAsync( where TRequest : IMessage; /// - /// Perform service invocation using gRPC semantics for the application idenfied by and invokes the method + /// Perform service invocation using gRPC semantics for the application identified by and invokes the method /// specified by with an empty request body. If the response has a success /// status code the body will be deserialized using Protobuf to a value of type ; /// otherwise an exception will be thrown. @@ -672,7 +672,7 @@ public abstract Task InvokeMethodGrpcAsync( where TResponse : IMessage, new(); /// - /// Perform service invocation using gRPC semantics for the application idenfied by and invokes the method + /// Perform service invocation using gRPC semantics for the application identified by and invokes the method /// specified by with a Protobuf serialized request body specified by . If the response has a success /// status code the body will be deserialized using Protobuf to a value of type ; /// otherwise an exception will be thrown. diff --git a/src/Dapr.Client/DaprException.cs b/src/Dapr.Client/DaprException.cs index 8c94a452d..e7b1efaba 100644 --- a/src/Dapr.Client/DaprException.cs +++ b/src/Dapr.Client/DaprException.cs @@ -47,6 +47,9 @@ public DaprException(string message, Exception innerException) /// /// The object that contains serialized object data of the exception being thrown. /// The object that contains contextual information about the source or destination. The context parameter is reserved for future use and can be null. +#if NET8_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] // add this attribute to GetObjectData +#endif protected DaprException(SerializationInfo info, StreamingContext context) : base(info, context) { diff --git a/src/Dapr.Extensions.Configuration/Dapr.Extensions.Configuration.csproj b/src/Dapr.Extensions.Configuration/Dapr.Extensions.Configuration.csproj index 0f1f64048..71fd0153e 100644 --- a/src/Dapr.Extensions.Configuration/Dapr.Extensions.Configuration.csproj +++ b/src/Dapr.Extensions.Configuration/Dapr.Extensions.Configuration.csproj @@ -1,7 +1,6 @@ - net6 enable diff --git a/src/Dapr.Workflow/Dapr.Workflow.csproj b/src/Dapr.Workflow/Dapr.Workflow.csproj index 9d8ba1a4e..d5820deb1 100644 --- a/src/Dapr.Workflow/Dapr.Workflow.csproj +++ b/src/Dapr.Workflow/Dapr.Workflow.csproj @@ -2,14 +2,14 @@ - net6;net7 + + net6;net7;net8 enable Dapr.Workflow Dapr Workflow Authoring SDK Dapr Workflow SDK for building workflows as code with Dapr 0.3.0 alpha - 10.0 diff --git a/src/Dapr.Workflow/WorkflowLoggingService.cs b/src/Dapr.Workflow/WorkflowLoggingService.cs new file mode 100644 index 000000000..482d95b97 --- /dev/null +++ b/src/Dapr.Workflow/WorkflowLoggingService.cs @@ -0,0 +1,75 @@ +// ------------------------------------------------------------------------ +// 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. +// ------------------------------------------------------------------------ + +namespace Dapr.Workflow +{ + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Extensions.Hosting; + using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Configuration; + + /// + /// Defines runtime options for workflows. + /// + internal sealed class WorkflowLoggingService : IHostedService + { + private readonly ILogger logger; + private static readonly HashSet registeredWorkflows = new(); + private static readonly HashSet registeredActivities = new(); + + public WorkflowLoggingService(ILogger logger, IConfiguration configuration) + { + this.logger = logger; + + } + public Task StartAsync(CancellationToken cancellationToken) + { + this.logger.Log(LogLevel.Information, "WorkflowLoggingService started"); + + this.logger.Log(LogLevel.Information, "List of registered workflows"); + foreach (string item in registeredWorkflows) + { + this.logger.Log(LogLevel.Information, item); + } + + this.logger.Log(LogLevel.Information, "List of registered activities:"); + foreach (string item in registeredActivities) + { + this.logger.Log(LogLevel.Information, item); + } + + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + this.logger.Log(LogLevel.Information, "WorkflowLoggingService stopped"); + + return Task.CompletedTask; + } + + public static void LogWorkflowName(string workflowName) + { + registeredWorkflows.Add(workflowName); + } + + public static void LogActivityName(string activityName) + { + registeredActivities.Add(activityName); + } + + } +} diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index 4dd202b1a..adc925777 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -54,6 +54,7 @@ public void RegisterWorkflow(string name, Func(string name, Func(); return new OrchestratorWrapper(workflow); }); + WorkflowLoggingService.LogWorkflowName(name); }); } @@ -91,6 +93,7 @@ public void RegisterActivity(string name, Func() where TActivity : class, IWorkflowActi TActivity activity = ActivatorUtilities.CreateInstance(serviceProvider); return new ActivityWrapper(activity); }); + WorkflowLoggingService.LogActivityName(name); }); } diff --git a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs index 50880ab24..ca514f221 100644 --- a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs +++ b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs @@ -47,7 +47,7 @@ public static IServiceCollection AddDaprWorkflow( #pragma warning disable CS0618 // Type or member is obsolete - keeping around temporarily - replaced by DaprWorkflowClient serviceCollection.TryAddSingleton(); #pragma warning restore CS0618 // Type or member is obsolete - + serviceCollection.AddHostedService(); serviceCollection.TryAddSingleton(); serviceCollection.AddDaprClient(); serviceCollection.AddDaprWorkflowClient(); diff --git a/src/Directory.Build.props b/src/Directory.Build.props index cdfef31a6..2794f1b1f 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -3,6 +3,7 @@ + net6;net8 $(RepoRoot)bin\$(Configuration)\prod\$(MSBuildProjectName)\ $(OutputPath)$(MSBuildProjectName).xml diff --git a/test/Dapr.Actors.AspNetCore.IntegrationTest.App/Dapr.Actors.AspNetCore.IntegrationTest.App.csproj b/test/Dapr.Actors.AspNetCore.IntegrationTest.App/Dapr.Actors.AspNetCore.IntegrationTest.App.csproj index af0bfa13e..c06d651f4 100644 --- a/test/Dapr.Actors.AspNetCore.IntegrationTest.App/Dapr.Actors.AspNetCore.IntegrationTest.App.csproj +++ b/test/Dapr.Actors.AspNetCore.IntegrationTest.App/Dapr.Actors.AspNetCore.IntegrationTest.App.csproj @@ -1,9 +1,5 @@ - - net6;net7 - - 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 1b8590d7e..deccfc1e6 100644 --- a/test/Dapr.Actors.AspNetCore.IntegrationTest/Dapr.Actors.AspNetCore.IntegrationTest.csproj +++ b/test/Dapr.Actors.AspNetCore.IntegrationTest/Dapr.Actors.AspNetCore.IntegrationTest.csproj @@ -1,8 +1,4 @@ - - net6;net7 - - 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 962963ac2..7e352d007 100644 --- a/test/Dapr.Actors.AspNetCore.Test/Dapr.Actors.AspNetCore.Test.csproj +++ b/test/Dapr.Actors.AspNetCore.Test/Dapr.Actors.AspNetCore.Test.csproj @@ -1,7 +1,6 @@ - net6;net7 Dapr.Actors.AspNetCore diff --git a/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj b/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj index 8a0fa16d7..8852dd465 100644 --- a/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj +++ b/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj @@ -1,6 +1,5 @@  - net6;net7 Dapr.Actors $(DefineConstants);ACTORS diff --git a/test/Dapr.AspNetCore.IntegrationTest.App/Dapr.AspNetCore.IntegrationTest.App.csproj b/test/Dapr.AspNetCore.IntegrationTest.App/Dapr.AspNetCore.IntegrationTest.App.csproj index 0e15b6855..f415639be 100644 --- a/test/Dapr.AspNetCore.IntegrationTest.App/Dapr.AspNetCore.IntegrationTest.App.csproj +++ b/test/Dapr.AspNetCore.IntegrationTest.App/Dapr.AspNetCore.IntegrationTest.App.csproj @@ -1,9 +1,5 @@  - - net6;net7 - - diff --git a/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj b/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj index c3cfd2cbf..3cd79d908 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj +++ b/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj @@ -1,7 +1,4 @@  - - net6;net7 - diff --git a/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj b/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj index 27f11a308..aa463be98 100644 --- a/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj +++ b/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj @@ -1,7 +1,4 @@  - - net6;net7 - diff --git a/test/Dapr.Client.Test/Dapr.Client.Test.csproj b/test/Dapr.Client.Test/Dapr.Client.Test.csproj index c6de11e31..aef5b4113 100644 --- a/test/Dapr.Client.Test/Dapr.Client.Test.csproj +++ b/test/Dapr.Client.Test/Dapr.Client.Test.csproj @@ -1,7 +1,4 @@  - - net6;net7 - diff --git a/test/Dapr.Client.Test/PublishEventApiTest.cs b/test/Dapr.Client.Test/PublishEventApiTest.cs index d8caf63d1..77d6ee905 100644 --- a/test/Dapr.Client.Test/PublishEventApiTest.cs +++ b/test/Dapr.Client.Test/PublishEventApiTest.cs @@ -11,6 +11,12 @@ // limitations under the License. // ------------------------------------------------------------------------ +using System.Collections.Immutable; +using System.Linq; +using System.Net.Http; +using System.Text.Json.Serialization; +using Grpc.Net.Client; + namespace Dapr.Client.Test { using System; @@ -51,6 +57,44 @@ public async Task PublishEventAsync_CanPublishTopicWithData() envelope.Metadata.Count.Should().Be(0); } + [Fact] + public async Task PublishEvent_ShouldRespectJsonStringEnumConverter() + { + //The following mimics how the TestClient is built, but adds the JsonStringEnumConverter to the serialization options + var handler = new TestClient.CapturingHandler(); + var httpClient = new HttpClient(handler); + var clientBuilder = new DaprClientBuilder() + .UseJsonSerializationOptions(new JsonSerializerOptions() + { + Converters = {new JsonStringEnumConverter(null, false)} + }) + .UseHttpClientFactory(() => httpClient) + .UseGrpcChannelOptions(new GrpcChannelOptions() + { + HttpClient = httpClient, ThrowOperationCanceledOnCancellation = true + }); + var client = new TestClient(clientBuilder.Build(), handler); + + //Ensure that the JsonStringEnumConverter is registered + client.InnerClient.JsonSerializerOptions.Converters.Count.Should().Be(1); + client.InnerClient.JsonSerializerOptions.Converters.First().GetType().Name.Should() + .Match(nameof(JsonStringEnumConverter)); + + var publishData = new Widget {Size = "Large", Color = WidgetColor.Red}; + var request = await client.CaptureGrpcRequestAsync(async daprClient => + { + await daprClient.PublishEventAsync(TestPubsubName, "test", publishData); + }); + + request.Dismiss(); + + var envelope = await request.GetRequestEnvelopeAsync(); + var jsonFromRequest = envelope.Data.ToStringUtf8(); + jsonFromRequest.Should() + .Be(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions)); + jsonFromRequest.Should().Match("{\"Size\":\"Large\",\"Color\":\"Red\"}"); + } + [Fact] public async Task PublishEventAsync_CanPublishTopicWithData_WithMetadata() { @@ -259,5 +303,18 @@ private class PublishData { public string PublishObjectParameter { get; set; } } + + private class Widget + { + public string Size { get; set; } + public WidgetColor Color { get; set; } + } + + private enum WidgetColor + { + Red, + Green, + Yellow + } } } diff --git a/test/Dapr.E2E.Test.Actors/Dapr.E2E.Test.Actors.csproj b/test/Dapr.E2E.Test.Actors/Dapr.E2E.Test.Actors.csproj index 0455d6db4..56ab3d222 100644 --- a/test/Dapr.E2E.Test.Actors/Dapr.E2E.Test.Actors.csproj +++ b/test/Dapr.E2E.Test.Actors/Dapr.E2E.Test.Actors.csproj @@ -1,9 +1,5 @@ - - net6;net7 - - 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 8dcf009bc..849870b98 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,7 +1,4 @@ - - net6;net7 - diff --git a/test/Dapr.E2E.Test.App.ReentrantActor/Dapr.E2E.Test.App.ReentrantActors.csproj b/test/Dapr.E2E.Test.App.ReentrantActor/Dapr.E2E.Test.App.ReentrantActors.csproj index dd087aef1..2fda3becc 100644 --- a/test/Dapr.E2E.Test.App.ReentrantActor/Dapr.E2E.Test.App.ReentrantActors.csproj +++ b/test/Dapr.E2E.Test.App.ReentrantActor/Dapr.E2E.Test.App.ReentrantActors.csproj @@ -1,9 +1,5 @@ - - net6;net7 - - 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 30d47e7c2..e6ad11456 100644 --- a/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj +++ b/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj @@ -1,13 +1,16 @@ - - net6;net7 - - + + + + + + + diff --git a/test/Dapr.E2E.Test.App/Program.cs b/test/Dapr.E2E.Test.App/Program.cs index e617558bf..5713129d5 100644 --- a/test/Dapr.E2E.Test.App/Program.cs +++ b/test/Dapr.E2E.Test.App/Program.cs @@ -1,23 +1,25 @@ -// ------------------------------------------------------------------------ -// 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. -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ +// 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.E2E.Test { using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; + using Serilog; public class Program { public static void Main(string[] args) { + Log.Logger = new LoggerConfiguration().WriteTo.File("log.txt").CreateLogger(); CreateHostBuilder(args).Build().Run(); } @@ -26,6 +28,6 @@ public static IHostBuilder CreateHostBuilder(string[] args) => .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); - }); + }).UseSerilog(); } } diff --git a/test/Dapr.E2E.Test.App/Startup.cs b/test/Dapr.E2E.Test.App/Startup.cs index 34e1b2eb8..8207c5883 100644 --- a/test/Dapr.E2E.Test.App/Startup.cs +++ b/test/Dapr.E2E.Test.App/Startup.cs @@ -29,6 +29,8 @@ namespace Dapr.E2E.Test using Microsoft.Extensions.Hosting; using System.Threading.Tasks; using System; + using Microsoft.Extensions.Logging; + using Serilog; /// /// Startup class. @@ -61,6 +63,10 @@ public void ConfigureServices(IServiceCollection services) services.AddAuthentication().AddDapr(); services.AddAuthorization(o => o.AddDapr()); services.AddControllers().AddDapr(); + services.AddLogging(builder => + { + builder.AddConsole(); + }); // Register a workflow and associated activity services.AddDaprWorkflow(options => { @@ -70,6 +76,11 @@ public void ConfigureServices(IServiceCollection services) var itemToPurchase = input; + // There are 5 of the same event to test that multiple similarly-named events can be raised in parallel + await context.WaitForExternalEventAsync("ChangePurchaseItem"); + await context.WaitForExternalEventAsync("ChangePurchaseItem"); + await context.WaitForExternalEventAsync("ChangePurchaseItem"); + await context.WaitForExternalEventAsync("ChangePurchaseItem"); itemToPurchase = await context.WaitForExternalEventAsync("ChangePurchaseItem"); // In real life there are other steps related to placing an order, like reserving @@ -103,11 +114,19 @@ public void ConfigureServices(IServiceCollection services) /// Webhost environment. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { + var logger = new LoggerConfiguration().WriteTo.File("log.txt").CreateLogger(); + + var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddSerilog(logger); + }); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } + app.UseSerilogRequestLogging(); + app.UseRouting(); app.UseAuthentication(); diff --git a/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj b/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj index 8b1448759..f899167c4 100644 --- a/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj +++ b/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj @@ -1,7 +1,4 @@  - - net6;net7 - diff --git a/test/Dapr.E2E.Test/DaprTestApp.cs b/test/Dapr.E2E.Test/DaprTestApp.cs index d65e21fd6..83f9948ac 100644 --- a/test/Dapr.E2E.Test/DaprTestApp.cs +++ b/test/Dapr.E2E.Test/DaprTestApp.cs @@ -132,16 +132,14 @@ public void Stop() private static string GetTargetFrameworkName() { var targetFrameworkName = ((TargetFrameworkAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(TargetFrameworkAttribute), false).FirstOrDefault()).FrameworkName; - string frameworkMoniker; - if (targetFrameworkName == ".NETCoreApp,Version=v6.0") - { - frameworkMoniker = "net6"; - } - else + + return targetFrameworkName switch { - frameworkMoniker = "net7"; - } - return frameworkMoniker; + ".NETCoreApp,Version=v6.0" => "net6", + ".NETCoreApp,Version=v7.0" => "net7", + ".NETCoreApp,Version=v8.0" => "net8", + _ => throw new InvalidOperationException($"Unsupported target framework: {targetFrameworkName}") + }; } private static (int, int, int, int) GetFreePorts() diff --git a/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs b/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs index ae30a9151..d95929ca3 100644 --- a/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs +++ b/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs @@ -11,25 +11,81 @@ // 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 System.Diagnostics; 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"); + } + } [Fact] public async Task TestWorkflows() { - string instanceId = "testInstanceId"; - string instanceId2 = "EventRaiseId"; - string workflowComponent = "dapr"; - string workflowName = "PlaceOrder"; + var instanceId = "testInstanceId"; + var instanceId2 = "EventRaiseId"; + var workflowComponent = "dapr"; + var workflowName = "PlaceOrder"; object input = "paperclips"; Dictionary workflowOptions = new Dictionary(); workflowOptions.Add("task_queue", "testQueue"); @@ -82,7 +138,7 @@ public async Task TestWorkflows() } catch (DaprException ex) { - ex.InnerException.Message.Should().Contain("No such instance exists", $"Instance {instanceId} was not correctly purged"); + ex.InnerException.Message.Should().Contain("no such instance exists", $"Instance {instanceId} was not correctly purged"); } // Start another workflow for event raising purposes @@ -93,8 +149,16 @@ public async Task TestWorkflows() input: input, workflowOptions: workflowOptions); - // RAISE EVENT TEST - await daprClient.RaiseWorkflowEventAsync(instanceId2, workflowComponent, "ChangePurchaseItem", "computers"); + // 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)); 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 0cd57ca67..2e4523582 100644 --- a/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj +++ b/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj @@ -1,7 +1,4 @@  - - net6;net7 - diff --git a/test/Directory.Build.props b/test/Directory.Build.props index e7d30ea2d..0ce23c19e 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -2,6 +2,8 @@ + net6;net7;net8 + $(RepoRoot)bin\$(Configuration)\test\$(MSBuildProjectName)\