Skip to content

Unblock Self-Hosted release by introducing UsageServiceMock (II) #14173

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 7 additions & 14 deletions components/server/ee/src/billing/billing-mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down
16 changes: 5 additions & 11 deletions components/server/ee/src/billing/entitlement-service-ubp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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(
Expand Down Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions components/server/ee/src/container-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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>(Config);
if (config.enablePayment) {
return ctx.container.get<UsageServiceImpl>(UsageServiceImpl);
}
return new NoOpUsageService();
})
.inSingletonScope();
});
5 changes: 3 additions & 2 deletions components/server/src/container-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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);
});
24 changes: 23 additions & 1 deletion components/server/src/user/usage-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<CostCenter_BillingStrategy | undefined>;
}

@injectable()
export class UsageService {
export class UsageServiceImpl implements UsageService {
@inject(UsageServiceDefinition.name)
protected readonly usageService: UsageServiceClient;

Expand All @@ -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<CostCenter_BillingStrategy | undefined> {
return CostCenter_BillingStrategy.BILLING_STRATEGY_OTHER;
}
}
14 changes: 3 additions & 11 deletions components/server/src/user/user-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 <authHost>/<authName> and returns the matching User
Expand Down Expand Up @@ -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<void> {
Expand Down