diff --git a/Dapr.PluggableComponents.Complete.sln b/Dapr.PluggableComponents.Complete.sln
index b06cf97..e793979 100644
--- a/Dapr.PluggableComponents.Complete.sln
+++ b/Dapr.PluggableComponents.Complete.sln
@@ -23,6 +23,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscordBindingSample", "sam
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.PluggableComponents.Tests", "src\Dapr.PluggableComponents.Tests\Dapr.PluggableComponents.Tests.csproj", "{C9027B0B-A589-4E92-AB32-34B8961C479B}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LocalEnvSecretStoreSample", "samples\LocalEnvSecretStoreSample\LocalEnvSecretStoreSample.csproj", "{FB79E8DE-3AA9-47E0-9EA6-627DCAFC6068}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -60,6 +62,10 @@ Global
{C9027B0B-A589-4E92-AB32-34B8961C479B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9027B0B-A589-4E92-AB32-34B8961C479B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9027B0B-A589-4E92-AB32-34B8961C479B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FB79E8DE-3AA9-47E0-9EA6-627DCAFC6068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FB79E8DE-3AA9-47E0-9EA6-627DCAFC6068}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FB79E8DE-3AA9-47E0-9EA6-627DCAFC6068}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FB79E8DE-3AA9-47E0-9EA6-627DCAFC6068}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{E54270BE-EF83-47BE-B29C-29BC89701099} = {8E8203A7-A0B9-4F48-9CD4-DE9A0D3B73FB}
@@ -69,5 +75,6 @@ Global
{A6565BA8-E05C-4B4E-A908-C4B86232F9AB} = {6F4E950F-E4CD-4FA4-BA3D-528F0022C03B}
{BCE0E3E4-60D9-4947-9E1A-300640BE5FBF} = {6F4E950F-E4CD-4FA4-BA3D-528F0022C03B}
{C9027B0B-A589-4E92-AB32-34B8961C479B} = {8E8203A7-A0B9-4F48-9CD4-DE9A0D3B73FB}
+ {FB79E8DE-3AA9-47E0-9EA6-627DCAFC6068} = {6F4E950F-E4CD-4FA4-BA3D-528F0022C03B}
EndGlobalSection
EndGlobal
diff --git a/samples/LocalEnvSecretStoreSample/LocalEnvSecretStoreSample.csproj b/samples/LocalEnvSecretStoreSample/LocalEnvSecretStoreSample.csproj
new file mode 100644
index 0000000..928a1c5
--- /dev/null
+++ b/samples/LocalEnvSecretStoreSample/LocalEnvSecretStoreSample.csproj
@@ -0,0 +1,20 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/LocalEnvSecretStoreSample/Program.cs b/samples/LocalEnvSecretStoreSample/Program.cs
new file mode 100644
index 0000000..931645d
--- /dev/null
+++ b/samples/LocalEnvSecretStoreSample/Program.cs
@@ -0,0 +1,18 @@
+using Dapr.PluggableComponents;
+using LocalEnvSecretStoreSample.Services;
+
+var app = DaprPluggableComponentsApplication.Create();
+
+app.RegisterService(
+ "local.env-pluggable",
+ serviceBuilder =>
+ {
+ serviceBuilder.RegisterSecretStore(
+ context =>
+ {
+ Console.WriteLine("Creating secret store for instance '{0}' on socket '{1}'...", context.InstanceId, context.SocketPath);
+ return new LocalEnvSecretStore(context.ServiceProvider.GetRequiredService>());
+ });
+ });
+
+app.Run();
diff --git a/samples/LocalEnvSecretStoreSample/Services/LocalEnvSecretStore.cs b/samples/LocalEnvSecretStoreSample/Services/LocalEnvSecretStore.cs
new file mode 100644
index 0000000..eba5788
--- /dev/null
+++ b/samples/LocalEnvSecretStoreSample/Services/LocalEnvSecretStore.cs
@@ -0,0 +1,74 @@
+using System.Collections;
+using Dapr.PluggableComponents.Components;
+using Dapr.PluggableComponents.Components.SecretStore;
+
+namespace LocalEnvSecretStoreSample.Services;
+
+internal sealed class LocalEnvSecretStore : ISecretStore
+{
+ private readonly ILogger logger;
+
+ public LocalEnvSecretStore(ILogger logger)
+ {
+ this.logger = logger;
+ }
+
+ #region ISecretStore Members
+
+ public Task GetAsync(SecretStoreGetRequest request, CancellationToken cancellationToken = default)
+ {
+ this.logger.LogInformation("Get request for secret {key}", request.Key);
+
+ return Task.FromResult(
+ new SecretStoreGetResponse
+ {
+ Secrets = new Dictionary
+ {
+ { request.Key, Environment.GetEnvironmentVariable(request.Key) ?? String.Empty }
+ }
+ });
+ }
+
+ public Task BulkGetAsync(SecretStoreBulkGetRequest request, CancellationToken cancellationToken = default)
+ {
+ this.logger.LogInformation("Get request for all secrets");
+
+ return Task.FromResult(
+ new SecretStoreBulkGetResponse
+ {
+ Keys =
+ Environment
+ .GetEnvironmentVariables()
+ .ToDictionary()
+ .ToDictionary(
+ kvp => kvp.Key,
+ kvp => new SecretStoreGetResponse
+ {
+ Secrets = new Dictionary
+ {
+ { kvp.Key, kvp.Value ?? String.Empty }
+ }
+ })
+ });
+ }
+
+ public Task InitAsync(MetadataRequest request, CancellationToken cancellationToken = default)
+ {
+ return Task.CompletedTask;
+ }
+
+ #endregion
+}
+
+internal static class DictionaryExtensions
+{
+ public static IEnumerable> ToDictionary(this IDictionary dictionary)
+ {
+ var enumerator = dictionary.GetEnumerator();
+
+ while (enumerator.MoveNext())
+ {
+ yield return new KeyValuePair((TKey)enumerator.Key, (TValue?)enumerator.Value);
+ }
+ }
+}
diff --git a/samples/LocalEnvSecretStoreSample/appsettings.Development.json b/samples/LocalEnvSecretStoreSample/appsettings.Development.json
new file mode 100644
index 0000000..ff66ba6
--- /dev/null
+++ b/samples/LocalEnvSecretStoreSample/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/samples/LocalEnvSecretStoreSample/appsettings.json b/samples/LocalEnvSecretStoreSample/appsettings.json
new file mode 100644
index 0000000..4d56694
--- /dev/null
+++ b/samples/LocalEnvSecretStoreSample/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/src/Dapr.PluggableComponents.AspNetCore/DaprPluggableComponentsServiceBuilder.cs b/src/Dapr.PluggableComponents.AspNetCore/DaprPluggableComponentsServiceBuilder.cs
index 0d2bdbb..7e524e2 100644
--- a/src/Dapr.PluggableComponents.AspNetCore/DaprPluggableComponentsServiceBuilder.cs
+++ b/src/Dapr.PluggableComponents.AspNetCore/DaprPluggableComponentsServiceBuilder.cs
@@ -14,6 +14,7 @@
using Dapr.PluggableComponents.Adaptors;
using Dapr.PluggableComponents.Components.Bindings;
using Dapr.PluggableComponents.Components.PubSub;
+using Dapr.PluggableComponents.Components.SecretStore;
using Dapr.PluggableComponents.Components.StateStore;
namespace Dapr.PluggableComponents;
@@ -181,6 +182,45 @@ public DaprPluggableComponentsServiceBuilder RegisterStateStore(Com
#endregion
+ #region Secret Store Registration
+
+ ///
+ /// Registers a singleton secret store with this service.
+ ///
+ /// The type of secret store to register.
+ /// The current instance.
+ ///
+ /// A single instance of the secret store will be created to service all configured Dapr components.
+ ///
+ /// Only a single secret store type can be associated with a given service.
+ ///
+ public DaprPluggableComponentsServiceBuilder RegisterSecretStore() where TSecretStore : class, ISecretStore
+ {
+ this.AddComponent();
+ return this;
+ }
+
+ ///
+ /// Registers a secret store with this service.
+ ///
+ /// The type of secret store to register.
+ /// A factory method called when creating new secret store instances.
+ /// The current instance.
+ ///
+ /// The factory method will be called once for each configured Dapr component; the returned instance will be
+ /// associated with that Dapr component and methods invoked when the component receives requests.
+ ///
+ /// Only a single secret store type can be associated with a given service.
+ ///
+ public DaprPluggableComponentsServiceBuilder RegisterSecretStore(ComponentProviderDelegate secretStoreFactory)
+ where TSecretStore : class, ISecretStore
+ {
+ this.AddComponent(secretStoreFactory);
+ return this;
+ }
+
+ #endregion
+
private void AddComponent()
where TComponentType : class
where TComponentImpl : class, TComponentType
@@ -236,4 +276,9 @@ private void AddRelatedStateStoreServices() where TStateStore : cla
this.AddRelatedService();
}
}
+
+ private void AddRelatedSecretStoreServices() where TSecretStore : class
+ {
+ this.AddRelatedService();
+ }
}
diff --git a/src/Dapr.PluggableComponents.Protos/Dapr.PluggableComponents.Protos.csproj b/src/Dapr.PluggableComponents.Protos/Dapr.PluggableComponents.Protos.csproj
index 6986646..9b68e40 100644
--- a/src/Dapr.PluggableComponents.Protos/Dapr.PluggableComponents.Protos.csproj
+++ b/src/Dapr.PluggableComponents.Protos/Dapr.PluggableComponents.Protos.csproj
@@ -1,8 +1,8 @@
-
+
v1
- v1.11.0
+ v1.14.4
https://raw.githubusercontent.com/dapr/dapr/$(ProtosTag)/dapr/proto/components/$(ProtosVersion)
$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework)\Protos
$(ProtosRootDir)\dapr\proto\components\$(ProtosVersion)
@@ -13,6 +13,7 @@
+
@@ -36,12 +37,8 @@
-
-
+
+
-
+
\ No newline at end of file
diff --git a/src/Dapr.PluggableComponents.Tests/Adaptors/AdaptorFixture.cs b/src/Dapr.PluggableComponents.Tests/Adaptors/AdaptorFixture.cs
index aa516d8..f8bcd3b 100644
--- a/src/Dapr.PluggableComponents.Tests/Adaptors/AdaptorFixture.cs
+++ b/src/Dapr.PluggableComponents.Tests/Adaptors/AdaptorFixture.cs
@@ -14,6 +14,7 @@
using Dapr.PluggableComponents.Components;
using Dapr.PluggableComponents.Components.Bindings;
using Dapr.PluggableComponents.Components.PubSub;
+using Dapr.PluggableComponents.Components.SecretStore;
using Dapr.PluggableComponents.Components.StateStore;
using Grpc.Core;
using Microsoft.Extensions.Logging;
@@ -75,6 +76,11 @@ public static AdaptorFixture CreateStateStore(IS
return new AdaptorFixture((logger, componentProvider) => new StateStoreAdaptor(logger, componentProvider), mockComponent);
}
+ public static AdaptorFixture CreateSecretStore(ISecretStore? mockComponent = null)
+ {
+ return new AdaptorFixture((logger, componentProvider) => new SecretStoreAdaptor(logger, componentProvider), mockComponent);
+ }
+
public static async Task TestInitAsync(
Func> adaptorFactory,
Func, Client.Autogen.Grpc.v1.MetadataRequest, Task> initCall)
diff --git a/src/Dapr.PluggableComponents.Tests/Adaptors/SecretStoreAdaptorTests.cs b/src/Dapr.PluggableComponents.Tests/Adaptors/SecretStoreAdaptorTests.cs
new file mode 100644
index 0000000..34360d1
--- /dev/null
+++ b/src/Dapr.PluggableComponents.Tests/Adaptors/SecretStoreAdaptorTests.cs
@@ -0,0 +1,137 @@
+// ------------------------------------------------------------------------
+// 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 Dapr.Client.Autogen.Grpc.v1;
+using Dapr.PluggableComponents.Adaptors;
+using Dapr.PluggableComponents.Components.SecretStore;
+using NSubstitute;
+using Xunit;
+
+namespace Dapr.PluggableComponents.Tests.Adaptors;
+
+public sealed class SecretStoreAdaptorTests
+{
+ [Fact]
+ public Task InitTests()
+ {
+ return AdaptorFixture.TestInitAsync(
+ () => AdaptorFixture.CreateSecretStore(),
+ (fixture, metadataRequest) => fixture.Adaptor.Init(new Proto.Components.V1.SecretStoreInitRequest { Metadata = metadataRequest }, fixture.Context));
+ }
+
+ [Fact]
+ public Task PingTests()
+ {
+ return AdaptorFixture.TestPingAsync(
+ AdaptorFixture.CreateSecretStore,
+ fixture => fixture.Adaptor.Ping(new PingRequest(), fixture.Context));
+ }
+
+ [Fact]
+ public async Task GetSecret()
+ {
+ using var fixture = AdaptorFixture.CreateSecretStore(Substitute.For());
+
+ string key = "key";
+
+ string key1 = "key1";
+ string key2 = "key2";
+
+ string value1 = "value1";
+ string value2 = "value2";
+
+ fixture.MockComponent
+ .GetAsync(Arg.Any(), Arg.Any())
+ .Returns(
+ new SecretStoreGetResponse
+ {
+ Secrets = new Dictionary
+ {
+ { key1, value1 },
+ { key2, value2 }
+ }
+ });
+
+ var getRequest = new Proto.Components.V1.GetSecretRequest();
+
+ getRequest.Key = key;
+
+ var response = await fixture.Adaptor.Get(
+ getRequest,
+ fixture.Context);
+
+ Assert.Contains(response.Data, item => item.Key == key1 && item.Value == value1);
+ Assert.Contains(response.Data, item => item.Key == key2 && item.Value == value2);
+
+ await fixture
+ .MockComponent
+ .Received(1)
+ .GetAsync(Arg.Is(
+ requests => requests.Key == key),
+ Arg.Is(cancellationToken => cancellationToken == fixture.Context.CancellationToken));
+ }
+
+ [Fact]
+ public async Task GetBulkSecrets()
+ {
+ using var fixture = AdaptorFixture.CreateSecretStore(Substitute.For());
+
+ string key = "key";
+
+ string key1 = "key1";
+ string key2 = "key2";
+
+ string value1 = "value1";
+ string value2 = "value2";
+
+ fixture.MockComponent
+ .BulkGetAsync(Arg.Any(), Arg.Any())
+ .Returns(
+ new SecretStoreBulkGetResponse
+ {
+ Keys = new Dictionary
+ {
+ {
+ key,
+ new SecretStoreGetResponse
+ {
+ Secrets = new Dictionary
+ {
+ { key1, value1 },
+ { key2, value2 }
+ }
+ }
+ }
+ }
+ });
+
+ var getRequest = new Proto.Components.V1.BulkGetSecretRequest();
+
+ var response = await fixture.Adaptor.BulkGet(
+ getRequest,
+ fixture.Context);
+
+ Assert.Contains(response.Data, item => item.Key == key);
+
+ var secretResponse = response.Data[key];
+
+ Assert.Contains(secretResponse.Secrets, item => item.Key == key1 && item.Value == value1);
+ Assert.Contains(secretResponse.Secrets, item => item.Key == key2 && item.Value == value2);
+
+ await fixture
+ .MockComponent
+ .Received(1)
+ .BulkGetAsync(Arg.Any(),
+ Arg.Is(cancellationToken => cancellationToken == fixture.Context.CancellationToken));
+ }
+}
diff --git a/src/Dapr.PluggableComponents/Adaptors/SecretStoreAdaptor.cs b/src/Dapr.PluggableComponents/Adaptors/SecretStoreAdaptor.cs
new file mode 100644
index 0000000..b4ed315
--- /dev/null
+++ b/src/Dapr.PluggableComponents/Adaptors/SecretStoreAdaptor.cs
@@ -0,0 +1,116 @@
+// ------------------------------------------------------------------------
+// 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 Dapr.Client.Autogen.Grpc.v1;
+using Dapr.PluggableComponents.Components;
+using Dapr.PluggableComponents.Components.SecretStore;
+using Dapr.Proto.Components.V1;
+using Grpc.Core;
+using Microsoft.Extensions.Logging;
+using static Dapr.Proto.Components.V1.SecretStore;
+
+namespace Dapr.PluggableComponents.Adaptors;
+
+///
+/// Represents the gRPC protocol adaptor for a state store Dapr Pluggable Component.
+///
+///
+/// An instances of this class is created for every request made to the component.
+///
+public class SecretStoreAdaptor : SecretStoreBase
+{
+ private readonly ILogger logger;
+ private readonly IDaprPluggableComponentProvider componentProvider;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// A logger used for internal purposes.
+ /// A means to obtain the Dapr Pluggable Component associated with this adapter instance.
+ /// If any parameter is null.
+ public SecretStoreAdaptor(ILogger logger, IDaprPluggableComponentProvider componentProvider)
+ {
+ this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ this.componentProvider = componentProvider ?? throw new ArgumentNullException(nameof(componentProvider));
+ }
+
+ ///
+ public override async Task Features(FeaturesRequest request, ServerCallContext context)
+ {
+ this.logger.LogDebug("Features request");
+
+ var response = new FeaturesResponse();
+
+ if (this.GetSecretStore(context) is IPluggableComponentFeatures features)
+ {
+ var featuresResponse = await features.GetFeaturesAsync(context.CancellationToken);
+
+ response.Features.AddRange(featuresResponse);
+ }
+
+ return response;
+ }
+
+ ///
+ public override async Task Get(GetSecretRequest request, ServerCallContext context)
+ {
+ this.logger.LogDebug("Get request for key {key}", request.Key);
+
+ var response = await this.GetSecretStore(context).GetAsync(
+ SecretStoreGetRequest.FromGetRequest(request),
+ context.CancellationToken);
+
+ return SecretStoreGetResponse.ToGetResponse(response);
+ }
+
+ ///
+ public override async Task BulkGet(BulkGetSecretRequest request, ServerCallContext context)
+ {
+ this.logger.LogDebug("Bulk get request for secret");
+
+ var secretStore = this.GetSecretStore(context);
+ var response = await secretStore.BulkGetAsync(SecretStoreBulkGetRequest.FromGetRequest(request), context.CancellationToken);
+ return SecretStoreBulkGetResponse.ToBulkGetResponse(response);
+ }
+
+ ///
+ public async override Task Init(Proto.Components.V1.SecretStoreInitRequest request, ServerCallContext context)
+ {
+ this.logger.LogDebug("Init request");
+
+ await this.GetSecretStore(context).InitAsync(
+ Components.MetadataRequest.FromMetadataRequest(request.Metadata),
+ context.CancellationToken);
+
+ return new SecretStoreInitResponse();
+ }
+
+ ///
+ public override async Task Ping(PingRequest request, ServerCallContext context)
+ {
+ this.logger.LogDebug("Ping request");
+
+ if (this.GetSecretStore(context) is IPluggableComponentLiveness ping)
+ {
+ await ping.PingAsync(context.CancellationToken);
+ }
+
+ return new PingResponse();
+ }
+
+ private ISecretStore GetSecretStore(ServerCallContext context)
+ {
+ return this.componentProvider.GetComponent(context);
+ }
+}
+
diff --git a/src/Dapr.PluggableComponents/Components/SecretStore/ISecretStore.cs b/src/Dapr.PluggableComponents/Components/SecretStore/ISecretStore.cs
new file mode 100644
index 0000000..4a8ae7e
--- /dev/null
+++ b/src/Dapr.PluggableComponents/Components/SecretStore/ISecretStore.cs
@@ -0,0 +1,37 @@
+// ------------------------------------------------------------------------
+// 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.
+// ------------------------------------------------------------------------
+
+namespace Dapr.PluggableComponents.Components.SecretStore;
+
+///
+/// Represents a secret store Dapr Pluggable Component.
+///
+public interface ISecretStore : IPluggableComponent
+{
+ ///
+ /// Called to get secret.
+ ///
+ /// Properties related to the secret to be retrieved.
+ /// The token to monitor for cancellation requests.
+ /// A representing the asynchronous operation, resulting in the retrieved secret, if any.
+ Task GetAsync(SecretStoreGetRequest request, CancellationToken cancellationToken = default);
+
+ ///
+ /// Called to get bulk secret.
+ ///
+ /// Properties related to the secret to be retrieved.
+ /// The token to monitor for cancellation requests.
+ /// A representing the asynchronous operation, resulting in the retrieved secret, if any.
+ Task BulkGetAsync(SecretStoreBulkGetRequest request, CancellationToken cancellationToken = default);
+
+}
diff --git a/src/Dapr.PluggableComponents/Components/SecretStore/SecretStoreBulkGetRequest.cs b/src/Dapr.PluggableComponents/Components/SecretStore/SecretStoreBulkGetRequest.cs
new file mode 100644
index 0000000..58cdb58
--- /dev/null
+++ b/src/Dapr.PluggableComponents/Components/SecretStore/SecretStoreBulkGetRequest.cs
@@ -0,0 +1,36 @@
+// ------------------------------------------------------------------------
+// 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 Dapr.Proto.Components.V1;
+
+namespace Dapr.PluggableComponents.Components.SecretStore;
+
+
+///
+/// Represents properties associated with a request to retrieve all secrets from a secret store.
+///
+public sealed record SecretStoreBulkGetRequest
+{
+ ///
+ /// Gets the metadata associated with the request.
+ ///
+ public IReadOnlyDictionary Metadata { get; init; } = new Dictionary();
+
+ internal static SecretStoreBulkGetRequest FromGetRequest(BulkGetSecretRequest request)
+ {
+ return new SecretStoreBulkGetRequest()
+ {
+ Metadata = request.Metadata
+ };
+ }
+}
diff --git a/src/Dapr.PluggableComponents/Components/SecretStore/SecretStoreBulkGetResponse.cs b/src/Dapr.PluggableComponents/Components/SecretStore/SecretStoreBulkGetResponse.cs
new file mode 100644
index 0000000..af3dd89
--- /dev/null
+++ b/src/Dapr.PluggableComponents/Components/SecretStore/SecretStoreBulkGetResponse.cs
@@ -0,0 +1,47 @@
+// ------------------------------------------------------------------------
+// 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 Dapr.Proto.Components.V1;
+
+namespace Dapr.PluggableComponents.Components.SecretStore;
+
+///
+/// Represents properties associated with a response to retrieving all secrets from a secret store.
+///
+public sealed record SecretStoreBulkGetResponse
+{
+ ///
+ /// Gets the groups of secrets.
+ ///
+ public IReadOnlyDictionary Keys { get; init; } = new Dictionary();
+
+ internal static BulkGetSecretResponse ToBulkGetResponse(SecretStoreBulkGetResponse response)
+ {
+ BulkGetSecretResponse grpcResponse = new();
+
+ foreach (var item in response.Keys)
+ {
+ SecretResponse secretResp = new();
+
+ foreach (var sec in item.Value.Secrets)
+ {
+ secretResp.Secrets.Add(sec.Key, sec.Value);
+ }
+
+ grpcResponse.Data.Add(item.Key, secretResp);
+ }
+
+ return grpcResponse;
+ }
+}
+
diff --git a/src/Dapr.PluggableComponents/Components/SecretStore/SecretStoreGetRequest.cs b/src/Dapr.PluggableComponents/Components/SecretStore/SecretStoreGetRequest.cs
new file mode 100644
index 0000000..f73d94a
--- /dev/null
+++ b/src/Dapr.PluggableComponents/Components/SecretStore/SecretStoreGetRequest.cs
@@ -0,0 +1,37 @@
+// ------------------------------------------------------------------------
+// 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 Dapr.Proto.Components.V1;
+
+namespace Dapr.PluggableComponents.Components.SecretStore;
+
+///
+/// Represents properties associated with a request to retrieve a secret from a secret store.
+///
+/// The key for the secrets that should be retrieved.
+public sealed record SecretStoreGetRequest(string Key)
+{
+ ///
+ /// Gets the metadata associated with the request.
+ ///
+ public IReadOnlyDictionary Metadata { get; init; } = new Dictionary();
+
+ internal static SecretStoreGetRequest FromGetRequest(GetSecretRequest request)
+ {
+ return new SecretStoreGetRequest(request.Key)
+ {
+ Metadata = request.Metadata
+ };
+ }
+}
+
diff --git a/src/Dapr.PluggableComponents/Components/SecretStore/SecretStoreGetResponse.cs b/src/Dapr.PluggableComponents/Components/SecretStore/SecretStoreGetResponse.cs
new file mode 100644
index 0000000..adad4f4
--- /dev/null
+++ b/src/Dapr.PluggableComponents/Components/SecretStore/SecretStoreGetResponse.cs
@@ -0,0 +1,42 @@
+// ------------------------------------------------------------------------
+// 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 Dapr.PluggableComponents.Utilities;
+using Dapr.Proto.Components.V1;
+
+namespace Dapr.PluggableComponents.Components.SecretStore;
+
+///
+/// Represents properties associated with a response to retrieving secret from a secret store.
+///
+public sealed record SecretStoreGetResponse
+{
+ ///
+ /// Gets the secrets.
+ ///
+ public IReadOnlyDictionary Secrets { get; init; } = new Dictionary();
+
+ internal static GetSecretResponse ToGetResponse(SecretStoreGetResponse response)
+ {
+ var grpcResponse = new GetSecretResponse();
+
+ // NOTE: in case of not found, you should not return any error.
+
+ if (response != null)
+ {
+ grpcResponse.Data.Add(response.Secrets);
+ }
+
+ return grpcResponse;
+ }
+}