From 70d1f0b7c42889109686bfae8bfdf70e8c3ef918 Mon Sep 17 00:00:00 2001 From: Sheldon Led Date: Wed, 22 Oct 2025 20:39:57 +0100 Subject: [PATCH 1/6] Update subscriptionUsage query - Rename lastUpdated to lastEventTransactionAt - Add oneTimeCredits node - Add usersUsage.creditsUsed --- doc/api/graphql/reference/_index.md | 15 +- .../one_time_credits_type.rb | 24 ++++ .../subscription_usage/users_usage_type.rb | 13 +- .../subscription_usage_type.rb | 8 +- .../subscription_usage_client.rb | 37 ++++- .../subscription_usage.rb | 18 ++- .../subscriptions_usage/user_usage.rb | 4 + .../one_time_credits_type_spec.rb | 12 ++ .../users_usage_type_spec.rb | 1 + .../subscription_usage_type_spec.rb | 3 +- .../subscription_usage_client_spec.rb | 47 ++++++- .../subscription_usage_spec.rb | 132 ++++++++++++++++-- .../subscriptions_usage/user_usage_spec.rb | 31 ++++ .../subscription_usage_spec.rb | 35 ++++- 14 files changed, 353 insertions(+), 27 deletions(-) create mode 100644 ee/app/graphql/types/gitlab_subscriptions/subscription_usage/one_time_credits_type.rb create mode 100644 ee/spec/graphql/types/gitlab_subscriptions/subscription_usage/one_time_credits_type_spec.rb diff --git a/doc/api/graphql/reference/_index.md b/doc/api/graphql/reference/_index.md index 4aefb70d19f546..c26f7324eb901c 100644 --- a/doc/api/graphql/reference/_index.md +++ b/doc/api/graphql/reference/_index.md @@ -30973,6 +30973,17 @@ Describes the subscription history of a given namespace. | `seatsInUse` | [`Int`](#int) | Seats being used in subscription. | | `startDate` | [`Time`](#time) | Subscription start date. | +### `GitlabSubscriptionOneTimeCredits` + +Describes the usage of one time credits for the subscription. + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `creditsUsed` | [`Float`](#float) | Total of GitLab Credits consumed from the one time credits allocation. | +| `totalCredits` | [`Float`](#float) | Total of GitLab Credits allocated as one time credits. | + ### `GitlabSubscriptionOverage` Describes the overage usage of consumables for the subscription. @@ -31006,7 +31017,8 @@ Describes the usage of consumables under the subscription. | Name | Type | Description | | ---- | ---- | ----------- | | `endDate` | [`ISO8601Date`](#iso8601date) | End date of the period covered by the usage data. | -| `lastUpdated` | [`ISO8601DateTime`](#iso8601datetime) | Date and time when the usage data was last updated. | +| `lastEventTransactionAt` | [`ISO8601DateTime`](#iso8601datetime) | Date and time when the last usage event resulted in a wallet transaction. | +| `oneTimeCredits` | [`GitlabSubscriptionOneTimeCredits`](#gitlabsubscriptiononetimecredits) | One time credits usage for the subscription. | | `overage` | [`GitlabSubscriptionOverage`](#gitlabsubscriptionoverage) | Overage statistics. | | `poolUsage` | [`GitlabSubscriptionPoolUsage`](#gitlabsubscriptionpoolusage) | Consumption usage for the subscription shared pool. | | `purchaseCreditsPath` | [`String`](#string) | URL to purchase GitLab Credits. | @@ -31075,6 +31087,7 @@ Describes the usage of consumables by users under the subscription. | Name | Type | Description | | ---- | ---- | ----------- | +| `creditsUsed` | [`Float`](#float) | GitLab Credits used by consumers of the subscription. | | `dailyUsage` | [`[GitlabSubscriptionDailyUsage!]`](#gitlabsubscriptiondailyusage) | Array of daily usage of GitLab Credits. | | `totalUsersUsingCredits` | [`Int`](#int) | Total number of users consuming GitLab Credits. | | `totalUsersUsingOverage` | [`Int`](#int) | Total number of users consuming overage. | diff --git a/ee/app/graphql/types/gitlab_subscriptions/subscription_usage/one_time_credits_type.rb b/ee/app/graphql/types/gitlab_subscriptions/subscription_usage/one_time_credits_type.rb new file mode 100644 index 00000000000000..4a8fe3d34e9e8f --- /dev/null +++ b/ee/app/graphql/types/gitlab_subscriptions/subscription_usage/one_time_credits_type.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Types + module GitlabSubscriptions + module SubscriptionUsage + class OneTimeCreditsType < BaseObject + graphql_name 'GitlabSubscriptionOneTimeCredits' + description 'Describes the usage of one time credits for the subscription' + + authorize :read_subscription_usage + + field :total_credits, + type: GraphQL::Types::Float, + null: true, + description: 'Total of GitLab Credits allocated as one time credits.' + + field :credits_used, + type: GraphQL::Types::Float, + null: true, + description: 'Total of GitLab Credits consumed from the one time credits allocation.' + end + end + end +end diff --git a/ee/app/graphql/types/gitlab_subscriptions/subscription_usage/users_usage_type.rb b/ee/app/graphql/types/gitlab_subscriptions/subscription_usage/users_usage_type.rb index 411e72cf6ef675..1b1b988c2e7380 100644 --- a/ee/app/graphql/types/gitlab_subscriptions/subscription_usage/users_usage_type.rb +++ b/ee/app/graphql/types/gitlab_subscriptions/subscription_usage/users_usage_type.rb @@ -10,16 +10,23 @@ class UsersUsageType < BaseObject authorize :read_subscription_usage # rubocop: disable GraphQL/ExtractType -- no value for now - field :total_users_using_credits, GraphQL::Types::Int, null: true, + field :total_users_using_credits, GraphQL::Types::Int, + null: true, description: 'Total number of users consuming GitLab Credits.' - field :total_users_using_pool, GraphQL::Types::Int, null: true, + field :total_users_using_pool, GraphQL::Types::Int, + null: true, description: 'Total number of users consuming pool GitLab Credits.' - field :total_users_using_overage, GraphQL::Types::Int, null: true, + field :total_users_using_overage, GraphQL::Types::Int, + null: true, description: 'Total number of users consuming overage.' # rubocop:enable GraphQL/ExtractType + field :credits_used, GraphQL::Types::Float, + null: true, + description: 'GitLab Credits used by consumers of the subscription.' + field :daily_usage, [DailyUsageType], null: true, diff --git a/ee/app/graphql/types/gitlab_subscriptions/subscription_usage_type.rb b/ee/app/graphql/types/gitlab_subscriptions/subscription_usage_type.rb index c66e25a1fa6136..808f30d175827f 100644 --- a/ee/app/graphql/types/gitlab_subscriptions/subscription_usage_type.rb +++ b/ee/app/graphql/types/gitlab_subscriptions/subscription_usage_type.rb @@ -8,9 +8,9 @@ class SubscriptionUsageType < BaseObject authorize :read_subscription_usage - field :last_updated, GraphQL::Types::ISO8601DateTime, + field :last_event_transaction_at, GraphQL::Types::ISO8601DateTime, null: true, - description: 'Date and time when the usage data was last updated.' + description: 'Date and time when the last usage event resulted in a wallet transaction.' field :start_date, GraphQL::Types::ISO8601Date, null: true, @@ -24,6 +24,10 @@ class SubscriptionUsageType < BaseObject null: true, description: 'URL to purchase GitLab Credits.' + field :one_time_credits, SubscriptionUsage::OneTimeCreditsType, + null: true, + description: 'One time credits usage for the subscription.' + field :pool_usage, SubscriptionUsage::PoolUsageType, null: true, description: 'Consumption usage for the subscription shared pool.' diff --git a/ee/lib/gitlab/subscription_portal/subscription_usage_client.rb b/ee/lib/gitlab/subscription_portal/subscription_usage_client.rb index 72c5f2bb72edbb..dd09383d32402c 100644 --- a/ee/lib/gitlab/subscription_portal/subscription_usage_client.rb +++ b/ee/lib/gitlab/subscription_portal/subscription_usage_client.rb @@ -16,13 +16,31 @@ class SubscriptionUsageClient < Client gitlabCreditsUsage { startDate endDate - lastUpdated + lastEventTransactionAt purchaseCreditsPath } } } GQL + GET_ONE_TIME_CREDITS_QUERY = <<~GQL + query subscriptionUsage( + $namespaceId: ID, + $licenseKey: String, + $startDate: ISO8601Date, + $endDate: ISO8601Date + ) { + subscription(namespaceId: $namespaceId, licenseKey: $licenseKey) { + gitlabCreditsUsage(startDate: $startDate, endDate: $endDate) { + oneTimeCredits { + totalCredits + creditsUsed + } + } + } + } + GQL + GET_POOL_USAGE_QUERY = <<~GQL query subscriptionUsage( $namespaceId: ID, @@ -131,6 +149,7 @@ class SubscriptionUsageClient < Client totalUsersUsingCredits totalUsersUsingPool totalUsersUsingOverage + creditsUsed dailyUsage { date creditsUsed @@ -177,6 +196,22 @@ def get_metadata end strong_memoize_attr :get_metadata + def get_one_time_credits + response = execute_graphql_query( + query: GET_ONE_TIME_CREDITS_QUERY, + variables: default_variables.merge(startDate: start_date, endDate: end_date) + ) + + if unsuccessful_response?(response) + error(GET_ONE_TIME_CREDITS_QUERY, response) + else + { + success: true, + oneTimeCredits: response.dig(:data, :subscription, :gitlabCreditsUsage, :oneTimeCredits) + } + end + end + def get_pool_usage response = execute_graphql_query( query: GET_POOL_USAGE_QUERY, diff --git a/ee/lib/gitlab_subscriptions/subscription_usage.rb b/ee/lib/gitlab_subscriptions/subscription_usage.rb index 72ec9695b07afb..35f9f718dbd06f 100644 --- a/ee/lib/gitlab_subscriptions/subscription_usage.rb +++ b/ee/lib/gitlab_subscriptions/subscription_usage.rb @@ -4,6 +4,7 @@ module GitlabSubscriptions class SubscriptionUsage include ::Gitlab::Utils::StrongMemoize + OneTimeCredits = Struct.new(:total_credits, :credits_used, :declarative_policy_subject) PoolUsage = Struct.new(:total_credits, :credits_used, :daily_usage, :declarative_policy_subject) Overage = Struct.new(:is_allowed, :credits_used, :daily_usage, :declarative_policy_subject) DailyUsage = Struct.new(:date, :credits_used, :declarative_policy_subject) @@ -28,14 +29,27 @@ def end_date usage_metadata[:endDate] end - def last_updated - usage_metadata[:lastUpdated] + def last_event_transaction_at + usage_metadata[:lastEventTransactionAt] end def purchase_credits_path usage_metadata[:purchaseCreditsPath] end + def one_time_credits + one_time_credits_response = subscription_usage_client.get_one_time_credits + + return unless one_time_credits_response[:success] + + OneTimeCredits.new( + total_credits: one_time_credits_response.dig(:oneTimeCredits, :totalCredits), + credits_used: one_time_credits_response.dig(:oneTimeCredits, :creditsUsed), + declarative_policy_subject: self + ) + end + strong_memoize_attr :one_time_credits + def pool_usage pool_usage_response = subscription_usage_client.get_pool_usage diff --git a/ee/lib/gitlab_subscriptions/subscriptions_usage/user_usage.rb b/ee/lib/gitlab_subscriptions/subscriptions_usage/user_usage.rb index 24adec5a336461..596a2e23605594 100644 --- a/ee/lib/gitlab_subscriptions/subscriptions_usage/user_usage.rb +++ b/ee/lib/gitlab_subscriptions/subscriptions_usage/user_usage.rb @@ -32,6 +32,10 @@ def total_users_using_overage usage_stats[:totalUsersUsingOverage] end + def credits_used + usage_stats[:creditsUsed] + end + def users(username: nil) strong_memoize_with(:users, username) do case subscription_usage.subscription_target diff --git a/ee/spec/graphql/types/gitlab_subscriptions/subscription_usage/one_time_credits_type_spec.rb b/ee/spec/graphql/types/gitlab_subscriptions/subscription_usage/one_time_credits_type_spec.rb new file mode 100644 index 00000000000000..3f1500a5f4bd84 --- /dev/null +++ b/ee/spec/graphql/types/gitlab_subscriptions/subscription_usage/one_time_credits_type_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['GitlabSubscriptionOneTimeCredits'], feature_category: :consumables_cost_management do + it { expect(described_class.graphql_name).to eq('GitlabSubscriptionOneTimeCredits') } + it { expect(described_class).to require_graphql_authorizations(:read_subscription_usage) } + + it 'has expected fields' do + expect(described_class).to have_graphql_fields([:total_credits, :credits_used]) + end +end diff --git a/ee/spec/graphql/types/gitlab_subscriptions/subscription_usage/users_usage_type_spec.rb b/ee/spec/graphql/types/gitlab_subscriptions/subscription_usage/users_usage_type_spec.rb index cb6a7358c5265c..c4bd6e5fec3b9a 100644 --- a/ee/spec/graphql/types/gitlab_subscriptions/subscription_usage/users_usage_type_spec.rb +++ b/ee/spec/graphql/types/gitlab_subscriptions/subscription_usage/users_usage_type_spec.rb @@ -11,6 +11,7 @@ :total_users_using_credits, :total_users_using_pool, :total_users_using_overage, + :credits_used, :daily_usage, :users ]) diff --git a/ee/spec/graphql/types/gitlab_subscriptions/subscription_usage_type_spec.rb b/ee/spec/graphql/types/gitlab_subscriptions/subscription_usage_type_spec.rb index 0513a0bb825b3b..ac4ba8b3c47590 100644 --- a/ee/spec/graphql/types/gitlab_subscriptions/subscription_usage_type_spec.rb +++ b/ee/spec/graphql/types/gitlab_subscriptions/subscription_usage_type_spec.rb @@ -8,10 +8,11 @@ it 'has expected fields' do expect(described_class).to have_graphql_fields([ - :last_updated, + :last_event_transaction_at, :start_date, :end_date, :purchase_credits_path, + :one_time_credits, :pool_usage, :overage, :users_usage diff --git a/ee/spec/lib/gitlab/subscription_portal/subscription_usage_client_spec.rb b/ee/spec/lib/gitlab/subscription_portal/subscription_usage_client_spec.rb index ed79d6a0ae2e4b..d8be7409bd2844 100644 --- a/ee/spec/lib/gitlab/subscription_portal/subscription_usage_client_spec.rb +++ b/ee/spec/lib/gitlab/subscription_portal/subscription_usage_client_spec.rb @@ -148,7 +148,7 @@ gitlabCreditsUsage: { startDate: "2025-10-01", endDate: "2025-10-31", - lastUpdated: "2025-10-01T16:19:59Z", + lastEventTransactionAt: "2025-10-01T16:19:59Z", purchaseCreditsPath: '/mock/path' } } @@ -162,7 +162,7 @@ subscriptionUsage: { startDate: "2025-10-01", endDate: "2025-10-31", - lastUpdated: "2025-10-01T16:19:59Z", + lastEventTransactionAt: "2025-10-01T16:19:59Z", purchaseCreditsPath: '/mock/path' } } @@ -178,6 +178,47 @@ end end + describe '#get_one_time_credits' do + context 'when the subscription portal response is successful' do + let(:request) { client.get_one_time_credits } + let(:query) { described_class::GET_ONE_TIME_CREDITS_QUERY } + let(:one_time_credits) do + { + totalCredits: 1000, + creditsUsed: 12.25 + } + end + + let(:portal_response) do + { + success: true, + data: { + subscription: { + gitlabCreditsUsage: { + oneTimeCredits: one_time_credits + } + } + } + } + end + + let(:expected_response) do + { + success: true, + oneTimeCredits: one_time_credits + } + end + + include_context 'for self-managed request' do + let(:variables) { { licenseKey: license_key, startDate: start_date, endDate: end_date } } + end + + include_context 'for gitlab.com request' do + let(:variables) { { namespaceId: namespace_id, startDate: start_date, endDate: end_date } } + end + end + end + describe '#get_pool_usage' do context 'when the subscription portal response is successful' do let(:request) { client.get_pool_usage } @@ -391,6 +432,7 @@ totalUsersUsingCredits: 3, totalUsersUsingPool: 2, totalUsersUsingOverage: 1, + creditsUsed: 123.45, dailyUsage: [{ date: '2025-10-01', creditsUsed: 321 }] } } @@ -406,6 +448,7 @@ totalUsersUsingCredits: 3, totalUsersUsingPool: 2, totalUsersUsingOverage: 1, + creditsUsed: 123.45, dailyUsage: [{ date: '2025-10-01', creditsUsed: 321 }] } } diff --git a/ee/spec/lib/gitlab_subscriptions/subscription_usage_spec.rb b/ee/spec/lib/gitlab_subscriptions/subscription_usage_spec.rb index 7fd1207ae79bf6..b7c0ee6194cf53 100644 --- a/ee/spec/lib/gitlab_subscriptions/subscription_usage_spec.rb +++ b/ee/spec/lib/gitlab_subscriptions/subscription_usage_spec.rb @@ -223,8 +223,8 @@ end end - describe '#last_updated' do - subject(:last_updated) { subscription_usage.last_updated } + describe '#last_event_transaction_at' do + subject(:last_event_transaction_at) { subscription_usage.last_event_transaction_at } before do allow(subscription_usage_client).to receive(:get_metadata).and_return(client_response) @@ -240,10 +240,12 @@ end context 'when the client returns a successful response' do - let(:client_response) { { success: true, subscriptionUsage: { lastUpdated: "2025-10-01T16:19:59Z" } } } + let(:client_response) do + { success: true, subscriptionUsage: { lastEventTransactionAt: "2025-10-01T16:19:59Z" } } + end it 'returns the last updated time' do - expect(last_updated).to be("2025-10-01T16:19:59Z") + expect(last_event_transaction_at).to be("2025-10-01T16:19:59Z") end end @@ -251,15 +253,15 @@ let(:client_response) { { success: false } } it 'returns nil' do - expect(last_updated).to be_nil + expect(last_event_transaction_at).to be_nil end end - context 'when the client response is missing lastUpdated' do - let(:client_response) { { success: true, subscriptionUsage: { lastUpdated: nil } } } + context 'when the client response is missing lastEventTransactionAt' do + let(:client_response) { { success: true, subscriptionUsage: { lastEventTransactionAt: nil } } } it 'returns nil' do - expect(last_updated).to be_nil + expect(last_event_transaction_at).to be_nil end end end @@ -273,14 +275,122 @@ end let(:license) { build_stubbed(:license) } - let(:client_response) { { success: true, subscriptionUsage: { lastUpdated: "2025-10-01T16:19:59Z" } } } + let(:client_response) { { success: true, subscriptionUsage: { lastEventTransactionAt: "2025-10-01T16:19:59Z" } } } before do allow(License).to receive(:current).and_return(license) end it 'returns the last updated time' do - expect(last_updated).to be("2025-10-01T16:19:59Z") + expect(last_event_transaction_at).to be("2025-10-01T16:19:59Z") + end + + context 'when License.current is nil' do + before do + allow(License).to receive(:current).and_return(nil) + end + + it 'handles nil license gracefully' do + expect { last_event_transaction_at }.not_to raise_error + end + end + end + end + + describe '#one_time_credits' do + subject(:one_time_credits) { subscription_usage.one_time_credits } + + before do + allow(subscription_usage_client).to receive(:get_one_time_credits).and_return(client_response) + end + + context 'when subscription_target is :namespace' do + let(:subscription_usage) do + described_class.new( + subscription_target: :namespace, + subscription_usage_client: subscription_usage_client, + namespace: group + ) + end + + context 'when the client returns a successful response' do + let(:client_response) do + { + success: true, + oneTimeCredits: { + totalCredits: 1000, + creditsUsed: 25.32 + } + } + end + + it 'returns a OneTimeCredits struct with correct data' do + expect(one_time_credits).to be_a(GitlabSubscriptions::SubscriptionUsage::OneTimeCredits) + expect(one_time_credits).to have_attributes( + total_credits: 1000, + credits_used: 25.32, + declarative_policy_subject: subscription_usage + ) + end + end + + context 'when the client returns an unsuccessful response' do + let(:client_response) { { success: false } } + + it 'returns nil' do + expect(one_time_credits).to be_nil + end + end + + context 'when the client response is missing oneTimeCredits data' do + let(:client_response) do + { + success: true, + oneTimeCredits: nil + } + end + + it 'returns a OneTimeCredits struct with no values' do + expect(one_time_credits).to be_a(GitlabSubscriptions::SubscriptionUsage::OneTimeCredits) + expect(one_time_credits).to have_attributes( + total_credits: nil, + credits_used: nil, + declarative_policy_subject: subscription_usage + ) + end + end + end + + context 'when subscription_target is :instance' do + let(:subscription_usage) do + described_class.new( + subscription_target: :instance, + subscription_usage_client: subscription_usage_client + ) + end + + let(:license) { build_stubbed(:license) } + let(:client_response) do + { + success: true, + oneTimeCredits: { + totalCredits: 2000, + creditsUsed: 123.99 + } + } + end + + before do + allow(License).to receive(:current).and_return(license) + end + + it 'returns a OneTimeCredits struct with correct data' do + expect(one_time_credits).to be_a(GitlabSubscriptions::SubscriptionUsage::OneTimeCredits) + expect(one_time_credits).to have_attributes( + total_credits: 2000, + credits_used: 123.99, + declarative_policy_subject: subscription_usage + ) end context 'when License.current is nil' do @@ -289,7 +399,7 @@ end it 'handles nil license gracefully' do - expect { last_updated }.not_to raise_error + expect { one_time_credits }.not_to raise_error end end end diff --git a/ee/spec/lib/gitlab_subscriptions/subscriptions_usage/user_usage_spec.rb b/ee/spec/lib/gitlab_subscriptions/subscriptions_usage/user_usage_spec.rb index 927d5dbbf4e908..bb6b0612d0653d 100644 --- a/ee/spec/lib/gitlab_subscriptions/subscriptions_usage/user_usage_spec.rb +++ b/ee/spec/lib/gitlab_subscriptions/subscriptions_usage/user_usage_spec.rb @@ -14,6 +14,7 @@ totalUsersUsingCredits: 3, totalUsersUsingPool: 2, totalUsersUsingOverage: 1, + creditsUsed: 123.45, dailyUsage: [{ date: '2025-10-01', creditsUsed: 321 }] } } @@ -158,6 +159,36 @@ end end + describe "#credits_used" do + before do + allow(subscription_usage_client).to receive(:get_users_usage_stats).and_return(client_response) + end + + context 'when the client returns a successful response' do + let(:client_response) { { success: true, usersUsage: { creditsUsed: 123.45 } } } + + it 'returns the correct data' do + expect(user_usage.credits_used).to eq(123.45) + end + end + + context 'when the client returns an unsuccessful response' do + let(:client_response) { { success: false } } + + it 'returns nil' do + expect(user_usage.credits_used).to be_nil + end + end + + context 'when the client response is missing the data' do + let(:client_response) { { success: true, usersUsage: nil } } + + it 'returns nil' do + expect(user_usage.credits_used).to be_nil + end + end + end + describe "#users" do context 'when subscription_target is :namespace' do before do diff --git a/ee/spec/requests/api/graphql/gitlab_subscriptions/subscription_usage_spec.rb b/ee/spec/requests/api/graphql/gitlab_subscriptions/subscription_usage_spec.rb index c2f4eb61d81207..4e0baca48508af 100644 --- a/ee/spec/requests/api/graphql/gitlab_subscriptions/subscription_usage_spec.rb +++ b/ee/spec/requests/api/graphql/gitlab_subscriptions/subscription_usage_spec.rb @@ -49,9 +49,13 @@ let(:user_arguments) { {} } let(:query_fields) do [ - :last_updated, + :last_event_transaction_at, :start_date, :end_date, + query_graphql_field(:one_time_credits, {}, [ + :total_credits, + :credits_used + ]), :purchase_credits_path, query_graphql_field(:pool_usage, {}, [ :total_credits, @@ -67,6 +71,7 @@ :total_users_using_credits, :total_users_using_pool, :total_users_using_overage, + :credits_used, query_graphql_field(:daily_usage, {}, [:date, :credits_used]), query_graphql_field(:users, user_arguments, [ query_graphql_field(:nodes, {}, [ @@ -124,7 +129,7 @@ subscriptionUsage: { startDate: "2025-10-01", endDate: "2025-10-31", - lastUpdated: "2025-10-01T16:19:59Z", + lastEventTransactionAt: "2025-10-01T16:19:59Z", purchaseCreditsPath: '/mock/path' } } @@ -176,10 +181,19 @@ totalUsersUsingCredits: 3, totalUsersUsingPool: 2, totalUsersUsingOverage: 1, + creditsUsed: 123.45, dailyUsage: [{ date: '2025-10-01', creditsUsed: 321 }] } } + one_time_credits = { + success: true, + oneTimeCredits: { + totalCredits: 1000, + creditsUsed: 15.32 + } + } + pool_usage = { success: true, poolUsage: { @@ -201,6 +215,7 @@ allow_next_instance_of(Gitlab::SubscriptionPortal::SubscriptionUsageClient) do |client| allow(client).to receive_messages( get_metadata: metadata, + get_one_time_credits: one_time_credits, get_pool_usage: pool_usage, get_overage_usage: overage_usage, get_events_for_user_id: { success: true, userEvents: events_for_user_id }, @@ -220,11 +235,16 @@ end it 'returns subscription usage for instance' do - expect(graphql_data_at(:subscription_usage, :lastUpdated)).to eq("2025-10-01T16:19:59Z") + expect(graphql_data_at(:subscription_usage, :lastEventTransactionAt)).to eq("2025-10-01T16:19:59Z") expect(graphql_data_at(:subscription_usage, :startDate)).to eq("2025-10-01") expect(graphql_data_at(:subscription_usage, :endDate)).to eq("2025-10-31") expect(graphql_data_at(:subscription_usage, :purchaseCreditsPath)).to eq("/mock/path") + expect(graphql_data_at(:subscription_usage, :oneTimeCredits)).to eq({ + totalCredits: 1000, + creditsUsed: 15.32 + }.with_indifferent_access) + expect(graphql_data_at(:subscription_usage, :poolUsage)).to eq({ totalCredits: 1000, creditsUsed: 250, @@ -240,6 +260,7 @@ expect(graphql_data_at(:subscription_usage, :usersUsage, :totalUsersUsingCredits)).to eq(3) expect(graphql_data_at(:subscription_usage, :usersUsage, :totalUsersUsingPool)).to eq(2) expect(graphql_data_at(:subscription_usage, :usersUsage, :totalUsersUsingOverage)).to eq(1) + expect(graphql_data_at(:subscription_usage, :usersUsage, :creditsUsed)).to eq(123.45) expect(graphql_data_at(:subscription_usage, :usersUsage, :dailyUsage)) .to match_array([{ date: '2025-10-01', creditsUsed: 321 }.with_indifferent_access]) @@ -322,11 +343,16 @@ end it 'returns subscription usage for the group' do - expect(graphql_data_at(:subscription_usage, :lastUpdated)).to eq("2025-10-01T16:19:59Z") + expect(graphql_data_at(:subscription_usage, :lastEventTransactionAt)).to eq("2025-10-01T16:19:59Z") expect(graphql_data_at(:subscription_usage, :startDate)).to eq("2025-10-01") expect(graphql_data_at(:subscription_usage, :endDate)).to eq("2025-10-31") expect(graphql_data_at(:subscription_usage, :purchaseCreditsPath)).to eq("/mock/path") + expect(graphql_data_at(:subscription_usage, :oneTimeCredits)).to eq({ + totalCredits: 1000, + creditsUsed: 15.32 + }.with_indifferent_access) + expect(graphql_data_at(:subscription_usage, :poolUsage)).to eq({ totalCredits: 1000, creditsUsed: 250, @@ -342,6 +368,7 @@ expect(graphql_data_at(:subscription_usage, :usersUsage, :totalUsersUsingCredits)).to eq(3) expect(graphql_data_at(:subscription_usage, :usersUsage, :totalUsersUsingPool)).to eq(2) expect(graphql_data_at(:subscription_usage, :usersUsage, :totalUsersUsingOverage)).to eq(1) + expect(graphql_data_at(:subscription_usage, :usersUsage, :creditsUsed)).to eq(123.45) expect(graphql_data_at(:subscription_usage, :usersUsage, :dailyUsage)) .to match_array([{ date: '2025-10-01', creditsUsed: 321 }.with_indifferent_access]) -- GitLab From 1c51121d0fc6ed2427e4e8a28a9d0329ddcf3253 Mon Sep 17 00:00:00 2001 From: Sheldon Led Date: Thu, 23 Oct 2025 07:32:33 +0100 Subject: [PATCH 2/6] Rename lastUpdated to lastEventTransactionAt on the frontend --- .../javascripts/usage_quotas/usage_billing/components/app.vue | 4 ++-- .../graphql/get_subscription_usage.query.graphql | 2 +- .../usage_quotas/usage_billing/users/show/components/app.vue | 2 +- .../show/graphql/get_user_subscription_usage.query.graphql | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ee/app/assets/javascripts/usage_quotas/usage_billing/components/app.vue b/ee/app/assets/javascripts/usage_quotas/usage_billing/components/app.vue index 63d46a57b9e7fe..e13a7a09e4f560 100644 --- a/ee/app/assets/javascripts/usage_quotas/usage_billing/components/app.vue +++ b/ee/app/assets/javascripts/usage_quotas/usage_billing/components/app.vue @@ -107,10 +107,10 @@ export default { -