Skip to content

Add Json Payload Functionality for User Agent Feature Extension #3489

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

samsharma2700
Copy link
Contributor

Description

Part 2 of UserAgent work. Previous PR: #3451

Testing

Builds are running and added unit tests to verify changes.

@Copilot Copilot AI review requested due to automatic review settings July 17, 2025 20:41
@samsharma2700 samsharma2700 requested a review from a team as a code owner July 17, 2025 20:41
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements the JSON payload functionality for the User Agent feature extension in Microsoft.Data.SqlClient, adding the core infrastructure for generating and managing user agent information that will be sent to SQL Server during connection establishment.

  • Implements UserAgentInfo class with OS detection, architecture detection, and runtime information gathering
  • Adds UserAgentInfoDto as a serializable data transfer object with size constraints and field-dropping logic
  • Includes comprehensive unit tests covering field truncation, payload sizing, and JSON serialization

Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
UserAgentInfo.cs (netfx/netcore) Core implementation with OS/runtime detection and JSON payload generation with size constraints
UserAgentInfoDto.cs (netfx/netcore) Data transfer object for JSON serialization with property name constants
UserAgentInfoTests.cs Unit tests covering truncation logic, payload sizing, and JSON contract validation
Microsoft.Data.SqlClient.csproj (netfx/netcore) Project file updates to include the new UserAgent classes
Microsoft.Data.SqlClient.UnitTests.csproj Test project configuration with empty folder reference
Comments suppressed due to low confidence (2)

Comment on lines 51 to 53
<ItemGroup>
<Folder Include="microsoft.data.sqlclient\tests\unittests\microsoft\data\sqlclient\" />
</ItemGroup>
Copy link
Preview

Copilot AI Jul 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This empty folder reference appears to be unnecessary and may have been added by mistake. Empty folder references in project files don't serve any functional purpose and should be removed to keep the project file clean.

Suggested change
<ItemGroup>
<Folder Include="microsoft.data.sqlclient\tests\unittests\microsoft\data\sqlclient\" />
</ItemGroup>

Copilot uses AI. Check for mistakes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UnitTests project doesn't explicitly specify any of the source files to compile. We let the SDK figure it out. This ItemGroup shouldn't be necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.Will update.

Comment on lines 195 to 196
// Last check to ensure we are within the limits(in case remaining fields are still too large)
return payload.Length > UserAgentPayloadMaxBytes
Copy link
Preview

Copilot AI Jul 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition check is inconsistent with the logic above. The method drops fields when payload exceeds UserAgentPayloadMaxBytes, but then checks the same condition again. This should check JsonPayloadMaxBytesSpec (2047) instead to ensure the final payload meets the server specification.

Suggested change
// Last check to ensure we are within the limits(in case remaining fields are still too large)
return payload.Length > UserAgentPayloadMaxBytes
// Last check to ensure we are within the limits (in case remaining fields are still too large)
return payload.Length > JsonPayloadMaxBytesSpec

Copilot uses AI. Check for mistakes.

@roji
Copy link
Member

roji commented Jul 17, 2025

@samsharma2700 @cheenamalhotra great to see this work happening. I've taken a look at the spec doc linked above, and could you clarify how we'd go about injecting EF information into the user agent? We'd need to identify EF Core (as opposed to direct users of SqlClient), it's version, etc.

@paulmedynski
Copy link
Contributor

Hi @roji - There is a V2 of the design that adds a public API for middleware like EF to pass version information, but we need to go through a security and privacy review before we can implement it. This V1 phase only includes values we can pull from the runtime that don't have any such concerns.

Copy link
Contributor

@paulmedynski paulmedynski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to move the UserAgent files into the src/ project and avoid duplicating them. I'll complete my review after the move.

Comment on lines 51 to 53
<ItemGroup>
<Folder Include="microsoft.data.sqlclient\tests\unittests\microsoft\data\sqlclient\" />
</ItemGroup>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UnitTests project doesn't explicitly specify any of the source files to compile. We let the SDK figure it out. This ItemGroup shouldn't be necessary.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new files should live in the src/ project, and shouldn't be duplicated in netcore and netfx.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! moved to src/

/// Gathers driver + environment info, enforces size constraints,
/// and serializes into a UTF-8 JSON payload.
/// </summary>
public static class UserAgentInfo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't part of our public API, so should be internal.

The UnitTests project has access to internals, so shouldn't have any effect on testing.

@roji
Copy link
Member

roji commented Jul 18, 2025

@paulmedynski ok - can you clarify what's planned for the 6.1.0 (v1, v2?)

@paulmedynski paulmedynski added this to the 7.0-preview1 milestone Jul 18, 2025
@paulmedynski
Copy link
Contributor

The V1 work will arrive in 7.0.0. The V2 work to support EF and other middleware may also make it into that timeframe, but depends on which reviews we need and how long they take. There is also ongoing SQL Server support that we need to align with to make any of this driver side work useful.

@samsharma2700 will be completing the V1 work over several PRs. We have (and will) put them into the 7.0.0-preview1 milestone.

Copy link

codecov bot commented Jul 18, 2025

Codecov Report

Attention: Patch coverage is 51.90840% with 63 lines in your changes missing coverage. Please review.

Project coverage is 66.89%. Comparing base (42146a3) to head (7b2ad0e).
Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
...icrosoft/Data/SqlClient/UserAgent/UserAgentInfo.cs 49.19% 63 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3489      +/-   ##
==========================================
- Coverage   68.85%   66.89%   -1.97%     
==========================================
  Files         277      279       +2     
  Lines       62237    62316      +79     
==========================================
- Hits        42854    41684    -1170     
- Misses      19383    20632    +1249     
Flag Coverage Δ
addons 91.04% <ø> (+0.22%) ⬆️
netcore 68.96% <50.38%> (-3.72%) ⬇️
netfx 69.34% <51.58%> (+1.15%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@roji
Copy link
Member

roji commented Jul 19, 2025

@paulmedynski thanks for the clarifications!

Copy link
Contributor

@paulmedynski paulmedynski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're on the right track here. We can discuss my comments and suggestions.

#if WINDOWS
using System.Management;
#endif

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#nullable enable

Add this to all new files, and we can remove it later when we enable nullable everywhere.

using System.Management;
#endif

namespace Microsoft.Data.SqlClient.UserAgent
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer namespace declaration:

namespace Microsoft.Data.SqlClient.UserAgent;

Then the entire definition isn't unnecessarily indented.

Copy link
Contributor Author

@samsharma2700 samsharma2700 Jul 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes agreed, the file-scoped namespace declarations do make the files cleaner. I assumed our project convention is to use traditional namespace declaration with a block, so continued with that.

/// Maximum number of bytes allowed for the user agent json payload.
/// payloads larger than this may be rejected by the server.
/// </summary>
public const int JsonPayloadMaxBytesSpec = 2047;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the significance of the Spec suffix here? I think JsonPayloadMaxBytes is accurate enough.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be public?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So Spec marks the specification defined limit for the payload, but we still send the payload if it is higher than that. We drop a few fields if payload is larger than UserAgentPayloadMaxBytes. Added a suffix to distinguish them easily. Made it public for easier testing but I'll hard code this value for now.

{
/// <summary>
/// Gathers driver + environment info, enforces size constraints,
/// and serializes into a UTF-8 JSON payload.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably worthwhile adding a link to the spec document here.


/// <summary>
/// Maximum number of bytes allowed for the user agent json payload.
/// payloads larger than this may be rejected by the server.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

payloads -> Payloads

/// <param name="jsonStringVal">The string value to truncate</param>
/// <param name="maxChars">Maximum number of characters allowed</param>
/// <returns>Truncated string or default value if input is invalid</returns>
private static string TruncateOrDefault(string jsonStringVal, int maxChars)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private -> internal will make it easier to test.

[InlineData(null, 5, "Unknown")] // null returns default
[InlineData("", 5, "Unknown")] // empty returns default
[InlineData("abc", 5, "abc")] // within limit unchanged
[InlineData("abcdef", 5, "abcde")] // overflow truncated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test exactly max chars in the input.

// Final payload must satisfy limits
Assert.InRange(payload.Length, 1, UserAgentInfo.UserAgentPayloadMaxBytes);

// Convert to string for field presence checks
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably better to parse as a JsonDocument and then explictily test the fields.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to also see tests that prove your trunctate, adjust, and serialize all works together. You could put all of that into an internal function that takes in the field values, and then you can check the output is as expected.

Assert.DoesNotContain(UserAgentInfoDto.OsInfo.DetailsJsonKey, json);
}

// 4. DTO serializes with expected JSON property names
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about testing that the values are serialized as expected as well?

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.

3 participants