Skip to content

Fix/yurii bart fix mark as departed error string issue#94

Open
r4m-semeyon wants to merge 8 commits intomasterfrom
fix/yurii-bart-fix-mark-as-departed-error-string-issue
Open

Fix/yurii bart fix mark as departed error string issue#94
r4m-semeyon wants to merge 8 commits intomasterfrom
fix/yurii-bart-fix-mark-as-departed-error-string-issue

Conversation

@r4m-semeyon
Copy link
Copy Markdown
Contributor

No description provided.

@github-actions
Copy link
Copy Markdown

Gemini AI Code Risk & Compatibility Analysis

Status: [OK] Analysis completed successfully

Files analyzed:

  • route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManager.cs
  • route4me-csharp-sdk/Route4MeSDKUnitTest/Tests/AddressesGroupTests.cs

Analysis Results

Based on my analysis of the provided code changes, here is a summary of potential risks and improvement suggestions.

Executive Summary

The most critical issue identified is a High severity backward compatibility risk in Route4MeManager.cs. The change in error handling for HTTP GET requests, from throwing an exception to returning a null/default value with an error message, is a significant behavioral change that could break existing client implementations. Additionally, a Medium severity performance risk exists due to the use of .Wait() and .Result on async tasks, which can cause deadlocks in certain application types. The test file changes are positive, improving coverage for the new error handling behavior.


File: route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManager.cs

‣ Potential Backward Compatibility Risks

  • Severity: High
  • Issue: The error handling for HTTP GET requests has been fundamentally changed.
    • Previous behavior: The code used HttpClient.GetStreamAsync(), which would throw a HttpRequestException for non-successful HTTP status codes (e.g., 404, 500).
    • New behavior: The code now uses HttpClient.GetAsync(), checks the IsSuccessStatusCode property, and if the status code is not successful, it no longer throws an exception. Instead, it returns default(T) (e.g., null) and populates an errorMessage string.
  • Risk: This is a significant breaking change. Clients of the SDK that have try-catch blocks expecting a HttpRequestException for failed GET requests will no longer see those exceptions. Their error handling logic will fail, and they may incorrectly interpret the returned null/default value as a valid successful response.
  • Suggestion: This change should be documented as a major breaking change in the release notes. To mitigate the impact, consider introducing a new method with the new behavior or adding a configuration flag to the Route4MeManager to allow users to opt into the new error handling, preserving the old exception-throwing behavior by default for a transitional period.

‣ Performance Risks & Coding Standard Violations

  • Severity: Medium

  • Issue: The synchronous Run<T> method (the second changed block) uses .Wait() and .Result to block on asynchronous operations (GetAsync, ReadAsStreamAsync). This is a common anti-pattern known as "sync-over-async".

  • Risk: This can lead to thread pool starvation and, more critically, deadlocks in applications with a synchronization context (like ASP.NET Classic or UI frameworks).

  • Suggestion: Avoid blocking on async calls with .Wait() or .Result. The best practice is to make the entire call chain asynchronous (i.e., use async and await all the way up). If a synchronous public method must be preserved for backward compatibility, investigate using a different, truly synchronous mechanism if available, or clearly document the risks of deadlocks to consumers.

  • Severity: Low

  • Issue: The code uses a broad catch (Exception) block when attempting to parse the error response.

  • Risk: This can catch and hide unexpected exceptions (e.g., OutOfMemoryException), making debugging more difficult. While it currently handles the case where the response isn't a valid ErrorResponse, it could be more specific.

  • Suggestion: If possible, catch more specific exceptions related to deserialization, such as JsonSerializationException from Newtonsoft.Json, to be more precise about the expected errors.


File: route4me-csharp-sdk/Route4MeSDKUnitTest/Tests/AddressesGroupTests.cs

‣ General Observations

  • Severity: Low / Informational
  • Observation: The changes in this file are for unit tests. They do not pose a risk to the production code but are a reaction to the changes in the application logic.
    • The test MarkAddressDepartedTest has been improved to test a more complete and realistic workflow (visit then depart).
    • The new test MarkAddressDepartedWithoutVisitingReturnsError is a valuable addition. It specifically verifies that the API correctly returns an error when business logic is violated (departing before visiting). This test confirms that the new error handling mechanism in Route4MeManager.cs is correctly capturing and reporting API-level errors as intended.
  • Suggestion: No improvements are needed; these changes enhance the quality and coverage of the test suite.

@github-actions
Copy link
Copy Markdown

Gemini AI Code Risk & Compatibility Analysis

Status: [OK] Analysis completed successfully

Files analyzed:

  • route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManager.cs
  • route4me-csharp-sdk/Route4MeSDKUnitTest/Tests/AddressesGroupTests.cs

Analysis Results

Based on the provided code changes, here is an analysis of potential risks and suggestions for improvement.

File: route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManager.cs

Potential Risks

  • Backward Compatibility Risk (High)

    • Description: The method of handling unsuccessful HTTP GET requests has fundamentally changed. Previously, GetStreamAsync would throw an HttpRequestException on a non-success status code (e.g., 404 Not Found, 500 Internal Server Error). The new code uses GetAsync, which does not throw an exception by default. Instead, it checks response.IsSuccessStatusCode and, upon failure, reads the response body into an errorMessage string.
    • Impact: Existing clients of the SDK that use try-catch blocks to handle request failures will no longer catch these exceptions. Their error handling logic will break, and they might incorrectly process a null or default return value as a successful API call. This is a significant breaking change in the public behavior of the SDK.
    • Severity: High
  • Performance Risk (Medium)

    • Description: The synchronous version of the Get method uses .Result on asynchronous tasks (GetAsync(...).Result and ReadAsStreamAsync().Result). Calling .Result or .Wait() on an async method can lead to thread pool starvation and deadlocks, especially in applications with a synchronization context (like classic ASP.NET or UI applications).
    • Impact: Consumers of the SDK in certain environments may experience application freezes or deadlocks that are difficult to debug. While the previous code used .Wait(), which has the same risk, the issue remains unaddressed.
    • Severity: Medium

Improvement Suggestions

  • Address the Breaking Change: This change in error handling should be treated as a major version bump for the SDK. It must be clearly and prominently documented in the release notes so that all users understand they need to update their error-handling logic from try-catch to checking the returned errorMessage string.
  • Improve Sync-over-Async Calls: To mitigate deadlock risks, avoid using .Result or .Wait(). If a synchronous method is required, consider using a fully synchronous HTTP client for that path or a more robust async-to-sync bridge.
  • Resource Management: The addition of response.Dispose() is a good practice and correctly ensures that the HttpResponseMessage and its associated resources are released.

File: route4me-csharp-sdk/Route4MeSDKUnitTest/Tests/AddressesGroupTests.cs

Analysis

  • No Risks Identified: The changes in this file are confined to unit tests and represent a significant improvement in test quality and coverage.
    • The MarkAddressDepartedTest was corrected to follow the logical sequence of first marking an address as visited and then as departed. It also appears to call the correct MarkAddressDeparted method, suggesting the original test was bugged.
    • A new test, MarkAddressDepartedWithoutVisitingReturnsError, has been added. This is an excellent addition as it verifies a specific business rule (an address cannot be departed before being visited) and confirms that the API provides a proper error message on failure, aligning with the error handling changes in Route4MeManager.cs.

Improvement Suggestions

  • No improvements are needed for the test file; the changes are positive.

yurii-bart and others added 3 commits January 22, 2026 11:42
…eparted-error-string-issue

# Conflicts:
#	route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeConfig.cs
…r-string-issue

Fix/fix mark as departed error string issue
@github-actions
Copy link
Copy Markdown

Gemini AI Code Risk & Compatibility Analysis

Status: [OK] Analysis completed successfully

Files analyzed:

  • route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeConfig.cs
  • route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManager.cs
  • route4me-csharp-sdk/Route4MeSDKTest/Examples/Configurations/TimeoutConfigurationExample.cs
  • route4me-csharp-sdk/Route4MeSDKUnitTest/Tests/AddressesGroupTests.cs

Analysis Results

(node:2272) MaxListenersExceededWarning: Possible EventTarget memory leak detected. 11 abort listeners added to [AbortSignal]. MaxListeners is 10. Use events.setMaxListeners() to increase limit
(Use node --trace-warnings ... to show where the warning was created)
Here is an analysis of the provided code changes.

Summary

The changes introduce an opt-in feature for improved API error handling, which is a valuable addition. The asynchronous implementation is well-done, but the synchronous version introduces significant performance and reliability risks by blocking on async calls (sync-over-async), which can cause deadlocks. The error parsing logic in the new synchronous method is also overly complex and fragile. While the change is backward-compatible by default, enabling the new feature exposes these risks.


High Severity Risks

  • Performance / Reliability: Sync-over-Async Implementation
    In Route4MeManager.cs, the new method GetJsonObjectFromAPIWithImprovedErrorHandling uses blocking calls like .Result and .Wait() on asynchronous tasks (e.g., httpClient.GetAsync(...).Result, response.Wait()). This is a dangerous practice known as "sync-over-async" which can lead to thread pool starvation and application deadlocks, especially in UI or ASP.NET applications.

    • File: route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManager.cs
    • Lines: 6997, 7051, 7059, 7066, etc.
    • Suggestion: The synchronous method should be rewritten to use a synchronous HTTP client pattern if one is required. However, the best practice is to use the async and await pattern throughout the call stack and expose only the asynchronous public methods. If a synchronous wrapper is absolutely necessary for backward compatibility, it should be implemented with extreme care to avoid deadlocks.

Medium Severity Risks

  • Coding Standard Violation: Fragile Type Checking
    The error handling logic in GetJsonObjectFromAPIWithImprovedErrorHandling checks the type of the response content by converting its name to a lower-case string (response.Result.Content.GetType().ToString().ToLower().Contains("httpconnectionresponsecontent")). This is very brittle and can break if the internal implementation of the .NET HTTP client changes in future versions.

    • File: route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManager.cs
    • Line: 7076
    • Suggestion: Refactor this to use type casting with the as operator (e.g., if (response.Result.Content is StreamContent streamContent)) or pattern matching, which is safer and more idiomatic C#.
  • Performance: Inefficient Reflection on Hot Path
    On every API call, the code uses reflection (.GetType().GetProperties().FirstOrDefault(...)) to check if an API key is present in the parameters object. Reflection is computationally expensive, and using it in a method that makes network calls can add unnecessary overhead.

    • File: route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManager.cs
    • Lines: 6979-6983
    • Suggestion: Modify the GenericParameters base class or an interface to include a property or method for providing the API key. This would allow for a strongly-typed, reflection-free check.

Low Severity Risks

  • Coding Standard Violation: Manual Dispose() Call
    The HttpResponseMessage object is disposed of by manually calling response.Dispose(). While correct in this instance, it's less robust than using a using block. A using statement ensures that Dispose() is called even if an exception occurs within the block.

    • File: route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManager.cs
    • Lines: 6799, 6857, 7011
    • Suggestion: Wrap the HttpResponseMessage instance in a using statement to guarantee proper disposal.
      using (var response = await httpClientHolder.HttpClient.GetAsync(uri.PathAndQuery).ConfigureAwait(false))
      {
          if (response.IsSuccessStatusCode)
          {
              // ...
          }
          else
          {
              // ...
          }
      }

Backward Compatibility

  • No Risks Detected.
    The new error handling feature is controlled by the static flag Route4MeConfig.UseImprovedErrorHandling, which is false by default. This makes the feature opt-in and ensures that existing client code will not be affected, preserving backward compatibility. This is an excellent approach for introducing significant behavioral changes.

@r4m-semeyon r4m-semeyon requested a review from h2rd January 22, 2026 12:47
@github-actions
Copy link
Copy Markdown

Gemini AI Code Risk & Compatibility Analysis

Status: [OK] Analysis completed successfully

Files analyzed:

  • route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeConfig.cs
  • route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManager.cs
  • route4me-csharp-sdk/Route4MeSDKTest/Examples/Configurations/TimeoutConfigurationExample.cs
  • route4me-csharp-sdk/Route4MeSDKUnitTest/Tests/AddressesGroupTests.cs

Analysis Results

(node:2267) MaxListenersExceededWarning: Possible EventTarget memory leak detected. 11 abort listeners added to [AbortSignal]. MaxListeners is 10. Use events.setMaxListeners() to increase limit
(Use node --trace-warnings ... to show where the warning was created)
Here is an analysis of the provided code changes.

Summary

The changes introduce an opt-in, improved error handling mechanism for API calls, which is a valuable feature for developers. However, the synchronous implementation of this new mechanism introduces a high-risk performance issue (sync-over-async) and a medium-risk resource leak. The asynchronous implementation is better but could also be made more robust with improved resource management.


1. Performance Risks

  • Severity: High
  • File: route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManager.cs
  • Method: GetJsonObjectFromAPIWithImprovedErrorHandling<T>

Risk:
The new synchronous method GetJsonObjectFromAPIWithImprovedErrorHandling uses blocking calls (.Result and .Wait()) on asynchronous Task objects (e.g., GetAsync(...).Result, response.Wait()). This is a "sync-over-async" anti-pattern that can lead to deadlocks and thread pool starvation, especially in UI or ASP.NET applications. It severely impacts performance and scalability by tying up threads that could be doing other work.

Lines of Concern:

// Potential deadlock/blocking
var response = acquireHttpClientHolder.HttpClient.GetAsync(uri.PathAndQuery).Result; 

// ...

// Blocking call
response.Wait(); 

// ...

// Blocking call
var streamTask = response.Result.Content.ReadAsStreamAsync();
streamTask.Wait();

Suggestion:
The synchronous wrapper should be re-implemented to avoid blocking on async methods. If a synchronous public API is absolutely required, consider using a more robust synchronization mechanism. However, the best practice is to encourage consumers of the library to use the asynchronous (...Async) methods throughout their call stack.


2. Coding Standard Violations & Potential Bugs

  • Severity: Medium
  • File: route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManager.cs
  • Methods: GetJsonObjectFromAPIWithImprovedErrorHandling<T>, GetJsonObjectFromAPIWithImprovedErrorHandlingAsync<T>

Violation 1: Potential Resource Leak
In the synchronous GetJsonObjectFromAPIWithImprovedErrorHandling method, the HttpResponseMessage object (response.Result) is not disposed of in the POST, PUT, or DELETE cases. This will lead to a resource leak, as the underlying network connection may not be released properly.

Violation 2: Manual Disposal is Not Robust
In both new methods, response.Dispose() is called manually at the end of a logic block. The idiomatic and safer way to handle IDisposable objects like HttpResponseMessage is with a using statement. This guarantees that .Dispose() is called even if an exception occurs during stream reading or processing.

Lines of Concern:

// In GetJsonObjectFromAPIWithImprovedErrorHandlingAsync
// This should be wrapped in a 'using' block
var response = await httpClientHolder.HttpClient.GetAsync(uri.PathAndQuery).ConfigureAwait(false);
// ...
response.Dispose(); // Manual disposal is not guaranteed if an exception occurs before this line

// In GetJsonObjectFromAPIWithImprovedErrorHandling (POST/PUT/DELETE case)
// The HttpResponseMessage in 'response.Result' is never disposed.
response.Wait();

Suggestion:
Wrap all HttpResponseMessage objects in using blocks to ensure they are always disposed of correctly.

Example Improvement:

// For the async GET case
using (var response = await httpClientHolder.HttpClient.GetAsync(uri.PathAndQuery).ConfigureAwait(false))
{
    if (response.IsSuccessStatusCode)
    {
        var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
        result = isString
            ? stream.ReadString() as T
            : (parseWithNewtonJson ? stream.ReadObjectNew<T>() : stream.ReadObject<T>());
    }
    else
    {
        errorMessage = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
    }
} // response.Dispose() is called automatically here

3. Backward Compatibility Risks

  • Severity: Low
  • File: route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeConfig.cs

Risk:
A new public static property UseImprovedErrorHandling has been added. While this is an opt-in feature and defaults to false to maintain existing behavior, it introduces a global static flag. Global state can sometimes lead to unexpected behavior in complex applications or multi-threaded environments where different parts of the application might expect different settings.

Suggestion:
The current implementation is acceptable for controlling a feature globally. An alternative for future consideration could be passing configuration settings through the Route4MeManager instance itself, allowing for different instances to have different behaviors if needed. For the current change, the risk is low as the default behavior is preserved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants