diff --git a/components/server/ee/src/billing/billing-mode.ts b/components/server/ee/src/billing/billing-mode.ts index c39eccef2c2cb4..a96ca8620343ba 100644 --- a/components/server/ee/src/billing/billing-mode.ts +++ b/components/server/ee/src/billing/billing-mode.ts @@ -16,11 +16,8 @@ import { TeamDB, TeamSubscription2DB, TeamSubscriptionDB, UserDB } from "@gitpod import { Plans } from "@gitpod/gitpod-protocol/lib/plans"; import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution"; import { TeamSubscription, TeamSubscription2 } from "@gitpod/gitpod-protocol/lib/team-subscription-protocol"; -import { - CostCenter_BillingStrategy, - UsageServiceClient, - UsageServiceDefinition, -} from "@gitpod/usage-api/lib/usage/v1/usage.pb"; +import { CostCenter_BillingStrategy } from "@gitpod/usage-api/lib/usage/v1/usage.pb"; +import { UsageService } from "../../../src/user/usage-service"; export const BillingModes = Symbol("BillingModes"); export interface BillingModes { @@ -45,7 +42,7 @@ export class BillingModesImpl implements BillingModes { @inject(Config) protected readonly config: Config; @inject(ConfigCatClientFactory) protected readonly configCatClientFactory: ConfigCatClientFactory; @inject(SubscriptionService) protected readonly subscriptionSvc: SubscriptionService; - @inject(UsageServiceDefinition.name) protected readonly usageService: UsageServiceClient; + @inject(UsageService) protected readonly usageService: UsageService; @inject(TeamSubscriptionDB) protected readonly teamSubscriptionDb: TeamSubscriptionDB; @inject(TeamSubscription2DB) protected readonly teamSubscription2Db: TeamSubscription2DB; @inject(TeamDB) protected readonly teamDB: TeamDB; @@ -140,10 +137,8 @@ export class BillingModesImpl implements BillingModes { // Stripe: Active personal subsciption? let hasUbbPersonal = false; - const constCenterResponse = await this.usageService.getCostCenter({ - attributionId: AttributionId.render({ kind: "user", userId: user.id }), - }); - if (constCenterResponse.costCenter?.billingStrategy === CostCenter_BillingStrategy.BILLING_STRATEGY_STRIPE) { + const billingStrategy = await this.usageService.getCurrentBillingStategy({ kind: "user", userId: user.id }); + if (billingStrategy === CostCenter_BillingStrategy.BILLING_STRATEGY_STRIPE) { hasUbbPersonal = true; } @@ -215,10 +210,8 @@ export class BillingModesImpl implements BillingModes { // 3. Now we're usage-based. We only have to figure out whether we have a plan yet or not. const result: BillingMode = { mode: "usage-based" }; - const costCenter = await this.usageService.getCostCenter({ - attributionId: AttributionId.render(AttributionId.create(team)), - }); - if (costCenter.costCenter?.billingStrategy === CostCenter_BillingStrategy.BILLING_STRATEGY_STRIPE) { + const billingStrategy = await this.usageService.getCurrentBillingStategy(AttributionId.create(team)); + if (billingStrategy === CostCenter_BillingStrategy.BILLING_STRATEGY_STRIPE) { result.paid = true; } return result; diff --git a/components/server/ee/src/billing/entitlement-service-ubp.ts b/components/server/ee/src/billing/entitlement-service-ubp.ts index ec8552b9a4b73d..6b57be1790bc56 100644 --- a/components/server/ee/src/billing/entitlement-service-ubp.ts +++ b/components/server/ee/src/billing/entitlement-service-ubp.ts @@ -26,11 +26,8 @@ import { Config } from "../../../src/config"; import { UserService } from "../../../src/user/user-service"; import { StripeService } from "../user/stripe-service"; import { BillingModes } from "./billing-mode"; -import { - CostCenter_BillingStrategy, - UsageServiceClient, - UsageServiceDefinition, -} from "@gitpod/usage-api/lib/usage/v1/usage.pb"; +import { CostCenter_BillingStrategy } from "@gitpod/usage-api/lib/usage/v1/usage.pb"; +import { UsageService } from "../../../src/user/usage-service"; const MAX_PARALLEL_WORKSPACES_FREE = 4; const MAX_PARALLEL_WORKSPACES_PAID = 16; @@ -45,7 +42,7 @@ export class EntitlementServiceUBP implements EntitlementService { @inject(BillingModes) protected readonly billingModes: BillingModes; @inject(UserService) protected readonly userService: UserService; @inject(StripeService) protected readonly stripeService: StripeService; - @inject(UsageServiceDefinition.name) protected readonly usageService: UsageServiceClient; + @inject(UsageService) protected readonly usageService: UsageService; @inject(TeamDB) protected readonly teamDB: TeamDB; async mayStartWorkspace( @@ -136,11 +133,8 @@ export class EntitlementServiceUBP implements EntitlementService { // Member of paid team? const teams = await this.teamDB.findTeamsByUser(user.id); const isTeamSubscribedPromises = teams.map(async (team: Team) => { - const costCenter = await this.usageService.getCostCenter({ - attributionId: AttributionId.render({ kind: "team", teamId: team.id }), - }); - - return costCenter.costCenter?.billingStrategy === CostCenter_BillingStrategy.BILLING_STRATEGY_STRIPE; + const billingStrategy = await this.usageService.getCurrentBillingStategy(AttributionId.create(team)); + return billingStrategy === CostCenter_BillingStrategy.BILLING_STRATEGY_STRIPE; }); // Return the first truthy promise, or false if all the promises were falsy. // Source: https://gist.github.com/jbreckmckye/66364021ebaa0785e426deec0410a235 diff --git a/components/server/ee/src/container-module.ts b/components/server/ee/src/container-module.ts index 7dce1de91e1912..8eb6049c1a79bf 100644 --- a/components/server/ee/src/container-module.ts +++ b/components/server/ee/src/container-module.ts @@ -65,6 +65,7 @@ import { BillingModes, BillingModesImpl } from "./billing/billing-mode"; import { EntitlementServiceLicense } from "./billing/entitlement-service-license"; import { EntitlementServiceImpl } from "./billing/entitlement-service"; import { EntitlementServiceUBP } from "./billing/entitlement-service-ubp"; +import { UsageService, UsageServiceImpl, NoOpUsageService } from "../../src/user/usage-service"; export const productionEEContainerModule = new ContainerModule((bind, unbind, isBound, rebind) => { rebind(Server).to(ServerEE).inSingletonScope(); @@ -132,4 +133,15 @@ export const productionEEContainerModule = new ContainerModule((bind, unbind, is bind(EntitlementServiceImpl).toSelf().inSingletonScope(); rebind(EntitlementService).to(EntitlementServiceImpl).inSingletonScope(); bind(BillingModes).to(BillingModesImpl).inSingletonScope(); + + // TODO(gpl) Remove as part of fixing https://github.com/gitpod-io/gitpod/issues/14129 + rebind(UsageService) + .toDynamicValue((ctx) => { + const config = ctx.container.get(Config); + if (config.enablePayment) { + return ctx.container.get(UsageServiceImpl); + } + return new NoOpUsageService(); + }) + .inSingletonScope(); }); diff --git a/components/server/src/container-module.ts b/components/server/src/container-module.ts index 2ca483c0b94568..e3423acae218d1 100644 --- a/components/server/src/container-module.ts +++ b/components/server/src/container-module.ts @@ -111,7 +111,7 @@ import { WebhookEventGarbageCollector } from "./projects/webhook-event-garbage-c import { LivenessController } from "./liveness/liveness-controller"; import { IDEServiceClient, IDEServiceDefinition } from "@gitpod/ide-service-api/lib/ide.pb"; import { prometheusClientMiddleware } from "@gitpod/gitpod-protocol/lib/util/nice-grpc"; -import { UsageService } from "./user/usage-service"; +import { UsageService, UsageServiceImpl } from "./user/usage-service"; import { OpenPrebuildPrefixContextParser } from "./workspace/open-prebuild-prefix-context-parser"; export const productionContainerModule = new ContainerModule((bind, unbind, isBound, rebind) => { @@ -297,5 +297,6 @@ export const productionContainerModule = new ContainerModule((bind, unbind, isBo bind(WebhookEventGarbageCollector).toSelf().inSingletonScope(); - bind(UsageService).toSelf().inSingletonScope(); + bind(UsageServiceImpl).toSelf().inSingletonScope(); + bind(UsageService).toService(UsageServiceImpl); }); diff --git a/components/server/src/user/usage-service.ts b/components/server/src/user/usage-service.ts index f94ba4807925d0..d0947797dadc07 100644 --- a/components/server/src/user/usage-service.ts +++ b/components/server/src/user/usage-service.ts @@ -12,8 +12,16 @@ import { } from "@gitpod/usage-api/lib/usage/v1/usage.pb"; import { inject, injectable } from "inversify"; +export const UsageService = Symbol("UsageService"); + +export interface UsageService { + getCurrentBalance(attributionId: AttributionId): Promise<{ usedCredits: number; usageLimit: number }>; + + getCurrentBillingStategy(attributionId: AttributionId): Promise; +} + @injectable() -export class UsageService { +export class UsageServiceImpl implements UsageService { @inject(UsageServiceDefinition.name) protected readonly usageService: UsageServiceClient; @@ -40,3 +48,17 @@ export class UsageService { return response.costCenter?.billingStrategy; } } + +// TODO(gpl) Remove as part of fixing https://github.com/gitpod-io/gitpod/issues/14129 +export class NoOpUsageService implements UsageService { + async getCurrentBalance(attributionId: AttributionId): Promise<{ usedCredits: number; usageLimit: number }> { + return { + usedCredits: 0, + usageLimit: 1000000000, + }; + } + + async getCurrentBillingStategy(attributionId: AttributionId): Promise { + return CostCenter_BillingStrategy.BILLING_STRATEGY_OTHER; + } +} diff --git a/components/server/src/user/user-service.ts b/components/server/src/user/user-service.ts index 29bae2c58569f7..cd07621e13c0a4 100644 --- a/components/server/src/user/user-service.ts +++ b/components/server/src/user/user-service.ts @@ -33,11 +33,7 @@ import { StripeService } from "../../ee/src/user/stripe-service"; import { ResponseError } from "vscode-ws-jsonrpc"; import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; import { UsageService } from "./usage-service"; -import { - CostCenter_BillingStrategy, - UsageServiceClient, - UsageServiceDefinition, -} from "@gitpod/usage-api/lib/usage/v1/usage.pb"; +import { CostCenter_BillingStrategy } from "@gitpod/usage-api/lib/usage/v1/usage.pb"; export interface FindUserByIdentityStrResult { user: User; @@ -84,8 +80,6 @@ export class UserService { @inject(TeamDB) protected readonly teamDB: TeamDB; @inject(StripeService) protected readonly stripeService: StripeService; @inject(UsageService) protected readonly usageService: UsageService; - @inject(UsageServiceDefinition.name) - protected readonly usageServiceClient: UsageServiceClient; /** * Takes strings in the form of / and returns the matching User @@ -327,10 +321,8 @@ export class UserService { if (attributionId.kind !== "team") { return false; } - const { costCenter } = await this.usageServiceClient.getCostCenter({ - attributionId: AttributionId.render(attributionId), - }); - return costCenter?.billingStrategy !== CostCenter_BillingStrategy.BILLING_STRATEGY_STRIPE; + const billingStrategy = await this.usageService.getCurrentBillingStategy(attributionId); + return billingStrategy !== CostCenter_BillingStrategy.BILLING_STRATEGY_STRIPE; } async setUsageAttribution(user: User, usageAttributionId: string): Promise {