Skip to content

feat: add service account support#152

Open
lajohn4747 wants to merge 17 commits into
masterfrom
johnla-multi-567-add-service-account-support-to-ruby-sdk
Open

feat: add service account support#152
lajohn4747 wants to merge 17 commits into
masterfrom
johnla-multi-567-add-service-account-support-to-ruby-sdk

Conversation

@lajohn4747

@lajohn4747 lajohn4747 commented Jun 25, 2026

Copy link
Copy Markdown

Summary

  • Service Account Credentials: New Mixpanel::ServiceAccountCredentials class for storing username, secret, and project_id
  • Import Support: Events#import now accepts service account credentials instead of API keys
  • Feature Flags Support: Both local and remote feature flags authenticate using service accounts
  • Readme.rdoc - Added usage examples
  • Comprehensive test coverage

@lajohn4747 lajohn4747 requested review from a team and rahul-mixpanel June 25, 2026 22:14
@linear-code

linear-code Bot commented Jun 25, 2026

Copy link
Copy Markdown

MULTI-567

@lajohn4747 lajohn4747 marked this pull request as draft June 25, 2026 22:15
@codecov

codecov Bot commented Jun 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.17%. Comparing base (3f9a8d4) to head (9f9ef92).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #152      +/-   ##
==========================================
+ Coverage   96.73%   97.17%   +0.44%     
==========================================
  Files          14       15       +1     
  Lines         673      707      +34     
==========================================
+ Hits          651      687      +36     
+ Misses         22       20       -2     
Flag Coverage Δ
openfeature 100.00% <ø> (ø)

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

☔ View full report in Codecov by Harness.
📢 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.

The as_json method alone is not sufficient for direct .to_json calls.
Ruby's JSON library requires an explicit to_json method to properly
serialize custom objects.
@lajohn4747 lajohn4747 changed the title Add service account support feat: add service account support Jun 26, 2026
Allow passing credentials directly to Tracker.new instead of requiring
them to be nested in the config hash. This provides a cleaner API:

Before:
  tracker = Tracker.new(token, nil,
    local_flags_config: { credentials: creds },
    remote_flags_config: { credentials: creds })

After:
  tracker = Tracker.new(token, nil,
    credentials: creds,
    local_flags_config: {},
    remote_flags_config: {})

Credentials in config still take precedence if both are provided,
allowing different credentials per provider if needed.
Credentials don't need to be part of the config hash - they're a
fundamental authentication parameter. This simplifies the API:

- Credentials are passed directly as a parameter to the flags providers
- No more confusing config[:credentials] nesting
- Config hash is only for provider-specific settings (api_host, timeouts, etc.)
- Cleaner separation of concerns

This removes 30+ lines of unnecessary complexity.
Update test setup to pass nil for credentials parameter in the
new 5-argument signature.
@lajohn4747 lajohn4747 marked this pull request as ready for review June 30, 2026 16:01
@lajohn4747 lajohn4747 requested review from efahk and tylerjroach June 30, 2026 16:01
@tylerjroach tylerjroach requested a review from Copilot June 30, 2026 16:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds first-class “service account” support across import and feature flags by introducing a credentials object, wiring it through the tracker/providers, and updating docs + tests to demonstrate and validate the new flow.

Changes:

  • Introduce Mixpanel::ServiceAccountCredentials and serialize it into import messages.
  • Update import pipeline (Events#importConsumer#send!) to send either legacy api_key or service account fields.
  • Thread optional credentials into local/remote flags providers and use them for HTTP Basic Auth, plus README usage examples and new/updated specs.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
spec/mixpanel-ruby/tracker_spec.rb Adds spec asserting credentials are passed into flags providers.
spec/mixpanel-ruby/flags/remote_flags_spec.rb Updates constructor usage and adds auth test for remote flags with credentials.
spec/mixpanel-ruby/flags/local_flags_spec.rb Updates constructor usage and adds auth test for local flags with credentials.
spec/mixpanel-ruby/events_spec.rb Adds import message-shape test for service account credentials (currently missing a require).
spec/mixpanel-ruby/credentials_spec.rb New unit tests for credentials validation + JSON serialization.
spec/mixpanel-ruby/consumer_spec.rb Adds test ensuring import requests include service account fields.
Readme.rdoc Documents service account usage for import and feature flags.
lib/mixpanel-ruby/tracker.rb Adds credentials: keyword and passes it to flags providers; updates import docs/signature.
lib/mixpanel-ruby/flags/remote_flags_provider.rb Extends initializer to accept credentials and forward into provider config.
lib/mixpanel-ruby/flags/local_flags_provider.rb Extends initializer to accept credentials and forward into provider config.
lib/mixpanel-ruby/flags/flags_provider.rb Uses credentials for Basic Auth when present; retains token auth fallback.
lib/mixpanel-ruby/events.rb Allows import to accept either API key or service account credentials.
lib/mixpanel-ruby/credentials.rb Implements ServiceAccountCredentials and JSON serialization helpers.
lib/mixpanel-ruby/consumer.rb Sends either service account fields or legacy API key when posting import payloads.
lib/mixpanel-ruby.rb Requires the new credentials file.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/mixpanel-ruby/flags/flags_provider.rb
Comment thread spec/mixpanel-ruby/events_spec.rb
@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown

Confidence Score: 4/5

Generally safe to merge; the core serialisation boundary is handled correctly, but the positional-argument shift in RemoteFlagsProvider is a silent breaking change for any direct instantiator of that class.

All concerns raised in the prior review threads appear addressed: LocalFlagsProvider now appends credentials as a default-nil last parameter, integer project_id is coerced before the empty? check, and the secret is intentionally included in as_json so the consumer can use it for Basic Auth after deserialization. The one remaining concern is that RemoteFlagsProvider takes credentials as a new mandatory 3rd positional argument — anyone calling RemoteFlagsProvider.new(token, config, callback, handler) directly will silently pass their callback as credentials with no ArgumentError.

lib/mixpanel-ruby/flags/remote_flags_provider.rb — the new positional argument ordering differs from LocalFlagsProvider's backward-compatible approach and can silently misroute arguments for existing direct callers.

Important Files Changed

Filename Overview
lib/mixpanel-ruby/credentials.rb New ServiceAccountCredentials class; validates username/secret/project_id, converts Integer project_id to String, and serialises with secret for the consumer's Basic Auth use.
lib/mixpanel-ruby/consumer.rb Consumer#send! now extracts credentials from the deserialized message and forwards them to request(); Consumer#request signature gains two optional parameters (credentials, type) and applies Basic Auth + project_id query param for :import.
lib/mixpanel-ruby/events.rb import() now accepts ServiceAccountCredentials or legacy API key; emits a deprecation warning for the legacy path.
lib/mixpanel-ruby/flags/flags_provider.rb FlagsProvider base class now reads credentials from provider_config and uses method-based access to set Basic Auth and append project_id.
lib/mixpanel-ruby/flags/local_flags_provider.rb credentials added as backward-compatible last parameter (default nil), passed through provider_config to base class.
lib/mixpanel-ruby/flags/remote_flags_provider.rb credentials inserted as mandatory 3rd positional parameter, shifting tracker_callback and error_handler — a breaking change for any direct instantiation.
lib/mixpanel-ruby/tracker.rb Tracker#initialize gains a credentials: keyword argument and passes it to both flag providers. Clean, additive change.
spec/mixpanel-ruby/credentials_spec.rb New spec covers validation, integer project_id coercion, JSON serialisation (including secret), and round-trip deserialization.
spec/mixpanel-ruby/consumer_spec.rb New test verifies Basic Auth header and project_id query param for service account imports.
spec/mixpanel-ruby/events_spec.rb Legacy import test updated to assert deprecation warning; new test verifies service account path produces correct message structure.
spec/mixpanel-ruby/flags/local_flags_spec.rb Existing calls updated to pass nil credentials; new test verifies Basic Auth header is set when credentials are provided.
spec/mixpanel-ruby/flags/remote_flags_spec.rb Existing calls updated to pass nil credentials in new 3rd position; new test verifies Basic Auth with service account credentials.
spec/mixpanel-ruby/tracker_spec.rb New test verifies credentials propagate to flag providers via instance_variable_get — correct by identity equality but brittle to internal refactoring.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant User
    participant Tracker
    participant Events
    participant Consumer
    participant MixpanelAPI

    User->>Tracker: "import(credentials, distinct_id, event, props)"
    Tracker->>Events: "import(credentials, ...)"
    Note over Events: "Credentials? message['credentials'] = obj"
    Events->>Events: "message.to_json (secret serialised)"
    Events->>Consumer: "sink.call(:import, message_json)"
    Consumer->>Consumer: "JSON.load -> credentials plain hash"
    Consumer->>Consumer: "uri.query += project_id"
    Consumer->>Consumer: "request.basic_auth(username, secret)"
    Consumer->>MixpanelAPI: "POST /import?project_id=... + Basic Auth"
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant User
    participant Tracker
    participant Events
    participant Consumer
    participant MixpanelAPI

    User->>Tracker: "import(credentials, distinct_id, event, props)"
    Tracker->>Events: "import(credentials, ...)"
    Note over Events: "Credentials? message['credentials'] = obj"
    Events->>Events: "message.to_json (secret serialised)"
    Events->>Consumer: "sink.call(:import, message_json)"
    Consumer->>Consumer: "JSON.load -> credentials plain hash"
    Consumer->>Consumer: "uri.query += project_id"
    Consumer->>Consumer: "request.basic_auth(username, secret)"
    Consumer->>MixpanelAPI: "POST /import?project_id=... + Basic Auth"
Loading

Reviews (8): Last reviewed commit: "Fix tests after rebase" | Re-trigger Greptile

Comment thread lib/mixpanel-ruby/consumer.rb
Comment thread lib/mixpanel-ruby/events.rb
@lajohn4747 lajohn4747 marked this pull request as draft June 30, 2026 22:01
The stub needs to include the project_id query parameter to match the actual request being made.
Comment thread lib/mixpanel-ruby/credentials.rb
…ation

The secret should not be included in the serialized message JSON to prevent
exposure in logs, queues, or custom consumer implementations. The secret is
only needed at the HTTP layer for Basic Auth, not in the message payload.
Comment thread lib/mixpanel-ruby/credentials.rb
Tests now verify that the secret is excluded from JSON serialization
for security, while still being accessible via the object's accessor.
Comment thread lib/mixpanel-ruby/credentials.rb Outdated
Mixpanel project IDs are displayed as integers in the dashboard, so
users naturally pass them as integers. This fix converts integer
project_ids to strings and avoids NoMethodError on Integer#empty?.
@lajohn4747 lajohn4747 marked this pull request as ready for review July 1, 2026 16:35
Comment thread lib/mixpanel-ruby/flags/local_flags_provider.rb Outdated
@lajohn4747 lajohn4747 requested review from efahk and tylerjroach July 1, 2026 17:21
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