[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
- Class method
- 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 toBillableUsers#count
-
daily_billable_users
→ Would be replaced byBillableUsers#daily_users
-
maximum_user_count
→ Would delegate toBillableUsers#maximum_count
-
overage
→ Would delegate toBillableUsers#overage
-
- From
User
model:-
billable
scope → Would be replaced byBillableUsers.billable_users
-
using_license_seat?
→ Would delegate toBillableUsers#user_billable?
-
paid_in_current_license?
→ Would be moved toBillableUsers#user_paid_in_current_license?
-
- From API and Admin Controllers:
- All calls to
License#daily_billable_users_count
would be replaced withGitLab::License::BillableUsers.count
- All calls to license historical data would use the new repository methods
- All calls to
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