feat: Implement Dapr.SecretsManagement as a purpose-specific client#1794
feat: Implement Dapr.SecretsManagement as a purpose-specific client#1794
Conversation
…erators, Aggregator, Tests, Example) Implements #1510 - Migrates secret management into a purpose-specific client. Projects created: - Dapr.SecretsManagement.Abstractions: SecretStoreAttribute, SecretAttribute - Dapr.SecretsManagement.Runtime: gRPC client, builder, DI extensions - Dapr.SecretsManagement.Generators: Source generator for typed secret stores - Dapr.SecretsManagement: Aggregator meta-project (NoTargets) - Dapr.SecretsManagement.Runtime.Test: Unit tests - examples/SecretManagement: Demo project Agent-Logs-Url: https://github.com/dapr/dotnet-sdk/sessions/db970aa7-6b0c-450f-93f9-878185757985 Co-authored-by: WhitWaldo <2238529+WhitWaldo@users.noreply.github.com>
…r csproj Agent-Logs-Url: https://github.com/dapr/dotnet-sdk/sessions/db970aa7-6b0c-450f-93f9-878185757985 Co-authored-by: WhitWaldo <2238529+WhitWaldo@users.noreply.github.com>
WhitWaldo
left a comment
There was a problem hiding this comment.
I think we need a few changes throughout
| "commandName": "Project", | ||
| "dotnetRunMessages": true, | ||
| "launchBrowser": false, | ||
| "applicationUrl": "http://localhost:5234", |
There was a problem hiding this comment.
Can you ensure that the port is consistent across other examples? I know they're all over the place today, but I'm ok standardizing on something easy like 6543 so all the READMEs can use a consistent port across all examples.
There was a problem hiding this comment.
Standardized to port 6543 in both launchSettings.json and README.md in ff244d8.
| return Results.Ok(secrets); | ||
| }); | ||
|
|
||
| // --- Example 3: Using typed secret store (source-generated) --- |
There was a problem hiding this comment.
Why is example 3 commented out?
There was a problem hiding this comment.
Uncommented and fully wired up — the example now registers AddMyVaultSecrets() and maps a /typed-secrets endpoint that injects the source-generated IMyVaultSecrets. Fixed in ff244d8.
| </PropertyGroup> | ||
|
|
||
| <!-- | ||
| NOTE: When consuming from NuGet, use the single 'Dapr.SecretsManagement' package instead of |
| ## Running the Sample | ||
|
|
||
| ```bash | ||
| dapr run --app-id secret-sample --app-port 5234 -- dotnet run |
There was a problem hiding this comment.
Remember to use the standardized port here
| /// <summary> | ||
| /// Well-known names and constants related to Dapr Secrets Management. | ||
| /// </summary> | ||
| public static class WellKnownSecrets |
There was a problem hiding this comment.
We should mark as many things internal and use InternalsVisibleTo as possible to narrow the public API
There was a problem hiding this comment.
Made WellKnownSecrets internal in ff244d8. The generator already uses its own string constants for attribute lookup, so no InternalsVisibleTo is needed.
| { | ||
| spc.ReportDiagnostic(Diagnostic.Create( | ||
| new DiagnosticDescriptor( | ||
| "DAPRSEC001", |
There was a problem hiding this comment.
We only use DAPR as a prefix and instead use the first two numbers of the identifier as a type identifier and refer to the documentation at https://docs.dapr.io/developing-applications/sdks/dotnet/dotnet-guidance/dotnet-guidance-source-generators/ for more details beyond the diagnostic message itself. We've generally reserved numbers for analyzers and codefixes - not sure how to approach diagnostics. Should this be handled in the same way?
There was a problem hiding this comment.
Changed to DAPR1601 following the DAPRxxyy convention (16 = SecretsManagement, matching how 13 = Workflow, 14 = Actors, 15 = Jobs). Fixed in ff244d8.
| "DAPRSEC001", | ||
| "SecretStore attribute not found", | ||
| "The source generator could not find the type '" + SecretStoreAttributeFullName + | ||
| "'. Ensure that Dapr.SecretsManagement.Abstractions is properly referenced.", |
There was a problem hiding this comment.
This might not be helpful to a user of the source generator as it's getting bundled into Dapr.SecretsManagement - they don't know anything about the packages comprising it.
There was a problem hiding this comment.
Updated the message to reference Dapr.SecretsManagement (the user-facing package) instead of the internal Abstractions sub-package. Fixed in ff244d8.
|
|
||
| // Implementation class name: strip leading 'I' from interface name, append "SecretStoreClient". | ||
| var implName = interfaceName.StartsWith("I", StringComparison.Ordinal) && interfaceName.Length > 1 && char.IsUpper(interfaceName[1]) | ||
| ? interfaceName.Substring(1) + "SecretStoreClient" |
There was a problem hiding this comment.
How well does this work if there are multiple secrets being registered and they share the same type name across different namespaces? Seems like this should instead do some sort of hash of the full type name instead and then append something more readable.
There was a problem hiding this comment.
Now uses implName + "_" + FNV-1a-hash(fullyQualifiedName).ToString("X8") + ".g.cs" for the hint name, e.g. MyVaultSecretsSecretStoreClient_2D89517D.g.cs. This ensures unique hint names even when different namespaces contain interfaces with the same simple name. Fixed in ff244d8.
| <PropertyGroup> | ||
| <IsPackable>true</IsPackable> | ||
| <PackageId>Dapr.SecretsManagement</PackageId> | ||
| <Description>Provides a purpose-specific client, source generator, and DI integration for working with Dapr secret stores in .NET applications</Description> |
There was a problem hiding this comment.
This could be simplified - users needn't know all of this to use the package from the NuGet description
There was a problem hiding this comment.
Simplified to Dapr Secrets Management SDK for .NET in ff244d8.
|
@copilot Also, please fix the merge conflicts |
…arrow public API, fix diagnostics, hash-based hint names - Standardize example port to 6543 (launchSettings.json + README.md) - Uncomment and wire up Example 3 (typed secret store via source generator) - Make WellKnownSecrets internal to narrow public API surface - Change diagnostic ID from DAPRSEC001 to DAPR1601 (consistent with DAPRxxyy convention) - Fix diagnostic message to reference Dapr.SecretsManagement (not the internal Abstractions sub-package) - Use FNV-1a hash of FQN for hint names to avoid collisions across namespaces - Simplify NuGet package description Agent-Logs-Url: https://github.com/dapr/dotnet-sdk/sessions/a7a790c9-bd31-4dd6-aad6-142a0b6d9c83 Co-authored-by: WhitWaldo <2238529+WhitWaldo@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
Co-authored-by: WhitWaldo <2238529+WhitWaldo@users.noreply.github.com>
Merge conflicts resolved in 347e0c8. The conflict was in |
Codecov Report✅ All modified and coverable lines are covered by tests.
Additional details and impacted files@@ Coverage Diff @@
## master #1794 +/- ##
===========================================
- Coverage 62.37% 43.87% -18.51%
===========================================
Files 285 285
Lines 8421 8421
Branches 980 980
===========================================
- Hits 5253 3695 -1558
- Misses 2935 4508 +1573
+ Partials 233 218 -15
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
WhitWaldo
left a comment
There was a problem hiding this comment.
Tentatively amazing, but I want to delve deeper into the service collection extensions
| /// An optional callback used to configure the <see cref="DaprSecretsManagementClientBuilder"/> with injected services. | ||
| /// </param> | ||
| /// <param name="lifetime">The lifetime of the registered services. Defaults to <see cref="ServiceLifetime.Singleton"/>.</param> | ||
| /// <returns>An <see cref="IDaprSecretsManagementBuilder"/> that can be used for further configuration.</returns> |
There was a problem hiding this comment.
Presently, all the implementation projects take a dependency on Dapr.Common and the DaprGenericClientBuilder to get the HttpClient and gRPC connection properly configured, retrieve app tokens, Dapr's gRPC/HTTP port, the hostname, etc. from DI, the IConfiguration or environment variables (with a fallback to the default values) in a common way.
This is a static approach and I'd like to tentatively look at revamping this. It's all internal, so making changes without impacting the public API, I'd like to look at facilitating a more fluent way of registering the client that allows any number of add-on methods (like registering a common serialization approach like I do in Dapr.Workflow today or augmenting how HttpClient is registered (e.g. allow users to provide their own interceptor overrides and the like). There has been some discussion about the HttpClient approach in this issue and there have been similar discussions in the past, but I'd like to explore options to improve the developer experience on this front going forward both in this library and more broadly.
Description
Migrates secret management out of
Dapr.Clientinto a dedicated, purpose-specific package following theDapr.Workflow.Versioningaggregator pattern. gRPC-only, DI-first, with a source generator for strongly-typed secret store access.Projects
Microsoft.Build.NoTargetsaggregator; single NuGet package consumers install[SecretStore],[Secret]attributes for source gen (internal API surface narrowed where possible)DaprSecretsManagementClient(abstract) +DaprSecretsManagementGrpcClient(sealed internal), builder, DI extensions[SecretStore]-annotated interfaces → emits implementation,IHostedServiceloader, and DI registration extension. UsesDAPR16xxdiagnostic ID convention and FNV-1a hash-based hint names to avoid collisions across namespaces.Source Generator Usage
The generator emits a concrete implementation backed by bulk secret retrieval at startup, an
IHostedServicefor pre-loading, and a typed DI registration extension.Other Changes
src/Dapr.Common/AssemblyInfo.cs—InternalsVisibleTofor Runtime + Test.github/workflows/sdk_build.yml— excludeDapr.SecretsManagement.{Abstractions,Generators,Runtime}from NuGet publish (same pattern as Workflow.Versioning)Issue reference
#1510
Checklist