Skip to content

Commit f6d4908

Browse files
committed
[dashboard] List projects using Public API
1 parent f5e88e5 commit f6d4908

File tree

9 files changed

+153
-26
lines changed

9 files changed

+153
-26
lines changed

components/dashboard/src/Menu.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { inResource, isGitpodIo } from "./utils";
2828
import { BillingMode } from "@gitpod/gitpod-protocol/lib/billing-mode";
2929
import { FeatureFlagContext } from "./contexts/FeatureFlagContext";
3030
import { publicApiTeamMembersToProtocol, teamsService } from "./service/public-api";
31+
import { listAllProjects } from "./service/public-api";
3132

3233
interface Entry {
3334
title: string;
@@ -37,7 +38,7 @@ interface Entry {
3738

3839
export default function Menu() {
3940
const { user } = useContext(UserContext);
40-
const { showUsageView, usePublicApiTeamsService } = useContext(FeatureFlagContext);
41+
const { showUsageView, usePublicApiTeamsService, usePublicApiProjectsService } = useContext(FeatureFlagContext);
4142
const { teams } = useContext(TeamsContext);
4243
const location = useLocation();
4344
const team = getCurrentTeam(location, teams);
@@ -148,9 +149,16 @@ export default function Menu() {
148149
return;
149150
}
150151
(async () => {
151-
const projects = !!team
152-
? await getGitpodService().server.getTeamProjects(team.id)
153-
: await getGitpodService().server.getUserProjects();
152+
let projects: Project[];
153+
if (!!team) {
154+
projects = usePublicApiProjectsService
155+
? await listAllProjects({ teamId: team.id })
156+
: await getGitpodService().server.getTeamProjects(team.id);
157+
} else {
158+
projects = usePublicApiProjectsService
159+
? await listAllProjects({ userId: user?.id })
160+
: await getGitpodService().server.getUserProjects();
161+
}
154162

155163
// Find project matching with slug, otherwise with name
156164
const project =

components/dashboard/src/contexts/FeatureFlagContext.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ const FeatureFlagContext = createContext<{
1919
showUsageView: boolean;
2020
showUseLastSuccessfulPrebuild: boolean;
2121
usePublicApiTeamsService: boolean;
22+
usePublicApiProjectsService: boolean;
2223
enablePersonalAccessTokens: boolean;
2324
}>({
2425
showUsageView: false,
2526
showUseLastSuccessfulPrebuild: false,
2627
usePublicApiTeamsService: false,
28+
usePublicApiProjectsService: false,
2729
enablePersonalAccessTokens: false,
2830
});
2931

@@ -36,6 +38,7 @@ const FeatureFlagContextProvider: React.FC = ({ children }) => {
3638
const [showUsageView, setShowUsageView] = useState<boolean>(false);
3739
const [showUseLastSuccessfulPrebuild, setShowUseLastSuccessfulPrebuild] = useState<boolean>(false);
3840
const [usePublicApiTeamsService, setUsePublicApiTeamsService] = useState<boolean>(false);
41+
const [usePublicApiProjectsService, setUsePublicApiProjectsService] = useState<boolean>(false);
3942
const [enablePersonalAccessTokens, setPersonalAccessTokensEnabled] = useState<boolean>(false);
4043

4144
useEffect(() => {
@@ -45,6 +48,7 @@ const FeatureFlagContextProvider: React.FC = ({ children }) => {
4548
usage_view: { defaultValue: false, setter: setShowUsageView },
4649
showUseLastSuccessfulPrebuild: { defaultValue: false, setter: setShowUseLastSuccessfulPrebuild },
4750
publicApiExperimentalTeamsService: { defaultValue: false, setter: setUsePublicApiTeamsService },
51+
publicApiExperimentalProjectsService: { defaultValue: false, setter: setUsePublicApiProjectsService },
4852
personalAccessTokensEnabled: { defaultValue: false, setter: setPersonalAccessTokensEnabled },
4953
};
5054

@@ -89,6 +93,7 @@ const FeatureFlagContextProvider: React.FC = ({ children }) => {
8993
showUseLastSuccessfulPrebuild,
9094
usePublicApiTeamsService,
9195
enablePersonalAccessTokens,
96+
usePublicApiProjectsService,
9297
}}
9398
>
9499
{children}

components/dashboard/src/projects/Events.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,16 @@ import Spinner from "../icons/Spinner.svg";
1717
import NoAccess from "../icons/NoAccess.svg";
1818
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
1919
import { openAuthorizeWindow } from "../provider-utils";
20+
import { UserContext } from "../user-context";
21+
import { FeatureFlagContext } from "../contexts/FeatureFlagContext";
22+
import { listAllProjects } from "../service/public-api";
2023

2124
export default function () {
2225
const location = useLocation();
2326

2427
const { teams } = useContext(TeamsContext);
28+
const { user } = useContext(UserContext);
29+
const { usePublicApiProjectsService } = useContext(FeatureFlagContext);
2530
const team = getCurrentTeam(location, teams);
2631

2732
const match = useRouteMatch<{ team: string; resource: string }>("/(t/)?:team/:resource");
@@ -61,9 +66,16 @@ export default function () {
6166
if (!teams || !projectSlug) {
6267
return;
6368
}
64-
const projects = !!team
65-
? await getGitpodService().server.getTeamProjects(team.id)
66-
: await getGitpodService().server.getUserProjects();
69+
let projects: Project[];
70+
if (!!team) {
71+
projects = usePublicApiProjectsService
72+
? await listAllProjects({ teamId: team.id })
73+
: await getGitpodService().server.getTeamProjects(team.id);
74+
} else {
75+
projects = usePublicApiProjectsService
76+
? await listAllProjects({ userId: user?.id })
77+
: await getGitpodService().server.getUserProjects();
78+
}
6779

6880
// Find project matching with slug, otherwise with name
6981
const project = projectSlug && projects.find((p) => (p.slug ? p.slug === projectSlug : p.name === projectSlug));

components/dashboard/src/projects/Prebuild.tsx

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

77
import dayjs from "dayjs";
8-
import { PrebuildWithStatus } from "@gitpod/gitpod-protocol";
8+
import { PrebuildWithStatus, Project } from "@gitpod/gitpod-protocol";
99
import { useContext, useEffect, useState } from "react";
1010
import { useHistory, useLocation, useRouteMatch } from "react-router";
1111
import Header from "../components/Header";
@@ -14,12 +14,17 @@ import Spinner from "../icons/Spinner.svg";
1414
import { getGitpodService, gitpodHostUrl } from "../service/service";
1515
import { TeamsContext, getCurrentTeam } from "../teams/teams-context";
1616
import { shortCommitMessage } from "./render-utils";
17+
import { listAllProjects } from "../service/public-api";
18+
import { UserContext } from "../user-context";
19+
import { FeatureFlagContext } from "../contexts/FeatureFlagContext";
1720

1821
export default function () {
1922
const history = useHistory();
2023
const location = useLocation();
2124

2225
const { teams } = useContext(TeamsContext);
26+
const { user } = useContext(UserContext);
27+
const { usePublicApiProjectsService } = useContext(FeatureFlagContext);
2328
const team = getCurrentTeam(location, teams);
2429

2530
const match = useRouteMatch<{ team: string; project: string; prebuildId: string }>(
@@ -37,9 +42,16 @@ export default function () {
3742
return;
3843
}
3944
(async () => {
40-
const projects = !!team
41-
? await getGitpodService().server.getTeamProjects(team.id)
42-
: await getGitpodService().server.getUserProjects();
45+
let projects: Project[];
46+
if (!!team) {
47+
projects = usePublicApiProjectsService
48+
? await listAllProjects({ teamId: team.id })
49+
: await getGitpodService().server.getTeamProjects(team.id);
50+
} else {
51+
projects = usePublicApiProjectsService
52+
? await listAllProjects({ userId: user?.id })
53+
: await getGitpodService().server.getUserProjects();
54+
}
4355

4456
const project =
4557
projectSlug && projects.find((p) => (!!p.slug ? p.slug === projectSlug : p.name === projectSlug));

components/dashboard/src/projects/Prebuilds.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,16 @@ import { TeamsContext, getCurrentTeam } from "../teams/teams-context";
2222
import { shortCommitMessage } from "./render-utils";
2323
import { Link } from "react-router-dom";
2424
import { Disposable } from "vscode-jsonrpc";
25+
import { UserContext } from "../user-context";
26+
import { FeatureFlagContext } from "../contexts/FeatureFlagContext";
27+
import { listAllProjects } from "../service/public-api";
2528

2629
export default function (props: { project?: Project; isAdminDashboard?: boolean }) {
2730
const location = useLocation();
2831

2932
const { teams } = useContext(TeamsContext);
33+
const { user } = useContext(UserContext);
34+
const { usePublicApiProjectsService } = useContext(FeatureFlagContext);
3035
const team = getCurrentTeam(location, teams);
3136

3237
const match = useRouteMatch<{ team: string; resource: string }>("/(t/)?:team/:resource");
@@ -84,10 +89,16 @@ export default function (props: { project?: Project; isAdminDashboard?: boolean
8489
return;
8590
}
8691
(async () => {
87-
const projects = !!team
88-
? await getGitpodService().server.getTeamProjects(team.id)
89-
: await getGitpodService().server.getUserProjects();
90-
92+
let projects: Project[];
93+
if (!!team) {
94+
projects = usePublicApiProjectsService
95+
? await listAllProjects({ teamId: team.id })
96+
: await getGitpodService().server.getTeamProjects(team.id);
97+
} else {
98+
projects = usePublicApiProjectsService
99+
? await listAllProjects({ userId: user?.id })
100+
: await getGitpodService().server.getUserProjects();
101+
}
91102
const newProject =
92103
projectSlug && projects.find((p) => (p.slug ? p.slug === projectSlug : p.name === projectSlug));
93104

components/dashboard/src/projects/Project.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,17 @@ import NoAccess from "../icons/NoAccess.svg";
1919
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
2020
import { openAuthorizeWindow } from "../provider-utils";
2121
import Alert from "../components/Alert";
22+
import { FeatureFlagContext } from "../contexts/FeatureFlagContext";
23+
import { listAllProjects } from "../service/public-api";
24+
import { UserContext } from "../user-context";
2225

2326
export default function () {
2427
const location = useLocation();
2528
const history = useHistory();
2629

2730
const { teams } = useContext(TeamsContext);
31+
const { user } = useContext(UserContext);
32+
const { usePublicApiProjectsService } = useContext(FeatureFlagContext);
2833
const team = getCurrentTeam(location, teams);
2934

3035
const match = useRouteMatch<{ team: string; resource: string }>("/(t/)?:team/:resource");
@@ -69,10 +74,16 @@ export default function () {
6974
if (!teams || !projectSlug) {
7075
return;
7176
}
72-
const projects = !!team
73-
? await getGitpodService().server.getTeamProjects(team.id)
74-
: await getGitpodService().server.getUserProjects();
75-
77+
let projects: Project[];
78+
if (!!team) {
79+
projects = usePublicApiProjectsService
80+
? await listAllProjects({ teamId: team.id })
81+
: await getGitpodService().server.getTeamProjects(team.id);
82+
} else {
83+
projects = usePublicApiProjectsService
84+
? await listAllProjects({ userId: user?.id })
85+
: await getGitpodService().server.getUserProjects();
86+
}
7687
// Find project matching with slug, otherwise with name
7788
const project = projectSlug && projects.find((p) => (p.slug ? p.slug === projectSlug : p.name === projectSlug));
7889

components/dashboard/src/projects/Projects.tsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,17 @@ import ContextMenu from "../components/ContextMenu";
2020
import ConfirmationModal from "../components/ConfirmationModal";
2121
import { prebuildStatusIcon } from "./Prebuilds";
2222
import Alert from "../components/Alert";
23+
import { FeatureFlagContext } from "../contexts/FeatureFlagContext";
24+
import { listAllProjects } from "../service/public-api";
25+
import { UserContext } from "../user-context";
2326

2427
export default function () {
2528
const location = useLocation();
2629
const history = useHistory();
2730

2831
const { teams } = useContext(TeamsContext);
32+
const { user } = useContext(UserContext);
33+
const { usePublicApiProjectsService } = useContext(FeatureFlagContext);
2934
const team = getCurrentTeam(location, teams);
3035
const [projects, setProjects] = useState<Project[]>([]);
3136
const [lastPrebuilds, setLastPrebuilds] = useState<Map<string, PrebuildWithStatus>>(new Map());
@@ -42,9 +47,17 @@ export default function () {
4247
if (!teams) {
4348
return;
4449
}
45-
const infos = !!team
46-
? await getGitpodService().server.getTeamProjects(team.id)
47-
: await getGitpodService().server.getUserProjects();
50+
51+
let infos: Project[];
52+
if (!!team) {
53+
infos = usePublicApiProjectsService
54+
? await listAllProjects({ teamId: team.id })
55+
: await getGitpodService().server.getTeamProjects(team.id);
56+
} else {
57+
infos = usePublicApiProjectsService
58+
? await listAllProjects({ userId: user?.id })
59+
: await getGitpodService().server.getUserProjects();
60+
}
4861
setProjects(infos);
4962

5063
const map = new Map();

components/dashboard/src/service/public-api.ts

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

77
import { createConnectTransport, createPromiseClient } from "@bufbuild/connect-web";
8-
import { Team as ProtocolTeam } from "@gitpod/gitpod-protocol/lib/teams-projects-protocol";
8+
import { Project as ProtocolProject, Team as ProtocolTeam } from "@gitpod/gitpod-protocol/lib/teams-projects-protocol";
99
import { TeamsService } from "@gitpod/public-api/lib/gitpod/experimental/v1/teams_connectweb";
1010
import { TokensService } from "@gitpod/public-api/lib/gitpod/experimental/v1/tokens_connectweb";
11+
import { ProjectsService } from "@gitpod/public-api/lib/gitpod/experimental/v1/projects_connectweb";
1112
import { Team } from "@gitpod/public-api/lib/gitpod/experimental/v1/teams_pb";
1213
import { TeamMemberInfo, TeamMemberRole } from "@gitpod/gitpod-protocol";
1314
import { TeamMember, TeamRole } from "@gitpod/public-api/lib/gitpod/experimental/v1/teams_pb";
15+
import { Project } from "@gitpod/public-api/lib/gitpod/experimental/v1/projects_pb";
1416

1517
const transport = createConnectTransport({
1618
baseUrl: `${window.location.protocol}//api.${window.location.host}`,
@@ -19,6 +21,7 @@ const transport = createConnectTransport({
1921

2022
export const teamsService = createPromiseClient(TeamsService, transport);
2123
export const personalAccessTokensService = createPromiseClient(TokensService, transport);
24+
export const projectsService = createPromiseClient(ProjectsService, transport);
2225

2326
export function publicApiTeamToProtocol(team: Team): ProtocolTeam {
2427
return {
@@ -58,3 +61,55 @@ export function publicApiTeamRoleToProtocol(role: TeamRole): TeamMemberRole {
5861
return "member";
5962
}
6063
}
64+
65+
export async function listAllProjects(opts: { userId?: string; teamId?: string }): Promise<ProtocolProject[]> {
66+
let pagination = {
67+
page: 1,
68+
pageSize: 100,
69+
};
70+
71+
const response = await projectsService.listProjects({
72+
teamId: opts.teamId,
73+
userId: opts.userId,
74+
pagination,
75+
});
76+
const results = response.projects;
77+
78+
while (results.length < response.totalResults) {
79+
pagination = {
80+
pageSize: 100,
81+
page: 1 + pagination.page,
82+
};
83+
const response = await projectsService.listProjects({
84+
teamId: opts.teamId,
85+
userId: opts.userId,
86+
pagination,
87+
});
88+
results.push(...response.projects);
89+
}
90+
91+
return results.map(projectToProtocol);
92+
}
93+
94+
export function projectToProtocol(project: Project): ProtocolProject {
95+
return {
96+
id: project.id,
97+
name: project.name,
98+
cloneUrl: project.cloneUrl,
99+
creationTime: project.creationTime?.toDate().toISOString() || "",
100+
slug: project.slug,
101+
teamId: project.teamId,
102+
userId: project.userId,
103+
appInstallationId: "undefined",
104+
settings: {
105+
allowUsingPreviousPrebuilds: project.settings?.prebuild?.usePreviousPrebuilds,
106+
keepOutdatedPrebuildsRunning: project.settings?.prebuild?.keepOutdatedPrebuildsRunning,
107+
prebuildEveryNthCommit: project.settings?.prebuild?.prebuildEveryNth,
108+
useIncrementalPrebuilds: project.settings?.prebuild?.enableIncrementalPrebuilds,
109+
workspaceClasses: {
110+
prebuild: project.settings?.workspace?.workspaceClass?.prebuild || "",
111+
regular: project.settings?.workspace?.workspaceClass?.regular || "",
112+
},
113+
},
114+
};
115+
}

components/gitpod-protocol/go/gitpod-service.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,7 +1540,7 @@ func (gp *APIoverJSONRPC) CreateProject(ctx context.Context, options *CreateProj
15401540
return
15411541
}
15421542
_params := []interface{}{options}
1543-
err = gp.C.Call(ctx, string(FunctionCreateProject), _params, nil)
1543+
err = gp.C.Call(ctx, string(FunctionCreateProject), _params, &res)
15441544
return
15451545
}
15461546

@@ -1560,7 +1560,7 @@ func (gp *APIoverJSONRPC) GetUserProjects(ctx context.Context) (res []*Project,
15601560
return
15611561
}
15621562
_params := []interface{}{}
1563-
err = gp.C.Call(ctx, string(FunctionGetUserProjects), _params, nil)
1563+
err = gp.C.Call(ctx, string(FunctionGetUserProjects), _params, &res)
15641564
return
15651565
}
15661566

@@ -1570,7 +1570,7 @@ func (gp *APIoverJSONRPC) GetTeamProjects(ctx context.Context, teamID string) (r
15701570
return
15711571
}
15721572
_params := []interface{}{teamID}
1573-
err = gp.C.Call(ctx, string(FunctionGetTeamProjects), _params, nil)
1573+
err = gp.C.Call(ctx, string(FunctionGetTeamProjects), _params, &res)
15741574
return
15751575
}
15761576

0 commit comments

Comments
 (0)