@@ -46,7 +46,6 @@ import {
46
46
FindPrebuildsParams ,
47
47
TeamMemberRole ,
48
48
WORKSPACE_TIMEOUT_DEFAULT_SHORT ,
49
- WorkspaceType ,
50
49
PrebuildEvent ,
51
50
} from "@gitpod/gitpod-protocol" ;
52
51
import { ResponseError } from "vscode-jsonrpc" ;
@@ -72,7 +71,7 @@ import { BlockedRepository } from "@gitpod/gitpod-protocol/lib/blocked-repositor
72
71
import { EligibilityService } from "../user/eligibility-service" ;
73
72
import { AccountStatementProvider } from "../user/account-statement-provider" ;
74
73
import { GithubUpgradeURL , PlanCoupon } from "@gitpod/gitpod-protocol/lib/payment-protocol" ;
75
- import { BillableSession , BillableSessionRequest , SortOrder } from "@gitpod/gitpod-protocol/lib/usage" ;
74
+ import { BillableSession , BillableSessionRequest } from "@gitpod/gitpod-protocol/lib/usage" ;
76
75
import {
77
76
AssigneeIdentityIdentifier ,
78
77
TeamSubscription ,
@@ -107,13 +106,13 @@ import { BitbucketAppSupport } from "../bitbucket/bitbucket-app-support";
107
106
import { URL } from "url" ;
108
107
import { UserCounter } from "../user/user-counter" ;
109
108
import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution" ;
110
- import { CachingUsageServiceClientProvider } from "@gitpod/usage-api/lib/usage/v1/sugar" ;
111
- import * as usage from "@gitpod/usage-api/lib/usage/v1/usage_pb" ;
109
+ import { CachingUsageServiceClientProvider , UsageService } from "@gitpod/usage-api/lib/usage/v1/sugar" ;
112
110
import { Timestamp } from "google-protobuf/google/protobuf/timestamp_pb" ;
113
111
import { EntitlementService } from "../../../src/billing/entitlement-service" ;
114
112
import { BillingMode } from "@gitpod/gitpod-protocol/lib/billing-mode" ;
115
113
import { BillingModes } from "../billing/billing-mode" ;
116
114
import { getExperimentsClientForBackend } from "@gitpod/gitpod-protocol/lib/experiments/configcat-server" ;
115
+ import { BillingService } from "../billing/billing-service" ;
117
116
118
117
@injectable ( )
119
118
export class GitpodServerEEImpl extends GitpodServerImpl {
@@ -160,6 +159,7 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
160
159
@inject ( EntitlementService ) protected readonly entitlementService : EntitlementService ;
161
160
162
161
@inject ( BillingModes ) protected readonly billingModes : BillingModes ;
162
+ @inject ( BillingService ) protected readonly billingService : BillingService ;
163
163
164
164
initialize (
165
165
client : GitpodClient | undefined ,
@@ -256,39 +256,22 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
256
256
) : Promise < void > {
257
257
await super . mayStartWorkspace ( ctx , user , runningInstances ) ;
258
258
259
- // TODO(at) replace the naive implementation based on usage service
260
- // with a proper call check against the upcoming invoice.
261
- // For now this should just enable the work on fronend.
262
- if ( await this . isUsageBasedFeatureFlagEnabled ( user ) ) {
263
- // dummy implementation to test frontend bits
264
- const attributionId = await this . userService . getWorkspaceUsageAttributionId ( user ) ;
265
- const costCenter = ! ! attributionId && ( await this . costCenterDB . findById ( attributionId ) ) ;
266
- if ( costCenter ) {
267
- const allSessions = await this . listBilledUsage ( ctx , {
268
- attributionId,
269
- startedTimeOrder : SortOrder . Descending ,
270
- } ) ;
271
- const totalUsage = allSessions . map ( ( s ) => s . credits ) . reduce ( ( a , b ) => a + b , 0 ) ;
272
-
273
- if ( totalUsage >= costCenter . spendingLimit ) {
274
- throw new ResponseError (
275
- ErrorCodes . PAYMENT_SPENDING_LIMIT_REACHED ,
276
- "Increase spending limit and try again." ,
277
- {
278
- attributionId : user . usageAttributionId ,
279
- } ,
280
- ) ;
281
- }
282
- }
283
- }
284
-
285
259
const result = await this . entitlementService . mayStartWorkspace ( user , new Date ( ) , runningInstances ) ;
286
260
if ( ! result . enoughCredits ) {
287
261
throw new ResponseError (
288
262
ErrorCodes . NOT_ENOUGH_CREDIT ,
289
263
`Not enough monthly workspace hours. Please upgrade your account to get more hours for your workspaces.` ,
290
264
) ;
291
265
}
266
+ if ( result . spendingLimitReachedOnCostCenter ) {
267
+ throw new ResponseError (
268
+ ErrorCodes . PAYMENT_SPENDING_LIMIT_REACHED ,
269
+ "Increase spending limit and try again." ,
270
+ {
271
+ attributionId : result . spendingLimitReachedOnCostCenter ,
272
+ } ,
273
+ ) ;
274
+ }
292
275
if ( ! ! result . hitParallelWorkspaceLimit ) {
293
276
throw new ResponseError (
294
277
ErrorCodes . TOO_MANY_RUNNING_WORKSPACES ,
@@ -2146,24 +2129,17 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
2146
2129
async getNotifications ( ctx : TraceContext ) : Promise < string [ ] > {
2147
2130
const result = await super . getNotifications ( ctx ) ;
2148
2131
const user = this . checkAndBlockUser ( "getNotifications" ) ;
2149
- if ( user . usageAttributionId ) {
2150
- // This change doesn't matter much because the listBilledUsage() call
2151
- // will be removed anyway in https://github.com/gitpod-io/gitpod/issues/11692
2152
- const request = {
2153
- attributionId : user . usageAttributionId ,
2154
- startedTimeOrder : SortOrder . Descending ,
2155
- } ;
2156
- const allSessions = await this . listBilledUsage ( ctx , request ) ;
2157
- const totalUsage = allSessions . map ( ( s ) => s . credits ) . reduce ( ( a , b ) => a + b , 0 ) ;
2158
- const costCenter = await this . costCenterDB . findById ( user . usageAttributionId ) ;
2132
+
2133
+ const billingMode = await this . billingModes . getBillingModeForUser ( user , new Date ( ) ) ;
2134
+ if ( billingMode . mode === "usage-based" ) {
2135
+ const limit = await this . billingService . checkSpendingLimitReached ( user ) ;
2136
+ const costCenter = await this . costCenterDB . findById ( AttributionId . render ( limit . attributionId ) ) ;
2159
2137
if ( costCenter ) {
2160
- if ( totalUsage > costCenter . spendingLimit ) {
2138
+ if ( limit . reached ) {
2161
2139
result . unshift ( "The spending limit is reached." ) ;
2162
- } else if ( totalUsage > costCenter . spendingLimit * 0.8 ) {
2140
+ } else if ( limit . almostReached ) {
2163
2141
result . unshift ( "The spending limit is almost reached." ) ;
2164
2142
}
2165
- } else {
2166
- log . warn ( "No costcenter found." , { userId : user . id , attributionId : user . usageAttributionId } ) ;
2167
2143
}
2168
2144
}
2169
2145
return result ;
@@ -2192,7 +2168,7 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
2192
2168
timestampFrom ,
2193
2169
timestampTo ,
2194
2170
) ;
2195
- const sessions = response . getSessionsList ( ) . map ( ( s ) => this . mapBilledSession ( s ) ) ;
2171
+ const sessions = response . getSessionsList ( ) . map ( ( s ) => UsageService . mapBilledSession ( s ) ) ;
2196
2172
2197
2173
return sessions ;
2198
2174
}
@@ -2233,28 +2209,6 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
2233
2209
await this . guardAccess ( { kind : "costCenter" , /*subject: costCenter,*/ owner } , operation ) ;
2234
2210
}
2235
2211
2236
- protected mapBilledSession ( s : usage . BilledSession ) : BillableSession {
2237
- function mandatory < T > ( v : T , m : ( v : T ) => string = ( s ) => "" + s ) : string {
2238
- if ( ! v ) {
2239
- throw new Error ( `Empty value in usage.BilledSession for instanceId '${ s . getInstanceId ( ) } '` ) ;
2240
- }
2241
- return m ( v ) ;
2242
- }
2243
- return {
2244
- attributionId : mandatory ( s . getAttributionId ( ) ) ,
2245
- userId : s . getUserId ( ) || undefined ,
2246
- teamId : s . getTeamId ( ) || undefined ,
2247
- projectId : s . getProjectId ( ) || undefined ,
2248
- workspaceId : mandatory ( s . getWorkspaceId ( ) ) ,
2249
- instanceId : mandatory ( s . getInstanceId ( ) ) ,
2250
- workspaceType : mandatory ( s . getWorkspaceType ( ) ) as WorkspaceType ,
2251
- workspaceClass : s . getWorkspaceClass ( ) ,
2252
- startTime : mandatory ( s . getStartTime ( ) , ( t ) => t ! . toDate ( ) . toISOString ( ) ) ,
2253
- endTime : s . getEndTime ( ) ?. toDate ( ) . toISOString ( ) ,
2254
- credits : s . getCredits ( ) , // optional
2255
- } ;
2256
- }
2257
-
2258
2212
async getBillingModeForUser ( ctx : TraceContextWithSpan ) : Promise < BillingMode > {
2259
2213
traceAPIParams ( ctx , { } ) ;
2260
2214
0 commit comments