diff --git a/lib/net9.0/Voucherify.deps.json b/lib/net9.0/Voucherify.deps.json index f05ddd0c..2c1cb4e9 100644 --- a/lib/net9.0/Voucherify.deps.json +++ b/lib/net9.0/Voucherify.deps.json @@ -6,7 +6,7 @@ "compilationOptions": {}, "targets": { ".NETCoreApp,Version=v9.0": { - "Voucherify/9.0.0": { + "Voucherify/9.0.1": { "dependencies": { "JsonSubTypes": "2.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.10", @@ -478,7 +478,7 @@ } }, "libraries": { - "Voucherify/9.0.0": { + "Voucherify/9.0.1": { "type": "project", "serviceable": false, "sha512": "" diff --git a/lib/net9.0/Voucherify.dll b/lib/net9.0/Voucherify.dll index f2cc01bd..8a205b19 100644 Binary files a/lib/net9.0/Voucherify.dll and b/lib/net9.0/Voucherify.dll differ diff --git a/lib/net9.0/Voucherify.xml b/lib/net9.0/Voucherify.xml index 872b8003..ec9aac18 100644 --- a/lib/net9.0/Voucherify.xml +++ b/lib/net9.0/Voucherify.xml @@ -26260,6 +26260,24 @@ RestSharp's HttpMethod instance. + + + Starts a stopwatch when debug timing is enabled. + + Whether debug timing is enabled for the current request. + A started when enabled; otherwise null. + + + + Stops timing and writes a debug log entry for a request stage. + + Whether debug timing is enabled for the current request. + The stopwatch instance started for the stage. + The request stage label (for example: construction or execution). + HTTP method name. + Request resource path. + Optional response used to enrich debug metadata. + Provides all logic for constructing a new RestSharp . @@ -26844,6 +26862,11 @@ Determine whether or not the "default credentials" (e.g. the user account under which the current process is running) will be sent along to the server. The default is false. + + + Determines whether request timing diagnostics are enabled. + + Gets or sets the default header. @@ -27053,6 +27076,12 @@ Api Key name. Api Key value. + + + Checks whether debug mode is enabled via the DEBUG environment variable. + + true when DEBUG equals "true" (case-insensitive); otherwise, false. + Merge configurations. @@ -27341,6 +27370,11 @@ Determine whether or not the "default credentials" (e.g. the user account under which the current process is running) will be sent along to the server. The default is false. + + + Determines whether request timing diagnostics are enabled. + + Get the servers associated with the operation. diff --git a/lib/netstandard2.0/Voucherify.deps.json b/lib/netstandard2.0/Voucherify.deps.json index 7ef79ffa..667abb3a 100644 --- a/lib/netstandard2.0/Voucherify.deps.json +++ b/lib/netstandard2.0/Voucherify.deps.json @@ -7,10 +7,9 @@ "targets": { ".NETStandard,Version=v2.0": {}, ".NETStandard,Version=v2.0/": { - "Voucherify/9.0.0": { + "Voucherify/9.0.1": { "dependencies": { "JsonSubTypes": "2.0.1", - "NETStandard.Library": "2.0.3", "Newtonsoft.Json": "13.0.3", "Polly": "8.1.0", "RestSharp": "112.0.0", @@ -42,12 +41,6 @@ } } }, - "Microsoft.NETCore.Platforms/1.1.0": {}, - "NETStandard.Library/2.0.3": { - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" - } - }, "Newtonsoft.Json/13.0.3": { "runtime": { "lib/netstandard2.0/Newtonsoft.Json.dll": { @@ -179,7 +172,7 @@ } }, "libraries": { - "Voucherify/9.0.0": { + "Voucherify/9.0.1": { "type": "project", "serviceable": false, "sha512": "" @@ -198,20 +191,6 @@ "path": "microsoft.bcl.asyncinterfaces/8.0.0", "hashPath": "microsoft.bcl.asyncinterfaces.8.0.0.nupkg.sha512" }, - "Microsoft.NETCore.Platforms/1.1.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==", - "path": "microsoft.netcore.platforms/1.1.0", - "hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512" - }, - "NETStandard.Library/2.0.3": { - "type": "package", - "serviceable": true, - "sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", - "path": "netstandard.library/2.0.3", - "hashPath": "netstandard.library.2.0.3.nupkg.sha512" - }, "Newtonsoft.Json/13.0.3": { "type": "package", "serviceable": true, diff --git a/lib/netstandard2.0/Voucherify.dll b/lib/netstandard2.0/Voucherify.dll index 01a7deee..9f72bc5b 100644 Binary files a/lib/netstandard2.0/Voucherify.dll and b/lib/netstandard2.0/Voucherify.dll differ diff --git a/lib/netstandard2.0/Voucherify.xml b/lib/netstandard2.0/Voucherify.xml index 872b8003..ec9aac18 100644 --- a/lib/netstandard2.0/Voucherify.xml +++ b/lib/netstandard2.0/Voucherify.xml @@ -26260,6 +26260,24 @@ RestSharp's HttpMethod instance. + + + Starts a stopwatch when debug timing is enabled. + + Whether debug timing is enabled for the current request. + A started when enabled; otherwise null. + + + + Stops timing and writes a debug log entry for a request stage. + + Whether debug timing is enabled for the current request. + The stopwatch instance started for the stage. + The request stage label (for example: construction or execution). + HTTP method name. + Request resource path. + Optional response used to enrich debug metadata. + Provides all logic for constructing a new RestSharp . @@ -26844,6 +26862,11 @@ Determine whether or not the "default credentials" (e.g. the user account under which the current process is running) will be sent along to the server. The default is false. + + + Determines whether request timing diagnostics are enabled. + + Gets or sets the default header. @@ -27053,6 +27076,12 @@ Api Key name. Api Key value. + + + Checks whether debug mode is enabled via the DEBUG environment variable. + + true when DEBUG equals "true" (case-insensitive); otherwise, false. + Merge configurations. @@ -27341,6 +27370,11 @@ Determine whether or not the "default credentials" (e.g. the user account under which the current process is running) will be sent along to the server. The default is false. + + + Determines whether request timing diagnostics are enabled. + + Get the servers associated with the operation. diff --git a/lib/netstandard2.1/Voucherify.deps.json b/lib/netstandard2.1/Voucherify.deps.json index 1e39839c..bcbf3b82 100644 --- a/lib/netstandard2.1/Voucherify.deps.json +++ b/lib/netstandard2.1/Voucherify.deps.json @@ -7,7 +7,7 @@ "targets": { ".NETStandard,Version=v2.1": {}, ".NETStandard,Version=v2.1/": { - "Voucherify/9.0.0": { + "Voucherify/9.0.1": { "dependencies": { "JsonSubTypes": "2.0.1", "Newtonsoft.Json": "13.0.3", @@ -169,7 +169,7 @@ } }, "libraries": { - "Voucherify/9.0.0": { + "Voucherify/9.0.1": { "type": "project", "serviceable": false, "sha512": "" diff --git a/lib/netstandard2.1/Voucherify.dll b/lib/netstandard2.1/Voucherify.dll index e7b835d3..7db207b1 100644 Binary files a/lib/netstandard2.1/Voucherify.dll and b/lib/netstandard2.1/Voucherify.dll differ diff --git a/lib/netstandard2.1/Voucherify.xml b/lib/netstandard2.1/Voucherify.xml index 872b8003..ec9aac18 100644 --- a/lib/netstandard2.1/Voucherify.xml +++ b/lib/netstandard2.1/Voucherify.xml @@ -26260,6 +26260,24 @@ RestSharp's HttpMethod instance. + + + Starts a stopwatch when debug timing is enabled. + + Whether debug timing is enabled for the current request. + A started when enabled; otherwise null. + + + + Stops timing and writes a debug log entry for a request stage. + + Whether debug timing is enabled for the current request. + The stopwatch instance started for the stage. + The request stage label (for example: construction or execution). + HTTP method name. + Request resource path. + Optional response used to enrich debug metadata. + Provides all logic for constructing a new RestSharp . @@ -26844,6 +26862,11 @@ Determine whether or not the "default credentials" (e.g. the user account under which the current process is running) will be sent along to the server. The default is false. + + + Determines whether request timing diagnostics are enabled. + + Gets or sets the default header. @@ -27053,6 +27076,12 @@ Api Key name. Api Key value. + + + Checks whether debug mode is enabled via the DEBUG environment variable. + + true when DEBUG equals "true" (case-insensitive); otherwise, false. + Merge configurations. @@ -27341,6 +27370,11 @@ Determine whether or not the "default credentials" (e.g. the user account under which the current process is running) will be sent along to the server. The default is false. + + + Determines whether request timing diagnostics are enabled. + + Get the servers associated with the operation. diff --git a/readme.md b/readme.md index 2c3298b2..b748ce53 100644 --- a/readme.md +++ b/readme.md @@ -90,6 +90,15 @@ webProxy.Credentials = System.Net.CredentialCache.DefaultCredentials; c.Proxy = webProxy; ``` +### ๐Ÿงช Request build timing (DEBUG) + +You can enable debug mode: + +- set `DEBUG=true` in environment variables (`.env`), or +- set it directly in code with `config.DebugModeEnabled = true`. + +When enabled, the SDK prints request duration in milliseconds to console. + ## ๐Ÿš€ Run code Once installed, run: @@ -163,7 +172,7 @@ This SDK is auto-generated (except for tests), so changes made here will be over ## ๐Ÿท๏ธ Link tags -[OpenAPI generated from tag](https://github.com/voucherifyio/voucherify-openapi/releases/tag/sdk-dotnet-9.0.0). +[OpenAPI generated from tag](https://github.com/voucherifyio/voucherify-openapi/releases/tag/sdk-dotnet-9.0.1). ## ๐Ÿ” Authorization @@ -255,7 +264,8 @@ Authorization schemes defined for the API. - `templates`: Gives access to all endpoints and methods starting with `/v1/templates`. ## ๐Ÿ“… Changelog - +- **2026-02-27** - `9.0.1` + - Added debug mode to measure request timings - **2025-12-14** - `9.0.0` - All POST requests now require a payload. As a result, the order of parameters in API method calls may have changed. If you encounter a build error, please check the documentation for the method you are calling. - `OrdersImportCreateRequestBodyItem` no longer has `CreatedAt` property. diff --git a/src/Voucherify.Test/.env.example b/src/Voucherify.Test/.env.example index 4be0f31f..da8836b4 100644 --- a/src/Voucherify.Test/.env.example +++ b/src/Voucherify.Test/.env.example @@ -4,3 +4,4 @@ X_APP_TOKEN= X_MANAGEMENT_ID= X_MANAGEMENT_TOKEN= PROJECT_ID= +DEBUG=true \ No newline at end of file diff --git a/src/Voucherify/Client/ApiClient.cs b/src/Voucherify/Client/ApiClient.cs index 31afe7f7..3e578966 100644 --- a/src/Voucherify/Client/ApiClient.cs +++ b/src/Voucherify/Client/ApiClient.cs @@ -12,6 +12,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -262,6 +263,46 @@ private RestSharpMethod Method(HttpMethod method) return other; } + /// + /// Starts a stopwatch when debug timing is enabled. + /// + /// Whether debug timing is enabled for the current request. + /// A started when enabled; otherwise null. + private static Stopwatch StartTiming(bool debugModeEnabled = false) + { + return debugModeEnabled ? Stopwatch.StartNew() : null; + } + + /// + /// Stops timing and writes a debug log entry for a request stage. + /// + /// Whether debug timing is enabled for the current request. + /// The stopwatch instance started for the stage. + /// The request stage label (for example: construction or execution). + /// HTTP method name. + /// Request resource path. + /// Optional response used to enrich debug metadata. + private static void StopTiming(bool debugModeEnabled, Stopwatch stopwatch, string stage, string method, string resource, RestResponse response = null) + { + if (!debugModeEnabled || stopwatch == null) + { + return; + } + + stopwatch.Stop(); + double elapsedMilliseconds = stopwatch.Elapsed.TotalMilliseconds; + + string responseMeta = string.Empty; + if (response != null) + { + int payloadSize = response.RawBytes?.Length ?? 0; + + responseMeta = $" status={(int)response.StatusCode} response body size={payloadSize}B"; + } + + Console.WriteLine($"[Voucherify SDK][DEBUG][{DateTimeOffset.UtcNow:yyyy-MM-dd HH:mm:ss.fff} UTC] {stage} {method} {resource}{responseMeta} took {elapsedMilliseconds:F2}ms"); + } + /// /// Provides all logic for constructing a new RestSharp . /// At this point, all information for querying the service is known. @@ -283,6 +324,9 @@ private RestRequest NewRequest( if (path == null) throw new ArgumentNullException("path"); if (options == null) throw new ArgumentNullException("options"); if (configuration == null) throw new ArgumentNullException("configuration"); + + bool debugModeEnabled = configuration.DebugModeEnabled; + Stopwatch stopwatch = StartTiming(debugModeEnabled); RestRequest request = new RestRequest(path, Method(method)); @@ -394,6 +438,8 @@ private RestRequest NewRequest( } } + StopTiming(debugModeEnabled, stopwatch, "Request construction", method.ToString(), path); + return request; } @@ -558,11 +604,17 @@ private async Task> ExecClientAsync(Func> DeserializeRestResponseFromPolicyAsync(RestClient client, RestRequest request, PolicyResult policyResult, CancellationToken cancellationToken = default) + private async Task> DeserializeRestResponseFromPolicyAsync(RestClient client, RestRequest request, PolicyResult policyResult, bool debugModeEnabled = false, CancellationToken cancellationToken = default) { if (policyResult.Outcome == OutcomeType.Successful) { - return await client.Deserialize(policyResult.Result, cancellationToken); + Stopwatch watch = StartTiming(debugModeEnabled); + + var deserializeResult = await client.Deserialize(policyResult.Result, cancellationToken); + + StopTiming(debugModeEnabled, watch, "Response parse", request.Method.ToString(), request.Resource, deserializeResult); + + return deserializeResult; } else { @@ -572,7 +624,7 @@ private async Task> DeserializeRestResponseFromPolicyAsync(Re }; } } - + private ApiResponse Exec(RestRequest request, RequestOptions options, IReadableConfiguration configuration) { Action setOptions = (clientOptions) => @@ -594,12 +646,23 @@ private ApiResponse Exec(RestRequest request, RequestOptions options, IRea if (RetryConfiguration.RetryPolicy != null) { var policy = RetryConfiguration.RetryPolicy; + + bool debugModeEnabled = configuration?.DebugModeEnabled ?? false; + Stopwatch watch = StartTiming(debugModeEnabled); var policyResult = policy.ExecuteAndCapture(() => client.Execute(request)); - return DeserializeRestResponseFromPolicyAsync(client, request, policyResult); + StopTiming(debugModeEnabled, watch, "HTTP send", request.Method.ToString(), request.Resource, policyResult.Result); + return DeserializeRestResponseFromPolicyAsync(client, request, policyResult, debugModeEnabled); } else { - return Task.FromResult(client.Execute(request)); + bool debugModeEnabled = configuration.DebugModeEnabled; + Stopwatch watch = StartTiming(debugModeEnabled); + + var executeResult = client.Execute(request); + + StopTiming(debugModeEnabled, watch, "HTTP end-to-end (send + parse)", request.Method.ToString(), request.Resource, executeResult); + + return Task.FromResult(executeResult); } }; @@ -615,15 +678,23 @@ private ApiResponse Exec(RestRequest request, RequestOptions options, IRea Func>> getResponse = async (client) => { + bool debugEnabled = configuration.DebugModeEnabled; if (RetryConfiguration.AsyncRetryPolicy != null) { var policy = RetryConfiguration.AsyncRetryPolicy; + Stopwatch networkStopwatch = StartTiming(debugEnabled); var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false); - return await DeserializeRestResponseFromPolicyAsync(client, request, policyResult, cancellationToken); + StopTiming(debugEnabled, networkStopwatch, "HTTP send", request.Method.ToString(), request.Resource, policyResult.Result); + + return await DeserializeRestResponseFromPolicyAsync(client, request, policyResult, debugEnabled, cancellationToken); } else { - return await client.ExecuteAsync(request, cancellationToken).ConfigureAwait(false); + Stopwatch networkStopwatch = StartTiming(debugEnabled); + var rawResponse = await client.ExecuteAsync(request, cancellationToken).ConfigureAwait(false); + StopTiming(debugEnabled, networkStopwatch, "HTTP end-to-end (send + parse)", request.Method.ToString(), request.Resource, rawResponse); + + return rawResponse; } }; diff --git a/src/Voucherify/Client/Configuration.cs b/src/Voucherify/Client/Configuration.cs index 3ed8c379..a1f255e9 100644 --- a/src/Voucherify/Client/Configuration.cs +++ b/src/Voucherify/Client/Configuration.cs @@ -35,7 +35,7 @@ public class Configuration : IReadableConfiguration /// Version of the package. /// /// Version of the package. - public const string Version = "9.0.0"; + public const string Version = "9.0.1"; /// /// Identifier for ISO 8601 DateTime Format @@ -114,7 +114,7 @@ public class Configuration : IReadableConfiguration public Configuration() { Proxy = null; - UserAgent = WebUtility.UrlEncode("DOTNET-SDK-9.0.0"); + UserAgent = WebUtility.UrlEncode("DOTNET-SDK-9.0.1"); BasePath = "https://api.voucherify.io"; DefaultHeaders = new ConcurrentDictionary(); ApiKey = new ConcurrentDictionary(); @@ -151,6 +151,7 @@ public Configuration() OperationServers = new Dictionary>>() { }; + DebugModeEnabled = IsDebugModeEnabledInEnvironment(); // Setting Timeout has side effects (forces ApiClient creation). Timeout = TimeSpan.FromSeconds(100); @@ -215,6 +216,11 @@ public virtual bool UseDefaultCredentials set { _useDefaultCredentials = value; } } + /// + /// Determines whether request timing diagnostics are enabled. + /// + public virtual bool DebugModeEnabled { get; set; } + /// /// Gets or sets the default header. /// @@ -587,7 +593,7 @@ public static string ToDebugReport() report += " OS: " + System.Environment.OSVersion + "\n"; report += " .NET Framework Version: " + System.Environment.Version + "\n"; report += " Version of the API: v2018-08-01\n"; - report += " SDK Package Version: 9.0.0\n"; + report += " SDK Package Version: 9.0.1\n"; return report; } @@ -613,6 +619,16 @@ public void AddApiKeyPrefix(string key, string value) ApiKeyPrefix[key] = value; } + /// + /// Checks whether debug mode is enabled via the DEBUG environment variable. + /// + /// true when DEBUG equals "true" (case-insensitive); otherwise, false. + private static bool IsDebugModeEnabledInEnvironment() + { + string debugValue = Environment.GetEnvironmentVariable("DEBUG"); + return string.Equals(debugValue, "true", StringComparison.OrdinalIgnoreCase); + } + #endregion Methods #region Static Members @@ -655,6 +671,7 @@ public static IReadableConfiguration MergeConfigurations(IReadableConfiguration DateTimeFormat = second.DateTimeFormat ?? first.DateTimeFormat, ClientCertificates = second.ClientCertificates ?? first.ClientCertificates, UseDefaultCredentials = second.UseDefaultCredentials, + DebugModeEnabled = second.DebugModeEnabled || first.DebugModeEnabled, RemoteCertificateValidationCallback = second.RemoteCertificateValidationCallback ?? first.RemoteCertificateValidationCallback, }; return config; diff --git a/src/Voucherify/Client/IReadableConfiguration.cs b/src/Voucherify/Client/IReadableConfiguration.cs index 5fca5e56..05d0473a 100644 --- a/src/Voucherify/Client/IReadableConfiguration.cs +++ b/src/Voucherify/Client/IReadableConfiguration.cs @@ -137,6 +137,11 @@ public interface IReadableConfiguration /// bool UseDefaultCredentials { get; } + /// + /// Determines whether request timing diagnostics are enabled. + /// + bool DebugModeEnabled { get; } + /// /// Get the servers associated with the operation. /// diff --git a/src/Voucherify/Voucherify.csproj b/src/Voucherify/Voucherify.csproj index 9b4c7efb..707f82ee 100644 --- a/src/Voucherify/Voucherify.csproj +++ b/src/Voucherify/Voucherify.csproj @@ -12,7 +12,7 @@ A library generated from a OpenAPI doc MIT Voucherify - 9.0.0 + 9.0.1 bin\$(Configuration)\$(TargetFramework)\Voucherify.xml https://github.com/voucherifyio/voucherify-dotNET-sdk git diff --git a/src/Voucherify/Voucherify.nuspec b/src/Voucherify/Voucherify.nuspec index 76eb3c33..f3364b54 100644 --- a/src/Voucherify/Voucherify.nuspec +++ b/src/Voucherify/Voucherify.nuspec @@ -9,11 +9,13 @@ MIT https://github.com/voucherifyio/voucherify-dotNET-sdk false - 9.0.0 + 9.0.1 .Net SDK for Voucherify - coupons, vouchers, promo codes - http://www.voucherify.io MIT + 2026-02-27: 9.0.1 + - Added debug mode to measure request timings 2025-12-14: 9.0.0 - All POST requests now require a payload. As a result, the order of parameters in API method calls may have changed. If you encounter a build error, please check the documentation for the method you are calling. โ€” `OrdersImportCreateRequestBodyItem` no longer has `CreatedAt` property.