Skip to content

Commit 57a61f4

Browse files
geroplroboquat
authored andcommitted
[server] Introduce EntitlementService.getBillingTier
1 parent 782a052 commit 57a61f4

File tree

8 files changed

+69
-3
lines changed

8 files changed

+69
-3
lines changed

components/gitpod-protocol/src/experiments/configcat.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const TEAM_ID_ATTRIBUTE = "team_id";
1515
export const TEAM_IDS_ATTRIBUTE = "team_ids";
1616
export const TEAM_NAME_ATTRIBUTE = "team_name";
1717
export const TEAM_NAMES_ATTRIBUTE = "team_names";
18+
export const BILLING_TIER_ATTRIBUTE = "billing_tier";
1819

1920
export class ConfigCatClient implements Client {
2021
private client: IConfigCatClient;
@@ -53,6 +54,9 @@ export function attributesToUser(attributes: Attributes): ConfigCatUser {
5354
custom[TEAM_NAMES_ATTRIBUTE] = attributes.teams.map((t) => t.name).join(",");
5455
custom[TEAM_IDS_ATTRIBUTE] = attributes.teams.map((t) => t.id).join(",");
5556
}
57+
if (attributes.billingTier) {
58+
custom[BILLING_TIER_ATTRIBUTE] = attributes.billingTier;
59+
}
5660

5761
return new ConfigCatUser(userId, email, "", custom);
5862
}

components/gitpod-protocol/src/experiments/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* See License-AGPL.txt in the project root for license information.
55
*/
66

7-
import { User } from "../protocol";
7+
import { BillingTier, User } from "../protocol";
88
import { Team } from "../teams-projects-protocol";
99

1010
export const Client = Symbol("Client");
@@ -15,6 +15,9 @@ export interface Attributes {
1515
// user.id is mapped to ConfigCat's "identifier" + "custom.user_id"
1616
user?: User | { id: string; email?: string };
1717

18+
// The BillingTier of this particular user
19+
billingTier?: BillingTier;
20+
1821
// Currently selected Gitpod Project ID (mapped to "custom.project_id")
1922
projectId?: string;
2023

components/gitpod-protocol/src/protocol.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,8 @@ export interface UserFeatureSettings {
286286
permanentWSFeatureFlags?: NamedWorkspaceFeatureFlag[];
287287
}
288288

289+
export type BillingTier = "paid" | "free";
290+
289291
/**
290292
* The values of this type MUST MATCH enum values in WorkspaceFeatureFlag from ws-manager/client/core_pb.d.ts
291293
* If they don't we'll break things during workspace startup.

components/server/ee/src/billing/entitlement-service-chargebee.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import { TeamDB, TeamSubscription2DB, TeamSubscriptionDB } from "@gitpod/gitpod-db/lib";
88
import { Accounting, SubscriptionService } from "@gitpod/gitpod-payment-endpoint/lib/accounting";
99
import {
10+
BillingTier,
1011
User,
1112
WorkspaceInstance,
1213
WorkspaceTimeoutDuration,
@@ -188,8 +189,17 @@ export class EntitlementServiceChargebee implements EntitlementService {
188189
* @param user
189190
*/
190191
async limitNetworkConnections(user: User, date: Date = new Date()): Promise<boolean> {
191-
const subscriptions = await this.subscriptionService.getNotYetCancelledSubscriptions(user, date.toISOString());
192-
const hasPaidPlan = subscriptions.some((s) => !Plans.isFreeTier(s.planId));
192+
const hasPaidPlan = this.hasPaidSubscription(user, date);
193193
return !hasPaidPlan;
194194
}
195+
196+
protected async hasPaidSubscription(user: User, date: Date): Promise<boolean> {
197+
const subscriptions = await this.subscriptionService.getNotYetCancelledSubscriptions(user, date.toISOString());
198+
return subscriptions.some((s) => !Plans.isFreeTier(s.planId));
199+
}
200+
201+
async getBillingTier(user: User): Promise<BillingTier> {
202+
const hasPaidPlan = await this.hasPaidSubscription(user, new Date());
203+
return hasPaidPlan ? "paid" : "free";
204+
}
195205
}

components/server/ee/src/billing/entitlement-service-license.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import { UserDB } from "@gitpod/gitpod-db/lib";
88
import {
9+
BillingTier,
910
User,
1011
WorkspaceInstance,
1112
WorkspaceTimeoutDuration,
@@ -61,4 +62,9 @@ export class EntitlementServiceLicense implements EntitlementService {
6162
async limitNetworkConnections(user: User, date: Date): Promise<boolean> {
6263
return false;
6364
}
65+
66+
async getBillingTier(user: User): Promise<BillingTier> {
67+
// TODO(gpl) Is this true? Cross-check this whole interface with Self-Hosted before next release!
68+
return "paid";
69+
}
6470
}

components/server/ee/src/billing/entitlement-service-ubp.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import { TeamDB, UserDB } from "@gitpod/gitpod-db/lib";
88
import {
9+
BillingTier,
910
Team,
1011
User,
1112
WorkspaceInstance,
@@ -146,4 +147,9 @@ export class EntitlementServiceUBP implements EntitlementService {
146147
.catch(reject);
147148
});
148149
}
150+
151+
async getBillingTier(user: User): Promise<BillingTier> {
152+
const hasPaidPlan = await this.hasPaidSubscription(user, new Date());
153+
return hasPaidPlan ? "paid" : "free";
154+
}
149155
}

components/server/ee/src/billing/entitlement-service.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
import {
8+
BillingTier,
89
User,
910
WorkspaceInstance,
1011
WorkspaceTimeoutDuration,
@@ -134,4 +135,26 @@ export class EntitlementServiceImpl implements EntitlementService {
134135
return false;
135136
}
136137
}
138+
139+
/**
140+
* Returns true if network connections should be limited
141+
* @param user
142+
*/
143+
async getBillingTier(user: User): Promise<BillingTier> {
144+
try {
145+
const now = new Date();
146+
const billingMode = await this.billingModes.getBillingModeForUser(user, now);
147+
switch (billingMode.mode) {
148+
case "none":
149+
return this.license.getBillingTier(user);
150+
case "chargebee":
151+
return this.chargebee.getBillingTier(user);
152+
case "usage-based":
153+
return this.ubp.getBillingTier(user);
154+
}
155+
} catch (err) {
156+
log.error({ userId: user.id }, "EntitlementService error: getBillingTier", err);
157+
return "paid";
158+
}
159+
}
137160
}

components/server/src/billing/entitlement-service.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
WORKSPACE_TIMEOUT_DEFAULT_SHORT,
1212
} from "@gitpod/gitpod-protocol";
1313
import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution";
14+
import { BillingTier } from "@gitpod/gitpod-protocol/src/protocol";
1415
import { injectable } from "inversify";
1516

1617
export interface MayStartWorkspaceResult {
@@ -68,6 +69,13 @@ export interface EntitlementService {
6869
* @param user
6970
*/
7071
limitNetworkConnections(user: User, date: Date): Promise<boolean>;
72+
73+
/**
74+
* Returns BillingTier of this particular user
75+
*
76+
* @param user
77+
*/
78+
getBillingTier(user: User): Promise<BillingTier>;
7179
}
7280

7381
/**
@@ -98,4 +106,8 @@ export class CommunityEntitlementService implements EntitlementService {
98106
async limitNetworkConnections(user: User): Promise<boolean> {
99107
return false;
100108
}
109+
110+
async getBillingTier(user: User): Promise<BillingTier> {
111+
return "free";
112+
}
101113
}

0 commit comments

Comments
 (0)