Skip to content

Commit 2e8bf4a

Browse files
committed
[server] Allow setting ws-class on project level
1 parent 1b48aa9 commit 2e8bf4a

File tree

7 files changed

+63
-15
lines changed

7 files changed

+63
-15
lines changed

components/dashboard/src/projects/ProjectSettings.tsx

Lines changed: 24 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 { useContext } from "react";
7+
import { useContext, useEffect, useState } from "react";
88
import { useLocation } from "react-router";
99
import { Project, ProjectSettings, Team } from "@gitpod/gitpod-protocol";
1010
import CheckBox from "../components/CheckBox";
@@ -14,6 +14,8 @@ import { PageWithSubMenu } from "../components/PageWithSubMenu";
1414
import PillLabel from "../components/PillLabel";
1515
import { ProjectContext } from "./project-context";
1616
import { FeatureFlagContext } from "../contexts/FeatureFlagContext";
17+
import SelectWorkspaceClass from "../settings/selectClass";
18+
import { BillingMode } from "@gitpod/gitpod-protocol/lib/billing-mode";
1719

1820
export function getProjectSettingsMenu(project?: Project, team?: Team) {
1921
const teamOrUserSlug = !!team ? "t/" + team.slug : "projects";
@@ -48,6 +50,14 @@ export function ProjectSettingsPage(props: { project?: Project; children?: React
4850
export default function () {
4951
const { showPersistentVolumeClaimUI } = useContext(FeatureFlagContext);
5052
const { project, setProject } = useContext(ProjectContext);
53+
const [teamBillingMode, setTeamBillingMode] = useState<BillingMode | undefined>(undefined);
54+
const { teams } = useContext(TeamsContext);
55+
const team = getCurrentTeam(useLocation(), teams);
56+
useEffect(() => {
57+
if (team) {
58+
getGitpodService().server.getBillingModeForTeam(team.id).then(setTeamBillingMode);
59+
}
60+
}, [team]);
5161

5262
if (!project) return null;
5363

@@ -59,6 +69,15 @@ export default function () {
5969
setProject({ ...project, settings: newSettings });
6070
};
6171

72+
const setWorkspaceClass = async (value: string) => {
73+
if (!project) {
74+
return value;
75+
}
76+
const before = project.settings?.workspaceClasses?.regular;
77+
updateProjectSettings({ workspaceClasses: { prebuild: value, regular: value } });
78+
return before;
79+
};
80+
6281
return (
6382
<ProjectSettingsPage project={project}>
6483
<h3>Prebuilds</h3>
@@ -144,6 +163,10 @@ export default function () {
144163
<>
145164
<br></br>
146165
<h3 className="mt-12">Workspaces</h3>
166+
<SelectWorkspaceClass
167+
enabled={BillingMode.canSetWorkspaceClass(teamBillingMode)}
168+
setWorkspaceClass={setWorkspaceClass}
169+
/>
147170
<CheckBox
148171
title={
149172
<span>

components/dashboard/src/settings/Preferences.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import SelectIDE from "./SelectIDE";
1414
import SelectWorkspaceClass from "./selectClass";
1515
import { PageWithSettingsSubMenu } from "./PageWithSettingsSubMenu";
1616
import { BillingMode } from "@gitpod/gitpod-protocol/lib/billing-mode";
17+
import { WorkspaceClasses } from "@gitpod/gitpod-protocol";
1718

1819
type Theme = "light" | "dark" | "system";
1920

@@ -49,13 +50,29 @@ export default function Preferences() {
4950
}
5051
};
5152

53+
const setWorkspaceClass = async (value: string) => {
54+
const additionalData = user?.additionalData || {};
55+
const prevWorkspaceClass = additionalData?.workspaceClasses?.regular;
56+
const workspaceClasses = (additionalData?.workspaceClasses || {}) as WorkspaceClasses;
57+
workspaceClasses.regular = value;
58+
workspaceClasses.prebuild = value;
59+
additionalData.workspaceClasses = workspaceClasses;
60+
if (value !== prevWorkspaceClass) {
61+
await getGitpodService().server.updateLoggedInUser({ additionalData });
62+
}
63+
return prevWorkspaceClass;
64+
};
65+
5266
return (
5367
<div>
5468
<PageWithSettingsSubMenu title="Preferences" subtitle="Configure user preferences.">
5569
<h3>Editor</h3>
5670
<p className="text-base text-gray-500 dark:text-gray-400">Choose the editor for opening workspaces.</p>
5771
<SelectIDE location="preferences" />
58-
<SelectWorkspaceClass enabled={BillingMode.canSetWorkspaceClass(userBillingMode)} />
72+
<SelectWorkspaceClass
73+
enabled={BillingMode.canSetWorkspaceClass(userBillingMode)}
74+
setWorkspaceClass={setWorkspaceClass}
75+
/>
5976
<h3 className="mt-12">Theme</h3>
6077
<p className="text-base text-gray-500 dark:text-gray-400">Early bird or night owl? Choose your side.</p>
6178
<div className="mt-4 space-x-3 flex">

components/dashboard/src/settings/selectClass.tsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,23 @@ import { useContext, useEffect, useState } from "react";
88
import { getGitpodService } from "../service/service";
99
import { UserContext } from "../user-context";
1010
import { trackEvent } from "../Analytics";
11-
import { WorkspaceClasses } from "@gitpod/gitpod-protocol";
1211
import WorkspaceClass from "../components/WorkspaceClass";
1312
import { SupportedWorkspaceClass } from "@gitpod/gitpod-protocol/lib/workspace-class";
1413

1514
interface SelectWorkspaceClassProps {
1615
enabled: boolean;
16+
setWorkspaceClass: (value: string) => Promise<string | undefined>;
1717
}
1818

1919
export default function SelectWorkspaceClass(props: SelectWorkspaceClassProps) {
2020
const { user } = useContext(UserContext);
2121

2222
const [workspaceClass, setWorkspaceClass] = useState<string>(user?.additionalData?.workspaceClasses?.regular || "");
2323
const actuallySetWorkspaceClass = async (value: string) => {
24-
const additionalData = user?.additionalData || {};
25-
const prevWorkspaceClass = additionalData?.workspaceClasses?.regular || "";
26-
const workspaceClasses = (additionalData?.workspaceClasses || {}) as WorkspaceClasses;
27-
workspaceClasses.regular = value;
28-
workspaceClasses.prebuild = value;
29-
additionalData.workspaceClasses = workspaceClasses;
30-
if (value !== prevWorkspaceClass) {
31-
await getGitpodService().server.updateLoggedInUser({ additionalData });
24+
const previousValue = await props.setWorkspaceClass(value);
25+
if (previousValue !== value) {
3226
trackEvent("workspace_class_changed", {
33-
previous: prevWorkspaceClass,
27+
previous: previousValue,
3428
current: value,
3529
});
3630
setWorkspaceClass(value);

components/gitpod-protocol/src/teams-projects-protocol.ts

Lines changed: 3 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 { PrebuiltWorkspaceState } from "./protocol";
7+
import { PrebuiltWorkspaceState, WorkspaceClasses } from "./protocol";
88
import { v4 as uuidv4 } from "uuid";
99
import { DeepPartial } from "./util/deep-partial";
1010
import { WebhookEvent } from "./webhook-event";
@@ -21,6 +21,8 @@ export interface ProjectSettings {
2121
allowUsingPreviousPrebuilds?: boolean;
2222
// how many commits in the commit history a prebuild is good (undefined and 0 means every commit is prebuilt)
2323
prebuildEveryNthCommit?: number;
24+
// preferred workspace classes
25+
workspaceClasses?: WorkspaceClasses;
2426
}
2527

2628
export interface Project {

components/server/src/workspace/gitpod-server-impl.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2980,6 +2980,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
29802980
let user = this.checkAndBlockUser("getSupportedWorkspaceClasses");
29812981
let selectedClass = await WorkspaceClasses.getConfiguredOrUpgradeFromLegacy(
29822982
user,
2983+
undefined,
29832984
this.config.workspaceClasses,
29842985
this.entitlementService,
29852986
);

components/server/src/workspace/workspace-classes.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import { WorkspaceDB } from "@gitpod/gitpod-db/lib";
8-
import { User, Workspace } from "@gitpod/gitpod-protocol";
8+
import { Project, User, Workspace } from "@gitpod/gitpod-protocol";
99
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
1010
import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing";
1111
import { EntitlementService } from "../billing/entitlement-service";
@@ -159,9 +159,13 @@ export namespace WorkspaceClasses {
159159
*/
160160
export async function getConfiguredOrUpgradeFromLegacy(
161161
user: User,
162+
project: Project | undefined,
162163
classes: WorkspaceClassesConfig,
163164
entitlementService: EntitlementService,
164165
): Promise<string> {
166+
if (project?.settings?.workspaceClasses?.regular) {
167+
return project?.settings?.workspaceClasses?.regular;
168+
}
165169
if (user.additionalData?.workspaceClasses?.regular) {
166170
return user.additionalData?.workspaceClasses?.regular;
167171
}

components/server/src/workspace/workspace-starter.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import {
6060
WithReferrerContext,
6161
EnvVarWithValue,
6262
BillingTier,
63+
Project,
6364
} from "@gitpod/gitpod-protocol";
6465
import { IAnalyticsWriter } from "@gitpod/gitpod-protocol/lib/analytics";
6566
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
@@ -204,6 +205,7 @@ export async function getWorkspaceClassForInstance(
204205
workspace: Workspace,
205206
previousInstance: WorkspaceInstance | undefined,
206207
user: User,
208+
project: Project | undefined,
207209
entitlementService: EntitlementService,
208210
config: WorkspaceClassesConfig,
209211
workspaceDb: DBWithTracing<WorkspaceDB>,
@@ -217,17 +219,22 @@ export async function getWorkspaceClassForInstance(
217219
if (prebuildClass) {
218220
const userClass = await WorkspaceClasses.getConfiguredOrUpgradeFromLegacy(
219221
user,
222+
project,
220223
config,
221224
entitlementService,
222225
);
223226
workspaceClass = WorkspaceClasses.selectClassForRegular(prebuildClass, userClass, config);
227+
} else if (project?.settings?.workspaceClasses?.regular) {
228+
workspaceClass = project?.settings?.workspaceClasses?.regular;
224229
} else if (user.additionalData?.workspaceClasses?.regular) {
225230
workspaceClass = user.additionalData?.workspaceClasses?.regular;
226231
}
227232
}
228233

229234
if (workspace.type == "prebuild") {
230-
if (user.additionalData?.workspaceClasses?.prebuild) {
235+
if (project?.settings?.workspaceClasses?.prebuild) {
236+
workspaceClass = project?.settings?.workspaceClasses?.prebuild;
237+
} else if (user.additionalData?.workspaceClasses?.prebuild) {
231238
workspaceClass = user.additionalData?.workspaceClasses?.prebuild;
232239
}
233240
}

0 commit comments

Comments
 (0)