Skip to content

[SM] Extract billable methods from user/license model to a class/module

Everyone can contribute. Help move this issue forward while earning points, leveling up and collecting rewards.

NOTES: The code this issue discusses refactoring would probably still be relevant even with SAM. We don't necessarily have to do this refactoring, and we may not end up doing it exactly how this issue describes. Leaving open for reference.

Create GitLab::License::BillableUsers Class Framework

Create a new class to serve as the central place for billing logic in self-managed GitLab instances. This class will handle all billable user calculations, provide a clean API for license-related queries, and maintain proper memoization.

Acceptance Criteria:

  • New class created in appropriate namespace (GitLab::License::BillableUsers)
  • Basic structure with appropriate initialization
  • Test framework established

Extract Core Billable User Identification Methods from User Model

Extract the core billable user identification methods from the EE::User module to our new BillableUsers class. This includes the billable scope and related methods that define which users count toward license limits.

Acceptance Criteria:

  • Extract the following methods from EE::User:
    • Class method billable (scope)
    • Class method non_billable_users_for_billable_management
    • Class method user_cap_reached?
    • Class method user_cap_max
    • Instance method using_license_seat?
    • Instance method paid_in_current_license?
    • Instance method pending_billable_invitations
  • Delegate from User model to new class

Extract License User Count Methods

Extract license-related user count methods from the License model to the new BillableUsers class. These methods calculate current billable users, track historical maximums, and determine license overages.

Acceptance Criteria:

  • Extract the following methods from License:
    • daily_billable_users_count
    • daily_billable_users
    • maximum_user_count
    • overage
    • historical_max
  • Delegate from License model to new class
  • Maintain performance characteristics and memoization

Issue 4: Create Repository for Historical Billable User Data

Create a dedicated repository pattern for accessing and manipulating historical billable user data, extracting this logic from the License model and associated services. This will centralize all historical data operations within the new BillableUsers class.

Acceptance Criteria:

  • Implement methods to retrieve historical billable user data
  • Create methods to generate CSV reports of user data
  • Extract logic from HistoricalUserData::CsvService
  • Maintain compatibility with existing export formats

Sample Extraction plan

GitLab::License::BillableUsers (new class)
├── Class methods
│   ├── count_billable_users [NEW - combines logic from User.billable scope]
│   ├── user_cap_reached? [FROM: User.user_cap_reached?]
│   ├── user_cap_max [FROM: User.user_cap_max]
│   └── historical_max [FROM: License#historical_max]
├── Instance methods
│   ├── count [NEW - replaces License#daily_billable_users_count]
│   ├── maximum_count [FROM: License#maximum_user_count]
│   ├── overage [FROM: License#overage]
│   ├── user_billable? [NEW - based on User#using_license_seat?]
│   └── historical_data [FROM: License#historical_data]
├── Repository methods for historical data
│   ├── export_csv [FROM: HistoricalUserData::CsvService#generate]
│   ├── record_measurement [NEW - encapsulates Analytics::UsageTrends::CounterJobWorker logic]
│   └── get_historical_data [FROM: License#historical_data]
└── Specialized calculators for different GitLab editions
    ├── calculate_for_ultimate [NEW - implements License#exclude_guests_from_active_count? logic]
    └── calculate_for_premium [NEW - implements similar plan-specific logic]
  • From License model:
    • daily_billable_users_count → Would delegate to BillableUsers#count
    • daily_billable_users → Would be replaced by BillableUsers#daily_users
    • maximum_user_count → Would delegate to BillableUsers#maximum_count
    • overage → Would delegate to BillableUsers#overage
  • From User model:
    • billable scope → Would be replaced by BillableUsers.billable_users
    • using_license_seat? → Would delegate to BillableUsers#user_billable?
    • paid_in_current_license? → Would be moved to BillableUsers#user_paid_in_current_license?
  • From API and Admin Controllers:
    • All calls to License#daily_billable_users_count would be replaced with GitLab::License::BillableUsers.count
    • All calls to license historical data would use the new repository methods

Update Admin Views and API Endpoints

Update admin views and API endpoints to use the new BillableUsers class instead of directly accessing User or License models. This includes admin dashboard pages, license information pages, and REST API endpoints.

Acceptance Criteria:

Update admin dashboard views Update license information views Update API endpoints in ee/lib/api/license.rb Ensure no regression in UI/API behavior Comprehensive test coverage

Update Background Workers and Services

Update background workers and services to use the new BillableUsers class for billable user calculations. This includes updating workers that refresh billable user counts and synchronize seat usage data.

Acceptance Criteria:

Update SyncSeatLinkWorker Update Analytics::UsageTrends::CounterJobWorker Update member management services that use billable logic Ensure no regression in worker/service behavior Comprehensive test coverage

Edited by 🤖 GitLab Bot 🤖