mirror of https://github.com/dapr/dotnet-sdk.git
Merge branch 'release-1.16' into actor-timer-return-value
Signed-off-by: Whit Waldo <whit.waldo@innovian.net>
This commit is contained in:
commit
3f5be552cb
|
@ -66,12 +66,18 @@ dotnet new web MyApp
|
|||
```
|
||||
|
||||
Next we'll configure the AppHost project to add the necessary package to support local Dapr development. Navigate
|
||||
into the AppHost directory with the following and install the `Aspire.Hosting.Dapr` package from NuGet into the project.
|
||||
into the AppHost directory with the following and install the `CommunityToolkit.Aspire.Hosting.Dapr` package from NuGet into the project.
|
||||
We'll also add a reference to our `MyApp` project so we can reference it during the registration process.
|
||||
|
||||
{{% alert color="primary" %}}
|
||||
|
||||
This package was previously called `Aspire.Hosting.Dapr`, which has been [marked as deprecated](https://www.nuget.org/packages/Aspire.Hosting.Dapr).
|
||||
|
||||
{{% /alert %}}
|
||||
|
||||
```sh
|
||||
cd aspiredemo.AppHost
|
||||
dotnet add package Aspire.Hosting.Dapr
|
||||
dotnet add package CommunityToolkit.Aspire.Hosting.Dapr
|
||||
dotnet add reference ../MyApp/
|
||||
```
|
||||
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
---
|
||||
type: docs
|
||||
title: "Dapr .NET SDK Development with Dapr CLI"
|
||||
linkTitle: "Experimental Attributes"
|
||||
weight: 61000
|
||||
description: Learn about local development with the Dapr CLI
|
||||
---
|
||||
|
||||
## Experimental Attributes
|
||||
|
||||
### Introduction to Experimental Attributes
|
||||
|
||||
With the release of .NET 8, C# 12 introduced the `[Experimental]` attribute, which provides a standardized way to mark
|
||||
APIs that are still in development or experimental. This attribute is defined in the `System.Diagnostics.CodeAnalysis`
|
||||
namespace and requires a diagnostic ID parameter used to generate compiler warnings when the experimental API
|
||||
is used.
|
||||
|
||||
In the Dapr .NET SDK, we now use the `[Experimental]` attribute instead of `[Obsolete]` to mark building blocks and
|
||||
components that have not yet passed the stable lifecycle certification. This approach provides a clearer distinction
|
||||
between:
|
||||
|
||||
1. **Experimental APIs** - Features that are available but still evolving and have not yet been certified as stable
|
||||
according to the [Dapr Component Certification Lifecycle](https://docs.dapr.io/operations/components/certification-lifecycle/).
|
||||
|
||||
2. **Obsolete APIs** - Features that are truly deprecated and will be removed in a future release.
|
||||
|
||||
### Usage in the Dapr .NET SDK
|
||||
|
||||
In the Dapr .NET SDK, we apply the `[Experimental]` attribute at the class level for building blocks that are still in
|
||||
the Alpha or Beta stages of the [Component Certification Lifecycle](https://docs.dapr.io/operations/components/certification-lifecycle/).
|
||||
The attribute includes:
|
||||
|
||||
- A diagnostic ID that identifies the experimental building block
|
||||
- A URL that points to the relevant documentation for that block
|
||||
|
||||
For example:
|
||||
|
||||
```csharp
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
namespace Dapr.Cryptography.Encryption
|
||||
{
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
public class DaprEncryptionClient
|
||||
{
|
||||
// Implementation
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The diagnostic IDs follow a naming convention of `DAPR_[BUILDING_BLOCK_NAME]`, such as:
|
||||
|
||||
- `DAPR_CONVERSATION` - For the Conversation building block
|
||||
- `DAPR_CRYPTOGRAPHY` - For the Cryptography building block
|
||||
- `DAPR_JOBS` - For the Jobs building block
|
||||
- `DAPR_DISTRIBUTEDLOCK` - For the Distributed Lock building block
|
||||
|
||||
### Suppressing Experimental Warnings
|
||||
|
||||
When you use APIs marked with the `[Experimental]` attribute, the compiler will generate errors.
|
||||
To build your solution without marking your own code as experimental, you will need to suppress these errors. Here are
|
||||
several approaches to do this:
|
||||
|
||||
#### Option 1: Using #pragma directive
|
||||
|
||||
You can use the `#pragma warning` directive to suppress the warning for specific sections of code:
|
||||
|
||||
```csharp
|
||||
// Disable experimental warning
|
||||
#pragma warning disable DAPR_CRYPTOGRAPHY
|
||||
// Your code using the experimental API
|
||||
var client = new DaprEncryptionClient();
|
||||
// Re-enable the warning
|
||||
#pragma warning restore DAPR_CRYPTOGRAPHY
|
||||
```
|
||||
|
||||
This approach is useful when you want to suppress warnings only for specific sections of your code.
|
||||
|
||||
#### Option 2: Project-level suppression
|
||||
|
||||
To suppress warnings for an entire project, add the following to your `.csproj` file.
|
||||
file.
|
||||
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<NoWarn>$(NoWarn);DAPR_CRYPTOGRAPHY</NoWarn>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
You can include multiple diagnostic IDs separated by semicolons:
|
||||
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<NoWarn>$(NoWarn);DAPR_CONVERSATION;DAPR_JOBS;DAPR_DISTRIBUTEDLOCK;DAPR_CRYPTOGRAPHY</NoWarn>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
This approach is particularly useful for test projects that need to use experimental APIs.
|
||||
|
||||
#### Option 3: Directory-level suppression
|
||||
|
||||
For suppressing warnings across multiple projects in a directory, add a `Directory.Build.props` file:
|
||||
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<NoWarn>$(NoWarn);DAPR_CONVERSATION;DAPR_JOBS;DAPR_DISTRIBUTEDLOCK;DAPR_CRYPTOGRAPHY</NoWarn>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
This file should be placed in the root directory of your test projects. You can learn more about using
|
||||
`Directory.Build.props` files in the
|
||||
[MSBuild documentation](https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-by-directory).
|
||||
|
||||
### Lifecycle of Experimental APIs
|
||||
|
||||
As building blocks move through the certification lifecycle and reach the "Stable" stage, the `[Experimental]` attribute will be removed. No migration or code changes will be required from users when this happens, except for the removal of any warning suppressions if they were added.
|
||||
|
||||
Conversely, the `[Obsolete]` attribute will now be reserved exclusively for APIs that are truly deprecated and scheduled for removal. When you see a method or class marked with `[Obsolete]`, you should plan to migrate away from it according to the migration guidance provided in the attribute message.
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **In application code:**
|
||||
- Be cautious when using experimental APIs, as they may change in future releases
|
||||
- Consider isolating usage of experimental APIs to make future updates easier
|
||||
- Document your use of experimental APIs for team awareness
|
||||
|
||||
2. **In test code:**
|
||||
- Use project-level suppression to avoid cluttering test code with warning suppressions
|
||||
- Regularly review which experimental APIs you're using and check if they've been stabilized
|
||||
|
||||
3. **When contributing to the SDK:**
|
||||
- Use `[Experimental]` for new building blocks that haven't completed certification
|
||||
- Use `[Obsolete]` only for truly deprecated APIs
|
||||
- Provide clear documentation links in the `UrlFormat` parameter
|
||||
|
||||
### Additional Resources
|
||||
|
||||
- [Dapr Component Certification Lifecycle](https://docs.dapr.io/operations/components/certification-lifecycle/)
|
||||
- [C# Experimental Attribute Documentation](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-12.0/experimental-attribute)
|
|
@ -0,0 +1,72 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2023 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.Buffers;
|
||||
using Dapr.Client;
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
|
||||
namespace Cryptography.Examples
|
||||
{
|
||||
internal class EncryptDecryptFileStreamExample(string componentName, string keyName) : Example
|
||||
{
|
||||
public override string DisplayName => "Use Cryptography to encrypt and decrypt a file";
|
||||
public override async Task RunAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using var client = new DaprClientBuilder().Build();
|
||||
|
||||
// The name of the file we're using as an example
|
||||
const string fileName = "file.txt";
|
||||
|
||||
Console.WriteLine("Original file contents:");
|
||||
foreach (var line in await File.ReadAllLinesAsync(fileName, cancellationToken))
|
||||
{
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
|
||||
//Encrypt from a file stream and buffer the resulting bytes to an in-memory buffer
|
||||
await using var encryptFs = new FileStream(fileName, FileMode.Open);
|
||||
|
||||
var bufferedEncryptedBytes = new ArrayBufferWriter<byte>();
|
||||
await foreach (var bytes in (client.EncryptAsync(componentName, encryptFs, keyName,
|
||||
new EncryptionOptions(KeyWrapAlgorithm.Rsa), cancellationToken)))
|
||||
{
|
||||
bufferedEncryptedBytes.Write(bytes.Span);
|
||||
}
|
||||
|
||||
Console.WriteLine("Encrypted bytes:");
|
||||
Console.WriteLine(Convert.ToBase64String(bufferedEncryptedBytes.WrittenMemory.ToArray()));
|
||||
|
||||
//We'll write to a temporary file via a FileStream
|
||||
var tempDecryptedFile = Path.GetTempFileName();
|
||||
await using var decryptFs = new FileStream(tempDecryptedFile, FileMode.Create);
|
||||
|
||||
//We'll stream the decrypted bytes from a MemoryStream into the above temporary file
|
||||
await using var encryptedMs = new MemoryStream(bufferedEncryptedBytes.WrittenMemory.ToArray());
|
||||
await foreach (var result in (client.DecryptAsync(componentName, encryptedMs, keyName,
|
||||
cancellationToken)))
|
||||
{
|
||||
decryptFs.Write(result.Span);
|
||||
}
|
||||
|
||||
decryptFs.Close();
|
||||
|
||||
//Let's confirm the value as written to the file
|
||||
var decryptedValue = await File.ReadAllTextAsync(tempDecryptedFile, cancellationToken);
|
||||
Console.WriteLine("Decrypted value: ");
|
||||
Console.WriteLine(decryptedValue);
|
||||
|
||||
//And some cleanup to delete our temp file
|
||||
File.Delete(tempDecryptedFile);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -11,24 +12,15 @@ using Microsoft.Extensions.Logging;
|
|||
namespace DistributedLock.Controllers;
|
||||
|
||||
[ApiController]
|
||||
public class BindingController : ControllerBase
|
||||
[Experimental("DAPR_DISTRIBUTEDLOCK", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/distributed-lock/distributed-lock-api-overview/")]
|
||||
public class BindingController(DaprClient client, ILogger<BindingController> logger) : ControllerBase
|
||||
{
|
||||
private DaprClient client;
|
||||
private ILogger<BindingController> logger;
|
||||
private string appId;
|
||||
|
||||
public BindingController(DaprClient client, ILogger<BindingController> logger)
|
||||
{
|
||||
this.client = client;
|
||||
this.logger = logger;
|
||||
this.appId = Environment.GetEnvironmentVariable("APP_ID");
|
||||
}
|
||||
|
||||
private string appId = Environment.GetEnvironmentVariable("APP_ID");
|
||||
|
||||
[HttpPost("cronbinding")]
|
||||
[Obsolete]
|
||||
public async Task<IActionResult> HandleBindingEvent()
|
||||
{
|
||||
logger.LogInformation($"Received binding event on {appId}, scanning for work.");
|
||||
logger.LogInformation("Received binding event on {appId}, scanning for work.", appId);
|
||||
|
||||
var request = new BindingRequest("localstorage", "list");
|
||||
var result = client.InvokeBindingAsync(request);
|
||||
|
@ -47,44 +39,40 @@ public class BindingController : ControllerBase
|
|||
return Ok();
|
||||
}
|
||||
|
||||
|
||||
[Obsolete]
|
||||
private async Task AttemptToProcessFile(string fileName)
|
||||
{
|
||||
// Locks are Disposable and will automatically unlock at the end of a 'using' statement.
|
||||
logger.LogInformation($"Attempting to lock: {fileName}");
|
||||
await using (var fileLock = await client.Lock("redislock", fileName, appId, 60))
|
||||
logger.LogInformation("Attempting to lock: {fileName}", fileName);
|
||||
await using var fileLock = await client.Lock("redislock", fileName, appId, 60);
|
||||
if (fileLock.Success)
|
||||
{
|
||||
if (fileLock.Success)
|
||||
logger.LogInformation("Successfully locked file: {fileName}", fileName);
|
||||
|
||||
// Get the file after we've locked it, we're safe here because of the lock.
|
||||
var fileState = await GetFile(fileName);
|
||||
|
||||
if (fileState == null)
|
||||
{
|
||||
logger.LogInformation($"Successfully locked file: {fileName}");
|
||||
|
||||
// Get the file after we've locked it, we're safe here because of the lock.
|
||||
var fileState = await GetFile(fileName);
|
||||
|
||||
if (fileState == null)
|
||||
{
|
||||
logger.LogWarning($"File {fileName} has already been processed!");
|
||||
return;
|
||||
}
|
||||
|
||||
// "Analyze" the file before committing it to our remote storage.
|
||||
fileState.Analysis = fileState.Number > 50 ? "High" : "Low";
|
||||
|
||||
// Save it to remote storage.
|
||||
await client.SaveStateAsync("redisstore", fileName, fileState);
|
||||
|
||||
// Remove it from local storage.
|
||||
var bindingDeleteRequest = new BindingRequest("localstorage", "delete");
|
||||
bindingDeleteRequest.Metadata["fileName"] = fileName;
|
||||
await client.InvokeBindingAsync(bindingDeleteRequest);
|
||||
|
||||
logger.LogInformation($"Done processing {fileName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogWarning($"Failed to lock {fileName}.");
|
||||
logger.LogWarning("File {fileName} has already been processed!", fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
// "Analyze" the file before committing it to our remote storage.
|
||||
fileState.Analysis = fileState.Number > 50 ? "High" : "Low";
|
||||
|
||||
// Save it to remote storage.
|
||||
await client.SaveStateAsync("redisstore", fileName, fileState);
|
||||
|
||||
// Remove it from local storage.
|
||||
var bindingDeleteRequest = new BindingRequest("localstorage", "delete");
|
||||
bindingDeleteRequest.Metadata["fileName"] = fileName;
|
||||
await client.InvokeBindingAsync(bindingDeleteRequest);
|
||||
|
||||
logger.LogInformation("Done processing {fileName}", fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogWarning("Failed to lock {fileName}.", fileName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,4 +91,4 @@ public class BindingController : ControllerBase
|
|||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,5 +7,6 @@
|
|||
<OutputPath>$(RepoRoot)bin\$(Configuration)\examples\$(MSBuildProjectName)\</OutputPath>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<NoWarn>$(NoWarn);DAPR_CONVERSATION;DAPR_JOBS;DAPR_DISTRIBUTEDLOCK;DAPR_CRYPTOGRAPHY</NoWarn>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -11,6 +11,7 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1.Dapr;
|
||||
|
||||
namespace Dapr.AI.Conversation;
|
||||
|
@ -30,6 +31,7 @@ namespace Dapr.AI.Conversation;
|
|||
/// exhaustion and other problems.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Experimental("DAPR_CONVERSATION", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/conversation/conversation-overview/")]
|
||||
public abstract class DaprConversationClient : DaprAIClient
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Dapr.Common;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1.Dapr;
|
||||
|
@ -21,6 +22,7 @@ namespace Dapr.AI.Conversation;
|
|||
/// Used to create a new instance of a <see cref="DaprConversationClient"/>.
|
||||
/// </summary>
|
||||
/// <param name="configuration">An optional <see cref="IConfiguration"/> to configure the client with.</param>
|
||||
[Experimental("DAPR_CONVERSATION", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/conversation/conversation-overview/")]
|
||||
public sealed class DaprConversationClientBuilder(IConfiguration? configuration = null) : DaprGenericClientBuilder<DaprConversationClient>(configuration)
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -30,6 +32,7 @@ public sealed class DaprConversationClientBuilder(IConfiguration? configuration
|
|||
/// <summary>
|
||||
/// Builds the client instance from the properties of the builder.
|
||||
/// </summary>
|
||||
[Experimental("DAPR_CONVERSATION", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/conversation/conversation-overview/")]
|
||||
public override DaprConversationClient Build()
|
||||
{
|
||||
var daprClientDependencies = BuildDaprClientDependencies(typeof(DaprConversationClient).Assembly);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Dapr.Common;
|
||||
using Dapr.Common.Extensions;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
|
||||
|
@ -23,6 +24,7 @@ namespace Dapr.AI.Conversation;
|
|||
/// <param name="client">The Dapr client.</param>
|
||||
/// <param name="httpClient">The HTTP client used by the client for calling the Dapr runtime.</param>
|
||||
/// <param name="daprApiToken">An optional token required to send requests to the Dapr sidecar.</param>
|
||||
[Experimental("DAPR_CONVERSATION", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/conversation/conversation-overview/")]
|
||||
internal sealed class DaprConversationGrpcClient(Autogenerated.Dapr.DaprClient client, HttpClient httpClient, string? daprApiToken = null) : DaprConversationClient(client, httpClient, daprApiToken: daprApiToken)
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Dapr.Common.Extensions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
|
@ -24,6 +25,7 @@ public static class DaprAiConversationBuilderExtensions
|
|||
/// <summary>
|
||||
/// Registers the necessary functionality for the Dapr AI Conversation functionality.
|
||||
/// </summary>
|
||||
[Experimental("DAPR_CONVERSATION", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/conversation/conversation-overview/")]
|
||||
public static IDaprAiConversationBuilder AddDaprConversationClient(
|
||||
this IServiceCollection services,
|
||||
Action<IServiceProvider, DaprConversationClientBuilder>? configure = null,
|
||||
|
|
|
@ -68,13 +68,12 @@ internal static class DurationExtensions
|
|||
|
||||
if (period.StartsWith(MonthlyPrefixPeriod))
|
||||
{
|
||||
var dateTime = DateTime.UtcNow;
|
||||
return dateTime.AddMonths(1) - dateTime;
|
||||
return TimeSpan.FromDays(30);
|
||||
}
|
||||
|
||||
if (period.StartsWith(MidnightPrefixPeriod))
|
||||
{
|
||||
return new TimeSpan();
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
|
||||
if (period.StartsWith(WeeklyPrefixPeriod))
|
||||
|
|
|
@ -220,14 +220,12 @@ internal sealed class ActorManager
|
|||
}
|
||||
}
|
||||
|
||||
internal async Task<bool> FireTimerAsync(
|
||||
ActorId actorId,
|
||||
Stream requestBodyStream,
|
||||
CancellationToken cancellationToken = default)
|
||||
|
||||
internal async Task FireTimerAsync(ActorId actorId, Stream requestBodyStream, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#pragma warning disable 0618
|
||||
var timerData = await JsonSerializer.DeserializeAsync<TimerInfo>(requestBodyStream, cancellationToken: cancellationToken);
|
||||
#pragma warning restore 0618
|
||||
#pragma warning disable 0618
|
||||
var timerData = await DeserializeAsync(requestBodyStream);
|
||||
#pragma warning restore 0618
|
||||
|
||||
return await this.DispatchInternalAsync(actorId, this.timerMethodContext, RequestFunc, cancellationToken);
|
||||
|
||||
|
@ -255,6 +253,62 @@ internal sealed class ActorManager
|
|||
}
|
||||
}
|
||||
|
||||
#pragma warning disable 0618
|
||||
internal static async Task<TimerInfo> DeserializeAsync(Stream stream)
|
||||
{
|
||||
var json = await JsonSerializer.DeserializeAsync<JsonElement>(stream);
|
||||
if (json.ValueKind == JsonValueKind.Null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var setAnyProperties = false; // Used to determine if anything was actually deserialized
|
||||
var dueTime = TimeSpan.Zero;
|
||||
var callback = "";
|
||||
var period = TimeSpan.Zero;
|
||||
var data = Array.Empty<byte>();
|
||||
TimeSpan? ttl = null;
|
||||
if (json.TryGetProperty("callback", out var callbackProperty))
|
||||
{
|
||||
setAnyProperties = true;
|
||||
callback = callbackProperty.GetString();
|
||||
}
|
||||
if (json.TryGetProperty("dueTime", out var dueTimeProperty))
|
||||
{
|
||||
setAnyProperties = true;
|
||||
var dueTimeString = dueTimeProperty.GetString();
|
||||
dueTime = ConverterUtils.ConvertTimeSpanFromDaprFormat(dueTimeString);
|
||||
}
|
||||
|
||||
if (json.TryGetProperty("period", out var periodProperty))
|
||||
{
|
||||
setAnyProperties = true;
|
||||
var periodString = periodProperty.GetString();
|
||||
(period, _) = ConverterUtils.ConvertTimeSpanValueFromISO8601Format(periodString);
|
||||
}
|
||||
|
||||
if (json.TryGetProperty("data", out var dataProperty) && dataProperty.ValueKind != JsonValueKind.Null)
|
||||
{
|
||||
setAnyProperties = true;
|
||||
data = dataProperty.GetBytesFromBase64();
|
||||
}
|
||||
|
||||
if (json.TryGetProperty("ttl", out var ttlProperty))
|
||||
{
|
||||
setAnyProperties = true;
|
||||
var ttlString = ttlProperty.GetString();
|
||||
ttl = ConverterUtils.ConvertTimeSpanFromDaprFormat(ttlString);
|
||||
}
|
||||
|
||||
if (!setAnyProperties)
|
||||
{
|
||||
return null; //No properties were ever deserialized, so return null instead of default values
|
||||
}
|
||||
|
||||
return new TimerInfo(callback, data, dueTime, period, ttl);
|
||||
}
|
||||
#pragma warning restore 0618
|
||||
|
||||
internal async Task ActivateActorAsync(ActorId actorId)
|
||||
{
|
||||
// An actor is activated by "Dapr" runtime when a call is to be made for an actor.
|
||||
|
|
|
@ -49,7 +49,7 @@ internal static class ConverterUtils
|
|||
int msIndex = spanOfValue.IndexOf("ms");
|
||||
|
||||
// handle days from hours.
|
||||
var hoursSpan = spanOfValue.Slice(0, hIndex);
|
||||
var hoursSpan = spanOfValue[..hIndex];
|
||||
var hours = int.Parse(hoursSpan);
|
||||
var days = hours / 24;
|
||||
hours %= 24;
|
||||
|
@ -103,7 +103,7 @@ internal static class ConverterUtils
|
|||
builder.Append($"{value.Days}D");
|
||||
}
|
||||
|
||||
builder.Append("T");
|
||||
builder.Append('T');
|
||||
|
||||
if(value.Hours > 0)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2025 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.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using Google.Protobuf;
|
||||
using Grpc.Core;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
|
||||
namespace Dapr.Client.Crypto;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the implementation to decrypt a stream of plaintext data with the Dapr runtime.
|
||||
/// </summary>
|
||||
internal sealed class DecryptionStreamProcessor : IDisposable
|
||||
{
|
||||
private bool disposed;
|
||||
private readonly Channel<ReadOnlyMemory<byte>> outputChannel = Channel.CreateUnbounded<ReadOnlyMemory<byte>>();
|
||||
|
||||
/// <summary>
|
||||
/// Surfaces any exceptions encountered while asynchronously processing the inbound and outbound streams.
|
||||
/// </summary>
|
||||
internal event EventHandler<Exception>? OnException;
|
||||
|
||||
/// <summary>
|
||||
/// Sends the provided bytes in chunks to the sidecar for the encryption operation.
|
||||
/// </summary>
|
||||
/// <param name="inputStream">The stream containing the bytes to decrypt.</param>
|
||||
/// <param name="call">The call to make to the sidecar to process the encryption operation.</param>
|
||||
/// <param name="streamingBlockSizeInBytes">The size, in bytes, of the streaming blocks.</param>
|
||||
/// <param name="options">The decryption options.</param>
|
||||
/// <param name="cancellationToken">Token used to cancel the ongoing request.</param>
|
||||
public async Task ProcessStreamAsync(
|
||||
Stream inputStream,
|
||||
AsyncDuplexStreamingCall<Autogenerated.DecryptRequest, Autogenerated.DecryptResponse> call,
|
||||
int streamingBlockSizeInBytes,
|
||||
Autogenerated.DecryptRequestOptions options,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
//Read from the input stream and write to the gRPC call
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var bufferedStream = new BufferedStream(inputStream, streamingBlockSizeInBytes);
|
||||
var buffer = new byte[streamingBlockSizeInBytes];
|
||||
int bytesRead;
|
||||
ulong sequenceNumber = 0;
|
||||
|
||||
while ((bytesRead = await bufferedStream.ReadAsync(buffer, cancellationToken)) > 0)
|
||||
{
|
||||
var request = new Autogenerated.DecryptRequest
|
||||
{
|
||||
Payload = new Autogenerated.StreamPayload
|
||||
{
|
||||
Data = ByteString.CopyFrom(buffer, 0, bytesRead), Seq = sequenceNumber
|
||||
}
|
||||
};
|
||||
|
||||
//Only include the options in the first message
|
||||
if (sequenceNumber == 0)
|
||||
{
|
||||
request.Options = options;
|
||||
}
|
||||
|
||||
await call.RequestStream.WriteAsync(request, cancellationToken);
|
||||
|
||||
//Increment the sequence number
|
||||
sequenceNumber++;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
// Expected cancellation exception
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnException?.Invoke(this, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await call.RequestStream.CompleteAsync();
|
||||
}
|
||||
}, cancellationToken);
|
||||
|
||||
//Start reading from the gRPC call and writing to the output channel
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await foreach (var response in call.ResponseStream.ReadAllAsync(cancellationToken))
|
||||
{
|
||||
await outputChannel.Writer.WriteAsync(response.Payload.Data.Memory, cancellationToken);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnException?.Invoke(this, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
outputChannel.Writer.Complete();
|
||||
}
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the processed bytes from the operation from the sidecar and
|
||||
/// returns as an enumerable stream.
|
||||
/// </summary>
|
||||
public async IAsyncEnumerable<ReadOnlyMemory<byte>> GetProcessedDataAsync([EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
await foreach (var data in outputChannel.Reader.ReadAllAsync(cancellationToken))
|
||||
{
|
||||
yield return data;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
outputChannel.Writer.TryComplete();
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2025 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.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using Google.Protobuf;
|
||||
using Grpc.Core;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
|
||||
namespace Dapr.Client.Crypto;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the implementation to encrypt a stream of plaintext data with the Dapr runtime.
|
||||
/// </summary>
|
||||
internal sealed class EncryptionStreamProcessor : IDisposable
|
||||
{
|
||||
private bool disposed;
|
||||
private readonly Channel<ReadOnlyMemory<byte>> outputChannel = Channel.CreateUnbounded<ReadOnlyMemory<byte>>();
|
||||
|
||||
/// <summary>
|
||||
/// Surfaces any exceptions encountered while asynchronously processing the inbound and outbound streams.
|
||||
/// </summary>
|
||||
internal event EventHandler<Exception>? OnException;
|
||||
|
||||
/// <summary>
|
||||
/// Sends the provided bytes in chunks to the sidecar for the encryption operation.
|
||||
/// </summary>
|
||||
/// <param name="inputStream">The stream containing the bytes to encrypt.</param>
|
||||
/// <param name="call">The call to make to the sidecar to process the encryption operation.</param>
|
||||
/// <param name="options">The encryption options.</param>
|
||||
/// <param name="streamingBlockSizeInBytes">The size, in bytes, of the streaming blocks.</param>
|
||||
/// <param name="cancellationToken">Token used to cancel the ongoing request.</param>
|
||||
public async Task ProcessStreamAsync(
|
||||
Stream inputStream,
|
||||
AsyncDuplexStreamingCall<Autogenerated.EncryptRequest, Autogenerated.EncryptResponse> call,
|
||||
Autogenerated.EncryptRequestOptions options,
|
||||
int streamingBlockSizeInBytes,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
//Read from the input stream and write to the gRPC call
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var bufferedStream = new BufferedStream(inputStream, streamingBlockSizeInBytes);
|
||||
var buffer = new byte[streamingBlockSizeInBytes];
|
||||
int bytesRead;
|
||||
ulong sequenceNumber = 0;
|
||||
|
||||
while ((bytesRead = await bufferedStream.ReadAsync(buffer, cancellationToken)) > 0)
|
||||
{
|
||||
var request = new Autogenerated.EncryptRequest
|
||||
{
|
||||
Payload = new Autogenerated.StreamPayload
|
||||
{
|
||||
Data = ByteString.CopyFrom(buffer, 0, bytesRead), Seq = sequenceNumber
|
||||
}
|
||||
};
|
||||
|
||||
//Only include the options in the first message
|
||||
if (sequenceNumber == 0)
|
||||
{
|
||||
request.Options = options;
|
||||
}
|
||||
|
||||
await call.RequestStream.WriteAsync(request, cancellationToken);
|
||||
|
||||
//Increment the sequence number
|
||||
sequenceNumber++;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
// Expected cancellation exception
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnException?.Invoke(this, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await call.RequestStream.CompleteAsync();
|
||||
}
|
||||
}, cancellationToken);
|
||||
|
||||
//Start reading from the gRPC call and writing to the output channel
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await foreach (var response in call.ResponseStream.ReadAllAsync(cancellationToken))
|
||||
{
|
||||
await outputChannel.Writer.WriteAsync(response.Payload.Data.Memory, cancellationToken);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnException?.Invoke(this, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
outputChannel.Writer.Complete();
|
||||
}
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the processed bytes from the operation from the sidecar and
|
||||
/// returns as an enumerable stream.
|
||||
/// </summary>
|
||||
public async IAsyncEnumerable<ReadOnlyMemory<byte>> GetProcessedDataAsync([EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
await foreach (var data in outputChannel.Reader.ReadAllAsync(cancellationToken))
|
||||
{
|
||||
yield return data;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
outputChannel.Writer.TryComplete();
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
|
@ -1089,7 +1090,7 @@ public abstract class DaprClient : IDisposable
|
|||
/// <param name="encryptionOptions">Options informing how the encryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of encrypted bytes.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
public abstract Task<ReadOnlyMemory<byte>> EncryptAsync(string vaultResourceName,
|
||||
ReadOnlyMemory<byte> plaintextBytes, string keyName, EncryptionOptions encryptionOptions,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
@ -1103,8 +1104,8 @@ public abstract class DaprClient : IDisposable
|
|||
/// <param name="encryptionOptions">Options informing how the encryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of encrypted bytes.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> EncryptAsync(string vaultResourceName, Stream plaintextStream, string keyName,
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
public abstract IAsyncEnumerable<ReadOnlyMemory<byte>> EncryptAsync(string vaultResourceName, Stream plaintextStream, string keyName,
|
||||
EncryptionOptions encryptionOptions, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
|
@ -1116,7 +1117,7 @@ public abstract class DaprClient : IDisposable
|
|||
/// <param name="options">Options informing how the decryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of decrypted bytes.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
public abstract Task<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName, ReadOnlyMemory<byte> ciphertextBytes, string keyName, DecryptionOptions options,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
|
@ -1128,7 +1129,7 @@ public abstract class DaprClient : IDisposable
|
|||
/// <param name="keyName">The name of the key to use from the Vault for the decryption operation.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of decrypted bytes.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
public abstract Task<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName,
|
||||
ReadOnlyMemory<byte> ciphertextBytes, string keyName, CancellationToken cancellationToken = default);
|
||||
|
||||
|
@ -1141,9 +1142,8 @@ public abstract class DaprClient : IDisposable
|
|||
/// <param name="options">Options informing how the decryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An asynchronously enumerable array of decrypted bytes.</returns>
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> DecryptAsync(string vaultResourceName, Stream ciphertextStream,
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
public abstract IAsyncEnumerable<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName, Stream ciphertextStream,
|
||||
string keyName, DecryptionOptions options, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
|
@ -1154,9 +1154,8 @@ public abstract class DaprClient : IDisposable
|
|||
/// <param name="keyName">The name of the key to use from the Vault for the decryption operation.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An asynchronously enumerable array of decrypted bytes.</returns>
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> DecryptAsync(string vaultResourceName, Stream ciphertextStream,
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
public abstract IAsyncEnumerable<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName, Stream ciphertextStream,
|
||||
string keyName, CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
|
@ -1171,7 +1170,7 @@ public abstract class DaprClient : IDisposable
|
|||
///// <param name="keyFormat">The format to use for the key result.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The name (and possibly version as name/version) of the key and its public key.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public abstract Task<(string Name, string PublicKey)> GetKeyAsync(string vaultResourceName, string keyName, SubtleGetKeyRequest.Types.KeyFormat keyFormat,
|
||||
// CancellationToken cancellationToken = default);
|
||||
|
||||
|
@ -1186,7 +1185,7 @@ public abstract class DaprClient : IDisposable
|
|||
///// <param name="associatedData">Any associated data when using AEAD ciphers.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The array of encrypted bytes.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public abstract Task<(byte[] CipherTextBytes, byte[] AuthenticationTag)> EncryptAsync(
|
||||
// string vaultResourceName,
|
||||
// byte[] plainTextBytes,
|
||||
|
@ -1206,7 +1205,7 @@ public abstract class DaprClient : IDisposable
|
|||
///// <param name="nonce">The bytes comprising the nonce.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The array of encrypted bytes.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public async Task<(byte[] CipherTextBytes, byte[] AuthenticationTag)> EncryptAsync(
|
||||
// string vaultResourceName,
|
||||
// byte[] plainTextBytes,
|
||||
|
@ -1229,7 +1228,7 @@ public abstract class DaprClient : IDisposable
|
|||
///// <param name="tag"></param>
|
||||
///// <param name="associatedData">Any associated data when using AEAD ciphers.</param>
|
||||
///// <returns>The array of plaintext bytes.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public abstract Task<byte[]> DecryptAsync(string vaultResourceName, byte[] cipherTextBytes,
|
||||
// string algorithm, string keyName, byte[] nonce, byte[] tag, byte[] associatedData,
|
||||
// CancellationToken cancellationToken = default);
|
||||
|
@ -1245,8 +1244,7 @@ public abstract class DaprClient : IDisposable
|
|||
///// <param name="nonce">The nonce value used.</param>
|
||||
///// <param name="tag"></param>
|
||||
///// <returns>The array of plaintext bytes.</returns>
|
||||
//[Obsolete(
|
||||
// "The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public async Task<byte[]> DecryptAsync(string vaultResourceName, byte[] cipherTextBytes,
|
||||
// string algorithm, string keyName, byte[] nonce, byte[] tag, CancellationToken cancellationToken = default) =>
|
||||
// await DecryptAsync(vaultResourceName, cipherTextBytes, algorithm, keyName, nonce, tag, Array.Empty<byte>(), cancellationToken);
|
||||
|
@ -1262,7 +1260,7 @@ public abstract class DaprClient : IDisposable
|
|||
///// <param name="associatedData">Any associated data when using AEAD ciphers.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The bytes comprising the wrapped plain-text key and the authentication tag, if applicable.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public abstract Task<(byte[] WrappedKey, byte[] AuthenticationTag)> WrapKeyAsync(string vaultResourceName, byte[] plainTextKey, string keyName, string algorithm, byte[] nonce, byte[] associatedData,
|
||||
// CancellationToken cancellationToken = default);
|
||||
|
||||
|
@ -1276,7 +1274,7 @@ public abstract class DaprClient : IDisposable
|
|||
///// <param name="nonce">The none used.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The bytes comprising the unwrapped key and the authentication tag, if applicable.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public async Task<(byte[] WrappedKey, byte[] AuthenticationTag)> WrapKeyAsync(string vaultResourceName, byte[] plainTextKey, string keyName, string algorithm,
|
||||
// byte[] nonce, CancellationToken cancellationToken = default) => await WrapKeyAsync(vaultResourceName, plainTextKey,
|
||||
// keyName, algorithm, nonce, Array.Empty<byte>(), cancellationToken);
|
||||
|
@ -1293,7 +1291,7 @@ public abstract class DaprClient : IDisposable
|
|||
///// <param name="associatedData">Any associated data when using AEAD ciphers.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The bytes comprising the unwrapped key.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public abstract Task<byte[]> UnwrapKeyAsync(string vaultResourceName, byte[] wrappedKey, string algorithm, string keyName, byte[] nonce, byte[] tag, byte[] associatedData,
|
||||
// CancellationToken cancellationToken = default);
|
||||
|
||||
|
@ -1308,7 +1306,7 @@ public abstract class DaprClient : IDisposable
|
|||
///// <param name="tag">The bytes comprising the authentication tag.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The bytes comprising the unwrapped key.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public async Task<byte[]> UnwrapKeyAsync(string vaultResourceName, byte[] wrappedKey, string algorithm, string keyName,
|
||||
// byte[] nonce, byte[] tag,
|
||||
// CancellationToken cancellationToken = default) => await UnwrapKeyAsync(vaultResourceName,
|
||||
|
@ -1324,7 +1322,7 @@ public abstract class DaprClient : IDisposable
|
|||
///// <param name="nonce">The nonce value.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The bytes comprising the unwrapped key.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public async Task<byte[]> UnwrapKeyAsync(string vaultResourceName, byte[] wrappedKey, string algorithm, string keyName,
|
||||
// byte[] nonce, CancellationToken cancellationToken = default) => await UnwrapKeyAsync(vaultResourceName,
|
||||
// wrappedKey, algorithm, keyName, nonce, Array.Empty<byte>(), Array.Empty<byte>(), cancellationToken);
|
||||
|
@ -1338,7 +1336,7 @@ public abstract class DaprClient : IDisposable
|
|||
///// <param name="keyName">The name of the key used.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The bytes comprising the signature.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public abstract Task<byte[]> SignAsync(string vaultResourceName, byte[] digest, string algorithm, string keyName,
|
||||
// CancellationToken cancellationToken = default);
|
||||
|
||||
|
@ -1352,12 +1350,14 @@ public abstract class DaprClient : IDisposable
|
|||
///// <param name="keyName">The name of the key used.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns><c>True</c> if the signature verification is successful; otherwise <c>false</c>.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public abstract Task<bool> VerifyAsync(string vaultResourceName, byte[] digest, byte[] signature, string algorithm, string keyName,
|
||||
// CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Distributed Lock
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to lock the given resourceId with response indicating success.
|
||||
/// </summary>
|
||||
|
@ -1367,7 +1367,7 @@ public abstract class DaprClient : IDisposable
|
|||
/// <param name="expiryInSeconds">The time after which the lock gets expired.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken" /> that can be used to cancel the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> containing a <see cref="TryLockResponse"/></returns>
|
||||
[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
[Experimental("DAPR_DISTRIBUTEDLOCK", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/distributed-lock/distributed-lock-api-overview/")]
|
||||
public abstract Task<TryLockResponse> Lock(
|
||||
string storeName,
|
||||
string resourceId,
|
||||
|
@ -1384,12 +1384,14 @@ public abstract class DaprClient : IDisposable
|
|||
/// <param name="lockOwner">Indicates the identifier of lock owner.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken" /> that can be used to cancel the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> containing a <see cref="UnlockResponse"/></returns>
|
||||
[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
[Experimental("DAPR_DISTRIBUTEDLOCK", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/distributed-lock/distributed-lock-api-overview/")]
|
||||
public abstract Task<UnlockResponse> Unlock(
|
||||
string storeName,
|
||||
string resourceId,
|
||||
string lockOwner,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
namespace Dapr.Client;
|
||||
|
||||
using Crypto;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
|
@ -28,6 +29,7 @@ using Google.Protobuf;
|
|||
using Google.Protobuf.WellKnownTypes;
|
||||
using Grpc.Core;
|
||||
using Grpc.Net.Client;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
|
||||
|
||||
/// <summary>
|
||||
|
@ -1666,19 +1668,17 @@ internal class DaprClientGrpc : DaprClient
|
|||
#region Cryptography
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
public override async Task<ReadOnlyMemory<byte>> EncryptAsync(string vaultResourceName,
|
||||
ReadOnlyMemory<byte> plaintextBytes, string keyName, EncryptionOptions encryptionOptions,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var memoryStream = plaintextBytes.CreateMemoryStream(true);
|
||||
|
||||
var encryptionResult =
|
||||
await EncryptAsync(vaultResourceName, memoryStream, keyName, encryptionOptions, cancellationToken);
|
||||
var encryptionResult = EncryptAsync(vaultResourceName, memoryStream, keyName, encryptionOptions, cancellationToken);
|
||||
|
||||
var bufferedResult = new ArrayBufferWriter<byte>();
|
||||
await foreach (var item in encryptionResult.WithCancellation(cancellationToken))
|
||||
await foreach (var item in encryptionResult)
|
||||
{
|
||||
bufferedResult.Write(item.Span);
|
||||
}
|
||||
|
@ -1687,16 +1687,18 @@ internal class DaprClientGrpc : DaprClient
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override async Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> EncryptAsync(string vaultResourceName,
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
public override async IAsyncEnumerable<ReadOnlyMemory<byte>> EncryptAsync(string vaultResourceName,
|
||||
Stream plaintextStream,
|
||||
string keyName, EncryptionOptions encryptionOptions, CancellationToken cancellationToken = default)
|
||||
string keyName, EncryptionOptions encryptionOptions,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(vaultResourceName, nameof(vaultResourceName));
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(keyName, nameof(keyName));
|
||||
ArgumentVerifier.ThrowIfNull(plaintextStream, nameof(plaintextStream));
|
||||
ArgumentVerifier.ThrowIfNull(encryptionOptions, nameof(encryptionOptions));
|
||||
|
||||
EventHandler<Exception> exceptionHandler = (_, ex) => throw ex;
|
||||
|
||||
var shouldOmitDecryptionKeyName =
|
||||
string.IsNullOrWhiteSpace(encryptionOptions
|
||||
|
@ -1719,185 +1721,88 @@ internal class DaprClientGrpc : DaprClient
|
|||
}
|
||||
|
||||
var options = CreateCallOptions(headers: null, cancellationToken);
|
||||
var duplexStream = client.EncryptAlpha1(options);
|
||||
var duplexStream = Client.EncryptAlpha1(options);
|
||||
|
||||
//Run both operations at the same time, but return the output of the streaming values coming from the operation
|
||||
var receiveResult = Task.FromResult(RetrieveEncryptedStreamAsync(duplexStream, cancellationToken));
|
||||
return await Task.WhenAll(
|
||||
//Stream the plaintext data to the sidecar in chunks
|
||||
SendPlaintextStreamAsync(plaintextStream, encryptionOptions.StreamingBlockSizeInBytes,
|
||||
duplexStream, encryptRequestOptions, cancellationToken),
|
||||
//At the same time, retrieve the encrypted response from the sidecar
|
||||
receiveResult).ContinueWith(_ => receiveResult.Result, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the plaintext bytes in chunks to the sidecar to be encrypted.
|
||||
/// </summary>
|
||||
private async Task SendPlaintextStreamAsync(Stream plaintextStream,
|
||||
int streamingBlockSizeInBytes,
|
||||
AsyncDuplexStreamingCall<Autogenerated.EncryptRequest, Autogenerated.EncryptResponse> duplexStream,
|
||||
Autogenerated.EncryptRequestOptions encryptRequestOptions,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
//Start with passing the metadata about the encryption request itself in the first message
|
||||
await duplexStream.RequestStream.WriteAsync(
|
||||
new Autogenerated.EncryptRequest { Options = encryptRequestOptions }, cancellationToken);
|
||||
|
||||
//Send the plaintext bytes in blocks in subsequent messages
|
||||
await using (var bufferedStream = new BufferedStream(plaintextStream, streamingBlockSizeInBytes))
|
||||
using var streamProcessor = new EncryptionStreamProcessor();
|
||||
try
|
||||
{
|
||||
var buffer = new byte[streamingBlockSizeInBytes];
|
||||
int bytesRead;
|
||||
ulong sequenceNumber = 0;
|
||||
streamProcessor.OnException += exceptionHandler;
|
||||
await streamProcessor.ProcessStreamAsync(plaintextStream, duplexStream, encryptRequestOptions,
|
||||
encryptionOptions.StreamingBlockSizeInBytes,
|
||||
cancellationToken);
|
||||
|
||||
while ((bytesRead =
|
||||
await bufferedStream.ReadAsync(buffer.AsMemory(0, streamingBlockSizeInBytes),
|
||||
cancellationToken)) !=
|
||||
0)
|
||||
await foreach (var value in streamProcessor.GetProcessedDataAsync(cancellationToken))
|
||||
{
|
||||
await duplexStream.RequestStream.WriteAsync(
|
||||
new Autogenerated.EncryptRequest
|
||||
{
|
||||
Payload = new Autogenerated.StreamPayload
|
||||
{
|
||||
Data = ByteString.CopyFrom(buffer, 0, bytesRead), Seq = sequenceNumber
|
||||
}
|
||||
}, cancellationToken);
|
||||
|
||||
//Increment the sequence number
|
||||
sequenceNumber++;
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
|
||||
//Send the completion message
|
||||
await duplexStream.RequestStream.CompleteAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the encrypted bytes from the encryption operation on the sidecar and returns as an enumerable stream.
|
||||
/// </summary>
|
||||
private async IAsyncEnumerable<ReadOnlyMemory<byte>> RetrieveEncryptedStreamAsync(
|
||||
AsyncDuplexStreamingCall<Autogenerated.EncryptRequest, Autogenerated.EncryptResponse> duplexStream,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
await foreach (var encryptResponse in duplexStream.ResponseStream.ReadAllAsync(cancellationToken)
|
||||
.ConfigureAwait(false))
|
||||
finally
|
||||
{
|
||||
yield return encryptResponse.Payload.Data.Memory;
|
||||
streamProcessor.OnException -= exceptionHandler;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override async Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> DecryptAsync(string vaultResourceName,
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
public override async IAsyncEnumerable<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName,
|
||||
Stream ciphertextStream, string keyName,
|
||||
DecryptionOptions decryptionOptions, CancellationToken cancellationToken = default)
|
||||
DecryptionOptions decryptionOptions,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(vaultResourceName, nameof(vaultResourceName));
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(keyName, nameof(keyName));
|
||||
ArgumentVerifier.ThrowIfNull(ciphertextStream, nameof(ciphertextStream));
|
||||
ArgumentVerifier.ThrowIfNull(decryptionOptions, nameof(decryptionOptions));
|
||||
decryptionOptions ??= new DecryptionOptions();
|
||||
|
||||
EventHandler<Exception> exceptionHandler = (_, ex) => throw ex;
|
||||
|
||||
var decryptRequestOptions = new Autogenerated.DecryptRequestOptions
|
||||
{
|
||||
ComponentName = vaultResourceName, KeyName = keyName
|
||||
ComponentName = vaultResourceName,
|
||||
KeyName = keyName
|
||||
};
|
||||
|
||||
var options = CreateCallOptions(headers: null, cancellationToken);
|
||||
var duplexStream = client.DecryptAlpha1(options);
|
||||
|
||||
//Run both operations at the same time, but return the output of the streaming values coming from the operation
|
||||
var receiveResult = Task.FromResult(RetrieveDecryptedStreamAsync(duplexStream, cancellationToken));
|
||||
return await Task.WhenAll(
|
||||
//Stream the ciphertext data to the sidecar in chunks
|
||||
SendCiphertextStreamAsync(ciphertextStream, decryptionOptions.StreamingBlockSizeInBytes,
|
||||
duplexStream, decryptRequestOptions, cancellationToken),
|
||||
//At the same time, retrieve the decrypted response from the sidecar
|
||||
receiveResult)
|
||||
//Return only the result of the `RetrieveEncryptedStreamAsync` method
|
||||
.ContinueWith(_ => receiveResult.Result, cancellationToken);
|
||||
using var streamProcessor = new DecryptionStreamProcessor();
|
||||
try
|
||||
{
|
||||
streamProcessor.OnException += exceptionHandler;
|
||||
await streamProcessor.ProcessStreamAsync(ciphertextStream, duplexStream, decryptionOptions.StreamingBlockSizeInBytes,
|
||||
decryptRequestOptions,
|
||||
cancellationToken);
|
||||
|
||||
await foreach (var value in streamProcessor.GetProcessedDataAsync(cancellationToken))
|
||||
{
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
streamProcessor.OnException -= exceptionHandler;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> DecryptAsync(string vaultResourceName,
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
public override IAsyncEnumerable<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName,
|
||||
Stream ciphertextStream, string keyName, CancellationToken cancellationToken = default) =>
|
||||
DecryptAsync(vaultResourceName, ciphertextStream, keyName, new DecryptionOptions(),
|
||||
cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Sends the ciphertext bytes in chunks to the sidecar to be decrypted.
|
||||
/// </summary>
|
||||
private async Task SendCiphertextStreamAsync(Stream ciphertextStream,
|
||||
int streamingBlockSizeInBytes,
|
||||
AsyncDuplexStreamingCall<Autogenerated.DecryptRequest, Autogenerated.DecryptResponse> duplexStream,
|
||||
Autogenerated.DecryptRequestOptions decryptRequestOptions,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
//Start with passing the metadata about the decryption request itself in the first message
|
||||
await duplexStream.RequestStream.WriteAsync(
|
||||
new Autogenerated.DecryptRequest { Options = decryptRequestOptions }, cancellationToken);
|
||||
|
||||
//Send the ciphertext bytes in blocks in subsequent messages
|
||||
await using (var bufferedStream = new BufferedStream(ciphertextStream, streamingBlockSizeInBytes))
|
||||
{
|
||||
var buffer = new byte[streamingBlockSizeInBytes];
|
||||
int bytesRead;
|
||||
ulong sequenceNumber = 0;
|
||||
|
||||
while ((bytesRead =
|
||||
await bufferedStream.ReadAsync(buffer.AsMemory(0, streamingBlockSizeInBytes),
|
||||
cancellationToken)) != 0)
|
||||
{
|
||||
await duplexStream.RequestStream.WriteAsync(
|
||||
new Autogenerated.DecryptRequest
|
||||
{
|
||||
Payload = new Autogenerated.StreamPayload
|
||||
{
|
||||
Data = ByteString.CopyFrom(buffer, 0, bytesRead), Seq = sequenceNumber
|
||||
}
|
||||
}, cancellationToken);
|
||||
|
||||
//Increment the sequence number
|
||||
sequenceNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
//Send the completion message
|
||||
await duplexStream.RequestStream.CompleteAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the decrypted bytes from the decryption operation on the sidecar and returns as an enumerable stream.
|
||||
/// </summary>
|
||||
private async IAsyncEnumerable<ReadOnlyMemory<byte>> RetrieveDecryptedStreamAsync(
|
||||
AsyncDuplexStreamingCall<Autogenerated.DecryptRequest, Autogenerated.DecryptResponse> duplexStream,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
await foreach (var decryptResponse in duplexStream.ResponseStream.ReadAllAsync(cancellationToken)
|
||||
.ConfigureAwait(false))
|
||||
{
|
||||
yield return decryptResponse.Payload.Data.Memory;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
public override async Task<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName,
|
||||
ReadOnlyMemory<byte> ciphertextBytes, string keyName, DecryptionOptions decryptionOptions,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var memoryStream = ciphertextBytes.CreateMemoryStream(true);
|
||||
|
||||
var decryptionResult =
|
||||
await DecryptAsync(vaultResourceName, memoryStream, keyName, decryptionOptions, cancellationToken);
|
||||
var decryptionResult = DecryptAsync(vaultResourceName, memoryStream, keyName, decryptionOptions, cancellationToken);
|
||||
|
||||
var bufferedResult = new ArrayBufferWriter<byte>();
|
||||
await foreach (var item in decryptionResult.WithCancellation(cancellationToken))
|
||||
await foreach (var item in decryptionResult)
|
||||
{
|
||||
bufferedResult.Write(item.Span);
|
||||
}
|
||||
|
@ -1906,8 +1811,7 @@ internal class DaprClientGrpc : DaprClient
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
public override async Task<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName,
|
||||
ReadOnlyMemory<byte> ciphertextBytes, string keyName, CancellationToken cancellationToken = default) =>
|
||||
await DecryptAsync(vaultResourceName, ciphertextBytes, keyName,
|
||||
|
@ -1916,7 +1820,7 @@ internal class DaprClientGrpc : DaprClient
|
|||
#region Subtle Crypto Implementation
|
||||
|
||||
///// <inheritdoc/>
|
||||
//[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public override async Task<(string Name, string PublicKey)> GetKeyAsync(string vaultResourceName, string keyName, Autogenerated.SubtleGetKeyRequest.Types.KeyFormat keyFormat,
|
||||
// CancellationToken cancellationToken = default)
|
||||
//{
|
||||
|
@ -1945,7 +1849,7 @@ internal class DaprClientGrpc : DaprClient
|
|||
//}
|
||||
|
||||
///// <inheritdoc/>
|
||||
//[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public override async Task<(byte[] CipherTextBytes, byte[] AuthenticationTag)> EncryptAsync(string vaultResourceName, byte[] plainTextBytes, string algorithm,
|
||||
// string keyName, byte[] nonce, byte[] associatedData, CancellationToken cancellationToken = default)
|
||||
//{
|
||||
|
@ -1981,7 +1885,7 @@ internal class DaprClientGrpc : DaprClient
|
|||
//}
|
||||
|
||||
///// <inheritdoc/>
|
||||
//[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public override async Task<byte[]> DecryptAsync(string vaultResourceName, byte[] cipherTextBytes, string algorithm, string keyName, byte[] nonce, byte[] tag,
|
||||
// byte[] associatedData, CancellationToken cancellationToken = default)
|
||||
//{
|
||||
|
@ -2017,7 +1921,7 @@ internal class DaprClientGrpc : DaprClient
|
|||
//}
|
||||
|
||||
///// <inheritdoc/>
|
||||
//[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public override async Task<(byte[] WrappedKey, byte[] AuthenticationTag)> WrapKeyAsync(string vaultResourceName, byte[] plainTextKey, string keyName,
|
||||
// string algorithm, byte[] nonce, byte[] associatedData, CancellationToken cancellationToken = default)
|
||||
//{
|
||||
|
@ -2053,7 +1957,7 @@ internal class DaprClientGrpc : DaprClient
|
|||
//}
|
||||
|
||||
///// <inheritdoc/>
|
||||
//[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public override async Task<byte[]> UnwrapKeyAsync(string vaultResourceName, byte[] wrappedKey, string algorithm,
|
||||
// string keyName, byte[] nonce, byte[] tag, byte[] associatedData, CancellationToken cancellationToken = default)
|
||||
//{
|
||||
|
@ -2090,7 +1994,7 @@ internal class DaprClientGrpc : DaprClient
|
|||
//}
|
||||
|
||||
///// <inheritdoc/>
|
||||
//[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public override async Task<byte[]> SignAsync(string vaultResourceName, byte[] digest, string algorithm, string keyName, CancellationToken cancellationToken = default)
|
||||
//{
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(vaultResourceName, nameof(vaultResourceName));
|
||||
|
@ -2123,7 +2027,7 @@ internal class DaprClientGrpc : DaprClient
|
|||
//}
|
||||
|
||||
///// <inheritdoc/>
|
||||
//[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
//public override async Task<bool> VerifyAsync(string vaultResourceName, byte[] digest, byte[] signature,
|
||||
// string algorithm, string keyName, CancellationToken cancellationToken = default)
|
||||
//{
|
||||
|
@ -2165,7 +2069,7 @@ internal class DaprClientGrpc : DaprClient
|
|||
#region Distributed Lock API
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Obsolete]
|
||||
[Experimental("DAPR_DISTRIBUTEDLOCK", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/distributed-lock/distributed-lock-api-overview/")]
|
||||
public async override Task<TryLockResponse> Lock(
|
||||
string storeName,
|
||||
string resourceId,
|
||||
|
@ -2205,7 +2109,7 @@ internal class DaprClientGrpc : DaprClient
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Obsolete]
|
||||
[Experimental("DAPR_DISTRIBUTEDLOCK", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/distributed-lock/distributed-lock-api-overview/")]
|
||||
public async override Task<UnlockResponse> Unlock(
|
||||
string storeName,
|
||||
string resourceId,
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// ------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dapr.Client;
|
||||
|
@ -19,7 +20,7 @@ namespace Dapr.Client;
|
|||
/// <summary>
|
||||
/// Class representing the response from a Lock API call.
|
||||
/// </summary>
|
||||
[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
[Experimental("DAPR_DISTRIBUTEDLOCK", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/distributed-lock/distributed-lock-api-overview/")]
|
||||
public sealed class TryLockResponse : IAsyncDisposable
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Dapr.Cryptography.Encryption.Models;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1.Dapr;
|
||||
|
||||
|
@ -31,6 +32,7 @@ namespace Dapr.Cryptography.Encryption;
|
|||
/// exhaustion and other problems.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
public abstract class DaprEncryptionClient(Autogenerated.DaprClient client, HttpClient httpClient, string? daprApiToken = null) : IDaprEncryptionClient
|
||||
{
|
||||
private bool disposed;
|
||||
|
@ -68,7 +70,6 @@ public abstract class DaprEncryptionClient(Autogenerated.DaprClient client, Http
|
|||
/// <param name="encryptionOptions">Options informing how the encryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of encrypted bytes.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract Task<ReadOnlyMemory<byte>> EncryptAsync(string vaultResourceName,
|
||||
ReadOnlyMemory<byte> plaintextBytes, string keyName, EncryptionOptions encryptionOptions,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
@ -82,7 +83,6 @@ public abstract class DaprEncryptionClient(Autogenerated.DaprClient client, Http
|
|||
/// <param name="encryptionOptions">Options informing how the encryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of encrypted bytes.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract IAsyncEnumerable<ReadOnlyMemory<byte>> EncryptAsync(string vaultResourceName, Stream plaintextStream, string keyName,
|
||||
EncryptionOptions encryptionOptions, CancellationToken cancellationToken = default);
|
||||
|
||||
|
@ -95,7 +95,6 @@ public abstract class DaprEncryptionClient(Autogenerated.DaprClient client, Http
|
|||
/// <param name="options">Options informing how the decryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of decrypted bytes.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract Task<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName, ReadOnlyMemory<byte> ciphertextBytes, string keyName, DecryptionOptions? options = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
|
@ -108,8 +107,6 @@ public abstract class DaprEncryptionClient(Autogenerated.DaprClient client, Http
|
|||
/// <param name="options">Options informing how the decryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An asynchronously enumerable array of decrypted bytes.</returns>
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract IAsyncEnumerable<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName, Stream ciphertextStream,
|
||||
string keyName, DecryptionOptions? options = null, CancellationToken cancellationToken = default);
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Dapr.Common;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
|
||||
|
@ -21,6 +22,7 @@ namespace Dapr.Cryptography.Encryption;
|
|||
/// Builds a <see cref="DaprEncryptionClient"/>.
|
||||
/// </summary>
|
||||
/// <param name="configuration">An optional instance of <see cref="IConfiguration"/>.</param>
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
public sealed class DaprEncryptionClientBuilder(IConfiguration? configuration = null) : DaprGenericClientBuilder<DaprEncryptionClient>(configuration)
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Buffers;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Dapr.Common;
|
||||
using Dapr.Cryptography.Extensions;
|
||||
|
@ -24,6 +25,7 @@ namespace Dapr.Cryptography.Encryption;
|
|||
/// <summary>
|
||||
/// A client for performing cryptography operations with Dapr.
|
||||
/// </summary>
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
internal sealed class DaprEncryptionGrpcClient(Autogenerated.Dapr.DaprClient client, HttpClient httpClient, string? daprApiToken = null) : DaprEncryptionClient(client, httpClient, daprApiToken: daprApiToken)
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -35,7 +37,6 @@ internal sealed class DaprEncryptionGrpcClient(Autogenerated.Dapr.DaprClient cli
|
|||
/// <param name="encryptionOptions">Options informing how the encryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of encrypted bytes.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override async Task<ReadOnlyMemory<byte>> EncryptAsync(
|
||||
string vaultResourceName,
|
||||
ReadOnlyMemory<byte> plaintextBytes,
|
||||
|
@ -64,8 +65,6 @@ internal sealed class DaprEncryptionGrpcClient(Autogenerated.Dapr.DaprClient cli
|
|||
/// <param name="encryptionOptions">Options informing how the encryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of encrypted bytes.</returns>
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override async IAsyncEnumerable<ReadOnlyMemory<byte>> EncryptAsync(
|
||||
string vaultResourceName,
|
||||
Stream plaintextStream,
|
||||
|
@ -132,7 +131,6 @@ internal sealed class DaprEncryptionGrpcClient(Autogenerated.Dapr.DaprClient cli
|
|||
/// <param name="options">Options informing how the decryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of decrypted bytes.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override async Task<ReadOnlyMemory<byte>> DecryptAsync(
|
||||
string vaultResourceName,
|
||||
ReadOnlyMemory<byte> ciphertextBytes,
|
||||
|
@ -161,8 +159,6 @@ internal sealed class DaprEncryptionGrpcClient(Autogenerated.Dapr.DaprClient cli
|
|||
/// <param name="options">Options informing how the decryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An asynchronously enumerable array of decrypted bytes.</returns>
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override async IAsyncEnumerable<ReadOnlyMemory<byte>> DecryptAsync(
|
||||
string vaultResourceName,
|
||||
Stream ciphertextStream,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Dapr.Common.Extensions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
|
@ -19,6 +20,7 @@ namespace Dapr.Cryptography.Encryption.Extensions;
|
|||
/// <summary>
|
||||
/// Contains extension methods for using Dapr cryptography with dependency injection.
|
||||
/// </summary>
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
public static class DaprCryptographyServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Dapr.Common;
|
||||
using Dapr.Cryptography.Encryption.Models;
|
||||
|
||||
|
@ -19,6 +20,7 @@ namespace Dapr.Cryptography.Encryption;
|
|||
/// <summary>
|
||||
/// Provides the implementation shape for the Dapr encryption client.
|
||||
/// </summary>
|
||||
[Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")]
|
||||
public interface IDaprEncryptionClient : IDaprClient
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -30,7 +32,6 @@ public interface IDaprEncryptionClient : IDaprClient
|
|||
/// <param name="encryptionOptions">Options informing how the encryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of encrypted bytes.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public Task<ReadOnlyMemory<byte>> EncryptAsync(string vaultResourceName,
|
||||
ReadOnlyMemory<byte> plaintextBytes, string keyName, EncryptionOptions encryptionOptions,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
@ -44,7 +45,6 @@ public interface IDaprEncryptionClient : IDaprClient
|
|||
/// <param name="encryptionOptions">Options informing how the encryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of encrypted bytes.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public IAsyncEnumerable<ReadOnlyMemory<byte>> EncryptAsync(string vaultResourceName, Stream plaintextStream, string keyName,
|
||||
EncryptionOptions encryptionOptions, CancellationToken cancellationToken = default);
|
||||
|
||||
|
@ -57,7 +57,6 @@ public interface IDaprEncryptionClient : IDaprClient
|
|||
/// <param name="options">Options informing how the decryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of decrypted bytes.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public Task<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName, ReadOnlyMemory<byte> ciphertextBytes, string keyName, DecryptionOptions? options = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
|
@ -70,8 +69,6 @@ public interface IDaprEncryptionClient : IDaprClient
|
|||
/// <param name="options">Options informing how the decryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An asynchronously enumerable array of decrypted bytes.</returns>
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public IAsyncEnumerable<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName, Stream ciphertextStream,
|
||||
string keyName, DecryptionOptions? options = null, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Dapr.Common;
|
||||
using Dapr.Jobs.Models;
|
||||
using Dapr.Jobs.Models.Responses;
|
||||
|
@ -33,6 +34,7 @@ namespace Dapr.Jobs;
|
|||
/// exhaustion and other problems.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Experimental("DAPR_JOBS", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/jobs/jobs-overview/")]
|
||||
public abstract class DaprJobsClient(Autogenerated.DaprClient client, HttpClient httpClient, string? daprApiToken = null) : IDaprClient
|
||||
{
|
||||
private bool disposed;
|
||||
|
@ -74,8 +76,6 @@ public abstract class DaprJobsClient(Autogenerated.DaprClient client, HttpClient
|
|||
/// to require that an existing job with the same name be deleted first.</param>
|
||||
/// <param name="failurePolicyOptions">The characteristics of the policy to apply when a job fails to trigger.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract Task ScheduleJobAsync(string jobName, DaprJobSchedule schedule,
|
||||
ReadOnlyMemory<byte>? payload = null, DateTimeOffset? startingFrom = null, int? repeats = null,
|
||||
DateTimeOffset? ttl = null, bool overwrite = false, IJobFailurePolicyOptions? failurePolicyOptions = null,
|
||||
|
@ -87,7 +87,6 @@ public abstract class DaprJobsClient(Autogenerated.DaprClient client, HttpClient
|
|||
/// <param name="jobName">The jobName of the job.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The details comprising the job.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract Task<DaprJobDetails> GetJobAsync(string jobName, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
|
@ -95,7 +94,6 @@ public abstract class DaprJobsClient(Autogenerated.DaprClient client, HttpClient
|
|||
/// </summary>
|
||||
/// <param name="jobName">The jobName of the job.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract Task DeleteJobAsync(string jobName, CancellationToken cancellationToken = default);
|
||||
|
||||
internal static KeyValuePair<string, string>? GetDaprApiTokenHeader(string apiToken)
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Dapr.Common;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
|
||||
|
@ -21,6 +22,7 @@ namespace Dapr.Jobs;
|
|||
/// Builds a <see cref="DaprJobsClient"/>.
|
||||
/// </summary>
|
||||
/// <param name="configuration">An optional instance of <see cref="IConfiguration"/>.</param>
|
||||
[Experimental("DAPR_JOBS", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/jobs/jobs-overview/")]
|
||||
public sealed class DaprJobsClientBuilder(IConfiguration? configuration = null) : DaprGenericClientBuilder<DaprJobsClient>(configuration)
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Dapr.Common;
|
||||
using Dapr.Jobs.Models;
|
||||
using Dapr.Jobs.Models.Responses;
|
||||
|
@ -24,6 +25,7 @@ namespace Dapr.Jobs;
|
|||
/// <summary>
|
||||
/// A client for interacting with the Dapr endpoints.
|
||||
/// </summary>
|
||||
[Experimental("DAPR_JOBS", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/jobs/jobs-overview/")]
|
||||
internal sealed class DaprJobsGrpcClient(Autogenerated.Dapr.DaprClient client, HttpClient httpClient, string? daprApiToken = null) : DaprJobsClient(client, httpClient, daprApiToken: daprApiToken)
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -38,7 +40,6 @@ internal sealed class DaprJobsGrpcClient(Autogenerated.Dapr.DaprClient client, H
|
|||
/// <param name="overwrite">A flag indicating whether the job should be overwritten when submitted (true); otherwise false to require that an existing job with the same name be deleted first.</param>
|
||||
/// <param name="failurePolicyOptions">The characteristics of the policy to apply when a job fails to trigger.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override async Task ScheduleJobAsync(string jobName, DaprJobSchedule schedule,
|
||||
ReadOnlyMemory<byte>? payload = null, DateTimeOffset? startingFrom = null, int? repeats = null,
|
||||
DateTimeOffset? ttl = null, bool overwrite = false, IJobFailurePolicyOptions? failurePolicyOptions = null,
|
||||
|
@ -157,7 +158,6 @@ internal sealed class DaprJobsGrpcClient(Autogenerated.Dapr.DaprClient client, H
|
|||
/// <param name="jobName">The name of the job.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The details comprising the job.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override async Task<DaprJobDetails> GetJobAsync(string jobName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(jobName))
|
||||
|
@ -170,17 +170,7 @@ internal sealed class DaprJobsGrpcClient(Autogenerated.Dapr.DaprClient client, H
|
|||
var envelope = new Autogenerated.GetJobRequest { Name = jobName };
|
||||
var grpcCallOptions = DaprClientUtilities.ConfigureGrpcCallOptions(typeof(DaprJobsClient).Assembly, this.DaprApiToken, cancellationToken);
|
||||
var response = await Client.GetJobAlpha1Async(envelope, grpcCallOptions);
|
||||
var schedule = DateTime.TryParse(response.Job.DueTime, out var dueTime)
|
||||
? DaprJobSchedule.FromDateTime(dueTime)
|
||||
: new DaprJobSchedule(response.Job.Schedule);
|
||||
|
||||
return new DaprJobDetails(schedule)
|
||||
{
|
||||
DueTime = !string.IsNullOrWhiteSpace(response.Job.DueTime) ? DateTime.Parse(response.Job.DueTime) : null,
|
||||
Ttl = !string.IsNullOrWhiteSpace(response.Job.Ttl) ? DateTime.Parse(response.Job.Ttl) : null,
|
||||
RepeatCount = (int?)response.Job.Repeats,
|
||||
Payload = response.Job.Data.ToByteArray()
|
||||
};
|
||||
return DeserializeJobResponse(response);
|
||||
}
|
||||
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
|
@ -199,13 +189,35 @@ internal sealed class DaprJobsGrpcClient(Autogenerated.Dapr.DaprClient client, H
|
|||
throw new DaprException("Get job operation failed: the Dapr endpoint did not return the expected value.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Testable method for performing job response deserialization.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is exposed strictly for testing purposes.
|
||||
/// </remarks>
|
||||
/// <param name="response">The job response to deserialize.</param>
|
||||
/// <returns>The deserialized job response.</returns>
|
||||
internal static DaprJobDetails DeserializeJobResponse(Autogenerated.GetJobResponse response)
|
||||
{
|
||||
var schedule = DateTime.TryParse(response.Job.DueTime, out var dueTime)
|
||||
? DaprJobSchedule.FromDateTime(dueTime)
|
||||
: new DaprJobSchedule(response.Job.Schedule);
|
||||
|
||||
return new DaprJobDetails(schedule)
|
||||
{
|
||||
DueTime = !string.IsNullOrWhiteSpace(response.Job.DueTime) ? DateTime.Parse(response.Job.DueTime) : null,
|
||||
Ttl = !string.IsNullOrWhiteSpace(response.Job.Ttl) ? DateTime.Parse(response.Job.Ttl) : null,
|
||||
RepeatCount = (int?)response.Job.Repeats ?? 0,
|
||||
Payload = response.Job.Data?.ToByteArray() ?? null
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified job.
|
||||
/// </summary>
|
||||
/// <param name="jobName">The name of the job.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override async Task DeleteJobAsync(string jobName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(jobName))
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Dapr.Common.Extensions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
|
@ -19,6 +20,7 @@ namespace Dapr.Jobs.Extensions;
|
|||
/// <summary>
|
||||
/// Contains extension methods for using Dapr Jobs with dependency injection.
|
||||
/// </summary>
|
||||
[Experimental("DAPR_JOBS", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/jobs/jobs-overview/")]
|
||||
public static class DaprJobsServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Dapr.Jobs.Models;
|
||||
|
@ -20,6 +21,7 @@ namespace Dapr.Jobs.Extensions;
|
|||
/// <summary>
|
||||
/// Provides helper extensions for performing serialization operations when scheduling one-time Cron jobs for the developer.
|
||||
/// </summary>
|
||||
[Experimental("DAPR_JOBS", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/jobs/jobs-overview/")]
|
||||
public static class DaprJobsSerializationExtensions
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -41,7 +43,6 @@ public static class DaprJobsSerializationExtensions
|
|||
/// <param name="overwrite">A flag indicating whether the job should be overwritten when submitted (true); otherwise false to require that an existing job with the same name be deleted first.</param>
|
||||
/// <param name="failurePolicyOptions">The characteristics of the policy to apply when a job fails to trigger.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public static async Task ScheduleJobWithPayloadAsync(this DaprJobsClient client, string jobName, DaprJobSchedule schedule,
|
||||
object payload, DateTime? startingFrom = null, int? repeats = null,
|
||||
JsonSerializerOptions? jsonSerializerOptions = null, DateTimeOffset? ttl = null,
|
||||
|
@ -71,7 +72,6 @@ public static class DaprJobsSerializationExtensions
|
|||
/// <param name="overwrite">A flag indicating whether the job should be overwritten when submitted (true); otherwise false to require that an existing job with the same name be deleted first.</param>
|
||||
/// <param name="failurePolicyOptions">The characteristics of the policy to apply when a job fails to trigger.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public static async Task ScheduleJobWithPayloadAsync(this DaprJobsClient client, string jobName, DaprJobSchedule schedule,
|
||||
string payload, DateTime? startingFrom = null, int? repeats = null, DateTimeOffset? ttl = null,
|
||||
bool overwrite = false, IJobFailurePolicyOptions? failurePolicyOptions = null,
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
// ------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Actors.Client;
|
||||
|
@ -175,6 +177,160 @@ public sealed class ActorManagerTests
|
|||
Assert.Equal(1, activator.DeleteCallCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Period_Iso8601_Time()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"0h0m7s10ms\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.FromSeconds(7).Add(TimeSpan.FromMilliseconds(10)), result.Period);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Period_DaprFormat_Every()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@every 15s\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.FromSeconds(15), result.Period);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Period_DaprFormat_Every2()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@every 3h2m15s\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.FromHours(3).Add(TimeSpan.FromMinutes(2)).Add(TimeSpan.FromSeconds(15)), result.Period);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Period_DaprFormat_Monthly()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@monthly\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.FromDays(30), result.Period);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Period_DaprFormat_Weekly()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@weekly\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.FromDays(7), result.Period);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Period_DaprFormat_Daily()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@daily\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.FromDays(1), result.Period);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Period_DaprFormat_Hourly()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@hourly\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.FromHours(1), result.Period);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_DueTime_DaprFormat_Hourly()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"dueTime\": \"@hourly\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.FromHours(1), result.DueTime);
|
||||
Assert.Equal(TimeSpan.Zero, result.Period);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_DueTime_Iso8601Times()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"dueTime\": \"0h0m7s10ms\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.Zero, result.Period);
|
||||
Assert.Equal(TimeSpan.FromSeconds(7).Add(TimeSpan.FromMilliseconds(10)), result.DueTime);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Ttl_DaprFormat_Hourly()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"ttl\": \"@hourly\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.Zero, result.Period);
|
||||
Assert.Equal(TimeSpan.FromHours(1), result.Ttl);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Ttl_Iso8601Times()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"ttl\": \"0h0m7s10ms\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.Zero, result.Period);
|
||||
Assert.Equal(TimeSpan.FromSeconds(7).Add(TimeSpan.FromMilliseconds(10)), result.Ttl);
|
||||
}
|
||||
|
||||
private interface ITestActor : IActor { }
|
||||
|
||||
private class TestActor : Actor, ITestActor, IDisposable
|
||||
|
@ -255,4 +411,4 @@ public sealed class ActorManagerTests
|
|||
return base.DeleteAsync(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,28 +30,6 @@ public class CryptographyApiTest
|
|||
(ReadOnlyMemory<byte>) Array.Empty<byte>(), keyName, new EncryptionOptions(KeyWrapAlgorithm.Rsa), CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EncryptAsync_Stream_VaultResourceName_ArgumentVerifierException()
|
||||
{
|
||||
var client = new DaprClientBuilder().Build();
|
||||
const string vaultResourceName = "";
|
||||
//Get response and validate
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.EncryptAsync(vaultResourceName,
|
||||
new MemoryStream(), "MyKey", new EncryptionOptions(KeyWrapAlgorithm.Rsa),
|
||||
CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EncryptAsync_Stream_KeyName_ArgumentVerifierException()
|
||||
{
|
||||
var client = new DaprClientBuilder().Build();
|
||||
const string keyName = "";
|
||||
//Get response and validate
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.EncryptAsync("myVault",
|
||||
(Stream) new MemoryStream(), keyName, new EncryptionOptions(KeyWrapAlgorithm.Rsa),
|
||||
CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DecryptAsync_ByteArray_VaultResourceName_ArgumentVerifierException()
|
||||
{
|
||||
|
@ -71,24 +49,4 @@ public class CryptographyApiTest
|
|||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.DecryptAsync("myVault",
|
||||
Array.Empty<byte>(), keyName, new DecryptionOptions(), CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DecryptAsync_Stream_VaultResourceName_ArgumentVerifierException()
|
||||
{
|
||||
var client = new DaprClientBuilder().Build();
|
||||
const string vaultResourceName = "";
|
||||
//Get response and validate
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.DecryptAsync(vaultResourceName,
|
||||
new MemoryStream(), "MyKey", new DecryptionOptions(), CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DecryptAsync_Stream_KeyName_ArgumentVerifierException()
|
||||
{
|
||||
var client = new DaprClientBuilder().Build();
|
||||
const string keyName = "";
|
||||
//Get response and validate
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.DecryptAsync("myVault",
|
||||
new MemoryStream(), keyName, new DecryptionOptions(), CancellationToken.None));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ public class DaprJobsAnalyzerAnalyzerTests
|
|||
using Dapr.Jobs.Extensions;
|
||||
using Dapr.Jobs.Models;
|
||||
|
||||
#pragma warning disable DAPR_JOBS
|
||||
public static class Program
|
||||
{
|
||||
public static void Main()
|
||||
|
@ -41,10 +42,11 @@ public class DaprJobsAnalyzerAnalyzerTests
|
|||
Encoding.UTF8.GetBytes("This is a test"), repeats: 10).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
#pragma warning restore DAPR_JOBS
|
||||
""";
|
||||
|
||||
var expected = VerifyAnalyzer.Diagnostic(MapDaprScheduledJobHandlerAnalyzer.DaprJobHandlerRule)
|
||||
.WithSpan(22, 25, 23, 83)
|
||||
.WithSpan(23, 25, 24, 83)
|
||||
.WithMessage(
|
||||
"Job invocations require the MapDaprScheduledJobHandler be set and configured for job name 'myJob' on IEndpointRouteBuilder");
|
||||
|
||||
|
@ -66,6 +68,7 @@ public class DaprJobsAnalyzerAnalyzerTests
|
|||
using Dapr.Jobs.Extensions;
|
||||
using Dapr.Jobs.Models;
|
||||
|
||||
#pragma warning disable DAPR_JOBS
|
||||
public static class Program
|
||||
{
|
||||
public static void Main()
|
||||
|
@ -78,6 +81,7 @@ public class DaprJobsAnalyzerAnalyzerTests
|
|||
var daprJobsClient = scope.ServiceProvider.GetRequiredService<DaprJobsClient>();
|
||||
}
|
||||
}
|
||||
#pragma warning restore DAPR_JOBS
|
||||
""";
|
||||
|
||||
var analyzer = new VerifyAnalyzer(Utilities.GetReferences());
|
||||
|
@ -98,6 +102,7 @@ public class DaprJobsAnalyzerAnalyzerTests
|
|||
using Dapr.Jobs.Extensions;
|
||||
using Dapr.Jobs.Models;
|
||||
|
||||
#pragma warning disable DAPR_JOBS
|
||||
public static class Program
|
||||
{
|
||||
public static void Main()
|
||||
|
@ -115,14 +120,15 @@ public class DaprJobsAnalyzerAnalyzerTests
|
|||
Encoding.UTF8.GetBytes("This is a test"), repeats: 10).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
#pragma warning restore DAPR_JOBS
|
||||
""";
|
||||
|
||||
var expected1 = VerifyAnalyzer.Diagnostic(MapDaprScheduledJobHandlerAnalyzer.DaprJobHandlerRule)
|
||||
.WithSpan(22, 25, 23, 83)
|
||||
.WithSpan(23, 25, 24, 83)
|
||||
.WithMessage(
|
||||
"Job invocations require the MapDaprScheduledJobHandler be set and configured for job name 'myJob' on IEndpointRouteBuilder");
|
||||
var expected2 = VerifyAnalyzer.Diagnostic(MapDaprScheduledJobHandlerAnalyzer.DaprJobHandlerRule)
|
||||
.WithSpan(24, 25, 25, 83)
|
||||
.WithSpan(25, 25, 26, 83)
|
||||
.WithMessage("Job invocations require the MapDaprScheduledJobHandler be set and configured for job name 'myJob2' on IEndpointRouteBuilder");
|
||||
var analyzer = new VerifyAnalyzer(Utilities.GetReferences());
|
||||
await analyzer.VerifyAnalyzerAsync<MapDaprScheduledJobHandlerAnalyzer>(testCode, expected1, expected2);
|
||||
|
@ -142,6 +148,7 @@ public class DaprJobsAnalyzerAnalyzerTests
|
|||
using Dapr.Jobs.Extensions;
|
||||
using Dapr.Jobs.Models;
|
||||
|
||||
#pragma warning disable DAPR_JOBS
|
||||
public static class Program
|
||||
{
|
||||
public static void Main()
|
||||
|
@ -164,6 +171,7 @@ public class DaprJobsAnalyzerAnalyzerTests
|
|||
}, TimeSpan.FromSeconds(5));
|
||||
}
|
||||
}
|
||||
#pragma warning restore DAPR_JOBS
|
||||
""";
|
||||
|
||||
var analyzer = new VerifyAnalyzer(Utilities.GetReferences());
|
||||
|
@ -183,7 +191,8 @@ public class DaprJobsAnalyzerAnalyzerTests
|
|||
using Dapr.Jobs;
|
||||
using Dapr.Jobs.Extensions;
|
||||
using Dapr.Jobs.Models;
|
||||
|
||||
|
||||
#pragma warning disable DAPR_JOBS
|
||||
public static class Program
|
||||
{
|
||||
public static async Task Main()
|
||||
|
@ -206,6 +215,7 @@ public class DaprJobsAnalyzerAnalyzerTests
|
|||
}, TimeSpan.FromSeconds(5));
|
||||
}
|
||||
}
|
||||
#pragma warning restore DAPR_JOBS
|
||||
""";
|
||||
|
||||
var analyzer = new VerifyAnalyzer(Utilities.GetReferences());
|
||||
|
@ -225,7 +235,8 @@ public class DaprJobsAnalyzerAnalyzerTests
|
|||
using Dapr.Jobs;
|
||||
using Dapr.Jobs.Extensions;
|
||||
using Dapr.Jobs.Models;
|
||||
|
||||
|
||||
#pragma warning disable DAPR_JOBS
|
||||
public static class Program
|
||||
{
|
||||
public static async Task Main()
|
||||
|
@ -245,6 +256,7 @@ public class DaprJobsAnalyzerAnalyzerTests
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
#pragma warning restore DAPR_JOBS
|
||||
|
||||
""";
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using Dapr.Client.Autogen.Grpc.v1;
|
||||
using Dapr.Jobs.Models;
|
||||
using Moq;
|
||||
|
||||
|
@ -166,6 +167,21 @@ public sealed class DaprJobsGrpcClientTests
|
|||
});
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldDeserialize_EveryExpression()
|
||||
{
|
||||
const string scheduleText = "@every 1m";
|
||||
var response = new GetJobResponse { Job = new Job { Name = "test", Schedule = scheduleText } };
|
||||
var schedule = DaprJobSchedule.FromExpression(scheduleText);
|
||||
|
||||
var jobDetails = DaprJobsGrpcClient.DeserializeJobResponse(response);
|
||||
Assert.Null(jobDetails.Payload);
|
||||
Assert.Equal(0, jobDetails.RepeatCount);
|
||||
Assert.Null(jobDetails.Ttl);
|
||||
Assert.Null(jobDetails.DueTime);
|
||||
Assert.Equal(jobDetails.Schedule.ExpressionValue, schedule.ExpressionValue);
|
||||
}
|
||||
|
||||
private sealed record TestPayload(string Name, string Color);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<OutputPath>$(RepoRoot)bin\$(Configuration)\test\$(MSBuildProjectName)\</OutputPath>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<NoWarn>$(NoWarn);DAPR_CONVERSATION;DAPR_JOBS;DAPR_DISTRIBUTEDLOCK;DAPR_CRYPTOGRAPHY</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Used to annotate PR with test failures: https://github.com/Tyrrrz/GitHubActionsTestLogger -->
|
||||
|
|
Loading…
Reference in New Issue