From 47fb1bdc92e2132f5615c677d46021d3877294c8 Mon Sep 17 00:00:00 2001 From: youcefnb Date: Mon, 27 Oct 2025 13:10:37 -0600 Subject: [PATCH 1/2] Refactor SDK to .NetStandard + Newtonsoft.Json --- Cargo.lock | 2 +- .../Bitwarden.Sdk.Tests.csproj | 6 ++-- .../csharp/Bitwarden.Sdk.Tests/SampleTests.cs | 2 +- .../csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj | 5 +-- .../Bitwarden.Sdk/BitwardenClient.Debug.cs | 27 +++++++++------ .../csharp/Bitwarden.Sdk/BitwardenClient.cs | 4 +-- .../csharp/Bitwarden.Sdk/BitwardenLibrary.cs | 34 +++++++++++-------- .../csharp/Bitwarden.Sdk/CommandRunner.cs | 12 +++---- languages/csharp/global.json | 2 +- package-lock.json | 2 +- package.json | 2 +- support/scripts/schemas.ts | 2 +- 12 files changed, 55 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eeb4fbc96..63055766d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -508,7 +508,7 @@ dependencies = [ [[package]] name = "bitwarden-py" -version = "0.1.0" +version = "1.0.0" dependencies = [ "bitwarden-json", "pyo3", diff --git a/languages/csharp/Bitwarden.Sdk.Tests/Bitwarden.Sdk.Tests.csproj b/languages/csharp/Bitwarden.Sdk.Tests/Bitwarden.Sdk.Tests.csproj index 3dbd8c99e..d5608e0a1 100644 --- a/languages/csharp/Bitwarden.Sdk.Tests/Bitwarden.Sdk.Tests.csproj +++ b/languages/csharp/Bitwarden.Sdk.Tests/Bitwarden.Sdk.Tests.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -22,8 +22,8 @@ - - + + diff --git a/languages/csharp/Bitwarden.Sdk.Tests/SampleTests.cs b/languages/csharp/Bitwarden.Sdk.Tests/SampleTests.cs index 6432f9ea1..aa5dac221 100644 --- a/languages/csharp/Bitwarden.Sdk.Tests/SampleTests.cs +++ b/languages/csharp/Bitwarden.Sdk.Tests/SampleTests.cs @@ -1,4 +1,4 @@ -namespace Bitwarden.Sdk.Tests; +namespace Bitwarden.Sdk.Tests; public class SampleTests { diff --git a/languages/csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj b/languages/csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj index cca8c0e54..cd37e3d80 100644 --- a/languages/csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj +++ b/languages/csharp/Bitwarden.Sdk/Bitwarden.Sdk.csproj @@ -1,7 +1,8 @@ - net8.0 + netstandard2.0 + latest enable enable Bitwarden.Sdk @@ -25,7 +26,7 @@ - + diff --git a/languages/csharp/Bitwarden.Sdk/BitwardenClient.Debug.cs b/languages/csharp/Bitwarden.Sdk/BitwardenClient.Debug.cs index 0b1e95409..066fc60a8 100644 --- a/languages/csharp/Bitwarden.Sdk/BitwardenClient.Debug.cs +++ b/languages/csharp/Bitwarden.Sdk/BitwardenClient.Debug.cs @@ -1,6 +1,7 @@ +using System; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; -using System.Text.Json; +using Newtonsoft.Json.Linq; namespace Bitwarden.Sdk; @@ -8,7 +9,6 @@ namespace Bitwarden.Sdk; [EditorBrowsable(EditorBrowsableState.Never)] partial class DebugCommand { - } #if DEBUG @@ -16,7 +16,7 @@ public sealed partial class BitwardenClient { public async Task CancellationTestAsync(CancellationToken token) { - var result = await _commandRunner.RunCommandAsync( + var result = await _commandRunner.RunCommandAsync( new Command { Debug = new DebugCommand @@ -28,12 +28,12 @@ public async Task CancellationTestAsync(CancellationToken token) }, }, token); - return ParseResult(result).GetInt32(); + return ParseResult(result).Value(); } public async Task ErrorTestAsync() { - var result = await _commandRunner.RunCommandAsync( + var result = await _commandRunner.RunCommandAsync( new Command { Debug = new DebugCommand @@ -42,17 +42,24 @@ public async Task ErrorTestAsync() }, }, CancellationToken.None); - return ParseResult(result).GetInt32(); + return ParseResult(result).Value(); } - private JsonElement ParseResult(JsonElement result) + private JToken ParseResult(JToken result) { - if (result.GetProperty("success").GetBoolean()) + // Expecting: { "success": true|false, "data": ..., "errorMessage": "..." } + if (result is JObject obj && obj.Value("success") == true) { - return result.GetProperty("data"); + var data = obj["data"]; + if (data is null) + { + throw new BitwardenException("Missing 'data' in successful response."); + } + return data; } - throw new BitwardenException(result.GetProperty("errorMessage").GetString()); + var message = (result as JObject)?.Value("errorMessage") ?? "Unknown error."; + throw new BitwardenException(message); } } #endif diff --git a/languages/csharp/Bitwarden.Sdk/BitwardenClient.cs b/languages/csharp/Bitwarden.Sdk/BitwardenClient.cs index 636a100b1..a935ff2cb 100644 --- a/languages/csharp/Bitwarden.Sdk/BitwardenClient.cs +++ b/languages/csharp/Bitwarden.Sdk/BitwardenClient.cs @@ -1,6 +1,4 @@ -using System.Text.Json; - -namespace Bitwarden.Sdk; +namespace Bitwarden.Sdk; public sealed partial class BitwardenClient : IDisposable { diff --git a/languages/csharp/Bitwarden.Sdk/BitwardenLibrary.cs b/languages/csharp/Bitwarden.Sdk/BitwardenLibrary.cs index e18d3976b..807cfa6d9 100644 --- a/languages/csharp/Bitwarden.Sdk/BitwardenLibrary.cs +++ b/languages/csharp/Bitwarden.Sdk/BitwardenLibrary.cs @@ -4,34 +4,38 @@ namespace Bitwarden.Sdk; internal static partial class BitwardenLibrary { - [LibraryImport("bitwarden_c", StringMarshalling = StringMarshalling.Utf8)] - private static partial BitwardenSafeHandle init(string settings); + [DllImport("bitwarden_c", CharSet = CharSet.Ansi, EntryPoint = "init")] + private static extern BitwardenSafeHandle init(string settings); - [LibraryImport("bitwarden_c", StringMarshalling = StringMarshalling.Utf8)] - private static partial void free_mem(IntPtr handle); + [DllImport("bitwarden_c", CharSet = CharSet.Ansi, EntryPoint = "free_mem")] + private static extern void free_mem(IntPtr handle); - [LibraryImport("bitwarden_c", StringMarshalling = StringMarshalling.Utf8)] - private static partial string run_command(string json, BitwardenSafeHandle handle); + [DllImport("bitwarden_c", CharSet = CharSet.Ansi, EntryPoint = "run_command")] + private static extern IntPtr run_command(string json, BitwardenSafeHandle handle); internal delegate void OnCompleteCallback(IntPtr json); - [LibraryImport("bitwarden_c", StringMarshalling = StringMarshalling.Utf8)] - private static partial IntPtr run_command_async(string json, + [DllImport("bitwarden_c", CharSet = CharSet.Ansi, EntryPoint = "run_command_async")] + private static extern IntPtr run_command_async(string json, BitwardenSafeHandle handle, OnCompleteCallback onCompletedCallback, [MarshalAs(UnmanagedType.U1)] bool isCancellable); - [LibraryImport("bitwarden_c", StringMarshalling = StringMarshalling.Utf8)] - private static partial void abort_and_free_handle(IntPtr joinHandle); + [DllImport("bitwarden_c", CharSet = CharSet.Ansi, EntryPoint = "abort_and_free_handle")] + private static extern void abort_and_free_handle(IntPtr joinHandle); - [LibraryImport("bitwarden_c", StringMarshalling = StringMarshalling.Utf8)] - private static partial void free_handle(IntPtr joinHandle); + [DllImport("bitwarden_c", CharSet = CharSet.Ansi, EntryPoint = "free_handle")] + private static extern void free_handle(IntPtr joinHandle); internal static BitwardenSafeHandle Init(string settings) => init(settings); internal static void FreeMemory(IntPtr handle) => free_mem(handle); - internal static string RunCommand(string json, BitwardenSafeHandle handle) => run_command(json, handle); + internal static string RunCommand(string json, BitwardenSafeHandle handle) + { + IntPtr resultPtr = run_command(json, handle); + return Marshal.PtrToStringAnsi(resultPtr); + } internal static Task RunCommandAsync(string json, BitwardenSafeHandle handle, CancellationToken cancellationToken) { @@ -45,7 +49,7 @@ internal static Task RunCommandAsync(string json, BitwardenSafeHandle ha abortPointer = run_command_async(json, handle, (resultPointer) => { - var stringResult = Marshal.PtrToStringUTF8(resultPointer); + var stringResult = Marshal.PtrToStringAnsi(resultPointer); tcs.SetResult(stringResult); if (abortPointer != IntPtr.Zero) @@ -64,7 +68,7 @@ internal static Task RunCommandAsync(string json, BitwardenSafeHandle ha // This register delegate will never be called unless the token is cancelable // therefore we know that the abortPointer is a valid pointer. abort_and_free_handle((IntPtr)state); - tcs.SetCanceled(cancellationToken); + tcs.SetCanceled(); }, abortPointer); return tcs.Task; diff --git a/languages/csharp/Bitwarden.Sdk/CommandRunner.cs b/languages/csharp/Bitwarden.Sdk/CommandRunner.cs index a62e0877e..9f075eb53 100644 --- a/languages/csharp/Bitwarden.Sdk/CommandRunner.cs +++ b/languages/csharp/Bitwarden.Sdk/CommandRunner.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using Newtonsoft.Json; namespace Bitwarden.Sdk; @@ -13,21 +13,21 @@ internal CommandRunner(BitwardenSafeHandle handle) internal T? RunCommand(Command command) { - var req = JsonSerializer.Serialize(command, Converter.Settings); + var req = JsonConvert.SerializeObject(command, Converter.Settings); var result = BitwardenLibrary.RunCommand(req, _handle); - return JsonSerializer.Deserialize(result, Converter.Settings); + return JsonConvert.DeserializeObject(result, Converter.Settings); } internal async Task RunCommandAsync(Command command, CancellationToken cancellationToken) { - var req = JsonSerializer.Serialize(command, Converter.Settings); + var req = JsonConvert.SerializeObject(command, Converter.Settings); var result = await BitwardenLibrary.RunCommandAsync(req, _handle, cancellationToken); - return JsonSerializer.Deserialize(result, Converter.Settings); + return JsonConvert.DeserializeObject(result, Converter.Settings); } internal async Task RunCommandAsync(string command, CancellationToken cancellationToken) { var result = await BitwardenLibrary.RunCommandAsync(command, _handle, cancellationToken); - return JsonSerializer.Deserialize(result, Converter.Settings); + return JsonConvert.DeserializeObject(result, Converter.Settings); } } diff --git a/languages/csharp/global.json b/languages/csharp/global.json index 391ba3c2a..f00af8bb6 100644 --- a/languages/csharp/global.json +++ b/languages/csharp/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.100", + "version": "10.0.100-rc.2.25502.107", "rollForward": "latestFeature" } } diff --git a/package-lock.json b/package-lock.json index 076083ee0..fe644814e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "devDependencies": { "prettier": "3.6.0", "quicktype-core": "23.2.6", - "rimraf": "6.0.1", + "rimraf": "^6.0.1", "ts-node": "10.9.2", "typescript": "5.5.4" } diff --git a/package.json b/package.json index 0457f08bb..c20081114 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "devDependencies": { "prettier": "3.6.0", "quicktype-core": "23.2.6", - "rimraf": "6.0.1", + "rimraf": "^6.0.1", "ts-node": "10.9.2", "typescript": "5.5.4" } diff --git a/support/scripts/schemas.ts b/support/scripts/schemas.ts index 873a9de7d..25c7d285c 100644 --- a/support/scripts/schemas.ts +++ b/support/scripts/schemas.ts @@ -55,7 +55,7 @@ async function main() { lang: "csharp", rendererOptions: { namespace: "Bitwarden.Sdk", - framework: "SystemTextJson", + framework: "NewtonSoft", "csharp-version": "6", }, }); From 6d1231c86b4e6e413fc7cf06fe8949269c6d6706 Mon Sep 17 00:00:00 2001 From: youcefnb Date: Mon, 27 Oct 2025 17:13:51 -0600 Subject: [PATCH 2/2] fix: update README to reflect asynchronous SDK method usage --- languages/csharp/README.md | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/languages/csharp/README.md b/languages/csharp/README.md index ece863fa3..d3d331154 100644 --- a/languages/csharp/README.md +++ b/languages/csharp/README.md @@ -20,28 +20,28 @@ using var bitwardenClient = new BitwardenClient(new BitwardenSettings IdentityUrl = identityUrl }); -bitwardenClient.LoginAccessToken(accessToken, stateFile); +await bitwardenClient.Auth.LoginAccessTokenAsync(accessToken, stateFile); ``` ### Create new project ```csharp var organizationId = Guid.Parse(""); -var projectResponse = bitwardenClient.Projects().Create(organizationId, "TestProject"); +var projectResponse = await bitwardenClient.Projects.CreateAsync(organizationId, "TestProject"); ``` ### List all projects ```csharp -var response = bitwardenClient.Projects.List(organizationId); +var projectList = await bitwardenClient.Projects.ListAsync(organizationId); ``` ### Update project ```csharp var projectId = projectResponse.Id; -projectResponse = bitwardenClient.Projects.Get(projectId); -projectResponse = bitwardenClient.Projects.Update(organizationId, projectId, "TestProjectUpdated"); +projectResponse = await bitwardenClient.Projects.UpdateAsync(organizationId, projectId, "TestProjectUpdated"); +projectResponse = await bitwardenClient.Projects.GetAsync(projectId); ``` ### Add new secret @@ -50,40 +50,41 @@ projectResponse = bitwardenClient.Projects.Update(organizationId, projectId, "Te var key = "key"; var value = "value"; var note = "note"; -var secretResponse = bitwardenClient.Secrets.Create(organizationId, key, value, note, new[] { projectId }); +var secretResponse = await bitwardenClient.Secrets.CreateAsync(organizationId, key, value, note, new[] { projectId }); ``` ### Update secret ```csharp var secretId = secretResponse.Id; -secretResponse = bitwardenClient.Secrets.Get(secretId); -secretResponse = bitwardenClient.Secrets.Update(organizationId, secretId, "key2", "value2", "note2", new[] { projectId }); +secretResponse = await bitwardenClient.Secrets.UpdateAsync(organizationId, secretId, "key2", "value2", "note2", new[] { projectId }); +secretResponse = await bitwardenClient.Secrets.GetAsync(secretId); ``` ### Secret GetByIds ```csharp -var secretsResponse = bitwardenClient.Secrets.GetByIds(new[] { secretResponse.Id }); +var secretsResponse = await bitwardenClient.Secrets.GetByIdsAsync(new[] { secretResponse.Id }); ``` ### List secrets ```csharp -var secretIdentifiersResponse = bitwardenClient.Secrets.List(organizationId); +var secretsList = await bitwardenClient.Secrets.ListAsync(organizationId); ``` ### Sync secrets ```csharp -var syncResponse = bitwardenClient.Secrets.Sync(organizationId, null); +var syncResponse = await bitwardenClient.Secrets.SyncAsync(organizationId, null); ``` # Delete secret or project ```csharp -bitwardenClient.Secrets.Delete(new [] { secretId }); -bitwardenClient.Projects.Delete(new [] { projectId }); +await bitwardenClient.Secrets.DeleteAsync(new [] { secretId }); +await bitwardenClient.Projects.DeleteAsync(new [] { projectId }); ``` +# All main SDK methods are asynchronous. Use `await` and ensure your calling code is in an `async Task` method. [Access Tokens]: https://bitwarden.com/help/access-tokens/ -[Bitwarden Secrets Manager]: https://bitwarden.com/products/secrets-manager/ +[Bitwarden Secrets Manager]: https://bitwarden.com/products/secrets-manager/ \ No newline at end of file