Skip to content
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

feat: client identification headers #224

Merged
merged 3 commits into from
Jan 21, 2025
Merged

Conversation

kwasniew
Copy link
Contributor

@kwasniew kwasniew commented Jan 20, 2025

About the changes

This PR adds standardised client identification headers to the feature and metrics calls that the client makes to Unleash. The headers are:

  • x-unleash-appname: the name of the application that is using the client. UNLEASH-APPNAME will be deleted in another PR (expand/contract pattern)
  • x-unleash-connection-id: an internal unique identifier for the current instance of the client generated by the built-in crypto lib
  • x-unleash-sdk: sdk information in the format unleash-ruby@<version> following reference unleash-node@<version> naming pattern

All the headers are intended for the Unleash team so clients should not be affected.
The main use cases we have are:

  • capacity planning by knowing the number of unique connections made to the backend API
  • debugging misconfigured SDKs sending more traffic than expected

Reference implementation: Unleash/unleash-client-node#690

Important files

Discussion points

@kwasniew kwasniew requested review from sighphyre and Tymek January 20, 2025 11:40
@@ -54,6 +54,9 @@ def http_headers
'User-Agent' => "UnleashClientRuby/#{Unleash::VERSION} #{RUBY_ENGINE}/#{RUBY_VERSION} [#{RUBY_PLATFORM}]",
'UNLEASH-INSTANCEID' => self.instance_id,
'UNLEASH-APPNAME' => self.app_name,
'X-UNLEASH-APPNAME' => self.app_name,
'X-UNLEASH-CONNECTION-ID' => @connection_id,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we want this one to be private internal detail of each SDK

@@ -54,6 +54,9 @@ def http_headers
'User-Agent' => "UnleashClientRuby/#{Unleash::VERSION} #{RUBY_ENGINE}/#{RUBY_VERSION} [#{RUBY_PLATFORM}]",
'UNLEASH-INSTANCEID' => self.instance_id,
'UNLEASH-APPNAME' => self.app_name,
'X-UNLEASH-APPNAME' => self.app_name,
'X-UNLEASH-CONNECTION-ID' => @connection_id,
'X-UNLEASH-SDK' => "unleash-ruby@#{Unleash::VERSION}",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

common naming convention across SDKs

@@ -54,6 +54,9 @@ def http_headers
'User-Agent' => "UnleashClientRuby/#{Unleash::VERSION} #{RUBY_ENGINE}/#{RUBY_VERSION} [#{RUBY_PLATFORM}]",
'UNLEASH-INSTANCEID' => self.instance_id,
'UNLEASH-APPNAME' => self.app_name,
'X-UNLEASH-APPNAME' => self.app_name,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this will replace UNLEASH-APPNAME over time

RSpec.describe Unleash::Client do
after do
WebMock.reset!
File.delete(Unleash.configuration.backup_file) if File.exist?(Unleash.configuration.backup_file)
end

it "Uses custom http headers when initializing client" do
fixed_uuid = "123e4567-e89b-12d3-a456-426614174000"
allow(SecureRandom).to receive(:uuid).and_return(fixed_uuid)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

since we don't expose connection id we need to mock the generation

@coveralls
Copy link

coveralls commented Jan 20, 2025

Pull Request Test Coverage Report for Build 12867886511

Details

  • 1 of 1 (100.0%) changed or added relevant line in 1 file are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.01%) to 95.385%

Totals Coverage Status
Change from base Build 12670653058: 0.01%
Covered Lines: 434
Relevant Lines: 455

💛 - Coveralls

@kwasniew kwasniew requested a review from rarruda January 20, 2025 11:43
Copy link
Member

@sighphyre sighphyre left a comment

Choose a reason for hiding this comment

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

Yep, seems reasonable to me!

@@ -87,6 +90,7 @@ def set_defaults
self.environment = 'default'
self.url = nil
self.instance_id = SecureRandom.uuid
@connection_id = SecureRandom.uuid
Copy link
Member

Choose a reason for hiding this comment

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

This is the dumbest comment I've ever left on a PR ever but the change in syntax here is a bit jarring. Any chance we can move this line either to the top of the bottom of this function?

Honestly I won't even be offended if you just ignore me

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It actually makes perfect sense to me. Had the same thought :) Moving it to the bottom

Copy link
Member

Choose a reason for hiding this comment

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

Thank you! I appreciate you haha

Copy link
Collaborator

@rarruda rarruda left a comment

Choose a reason for hiding this comment

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

Mostly bike shedding, but since you ask:

  1. Any reasons why you use uppercase for the headers here in the Ruby SDK but lowercase in the node SDK? (IIRC the mocks ignore the case in the headers, but that doesn't mean that we should have them different across different SDKs, if the point is to standardize)
  2. Any links to any more formal docs/specs? I think a link to the PR in the node SDK is ok too, but would be even better if there was a link to something a tiny bit more formal.
  3. In an ideal world the commit message would include:
  • the migration path between the headers (which header is going to be replacing which),
  • the rationale ("standardization across SDKs" should suffice, but "client identification headers" is a bit on the opaque side for my taste).
  • Ideally also add a link/hook to point 2. (either which commit in the node SDK is used as a base, or a link to the docs/spec).

But the code itself is quite sane and i enjoy nit-picking. So if you feel like merging as is, I don't frown for a moment either.

@kwasniew
Copy link
Contributor Author

@rarruda Thanks so much for taking the time to review this PR.
To answer your comments:

  1. we keep the existing convention in this SDK (UNLEASH-INSTANCEID and UNLEASH-APPNAME). Since HTTP headers are case insensitive we're safe here no only in tests but also in the production setup. I noticed that some SDKs like Java use upper case while some others like Node use lower-case. If we decide to unify the casing I'd like to do it in another PR.
  2. Since this is internal change not affecting clients of the SDK (only Unleash core team and SDK maintainers) I linked to the reference implementation only. However I'm enriching the PR description based on feedback gathered so far. At some point we may introduce executable spec for the HTTP contract as we have for the flag evaluations and that would be the ideal case.
  3. I expanded the PR description with the use case description and the explanation that users of the SDK are not affected by this change.

@kwasniew kwasniew merged commit da097f4 into main Jan 21, 2025
43 checks passed
@kwasniew kwasniew deleted the client-identification-headers branch January 21, 2025 09:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

4 participants