Skip to content

Commit ac1cc39

Browse files
committed
[public-api] migrate PrebuildService
1 parent 0a5d89c commit ac1cc39

24 files changed

+1574
-923
lines changed

components/dashboard/src/components/PrebuildLogs.tsx

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ import {
1111
WorkspaceImageBuild,
1212
HEADLESS_LOG_STREAM_STATUS_CODE_REGEX,
1313
Disposable,
14-
PrebuildWithStatus,
1514
} from "@gitpod/gitpod-protocol";
1615
import { getGitpodService } from "../service/service";
1716
import { PrebuildStatus } from "../projects/Prebuilds";
18-
import { workspaceClient } from "../service/public-api";
17+
import { watchWorkspaceStatus } from "../data/workspaces/listen-to-workspace-ws-messages";
18+
import { prebuildClient, watchPrebuild, workspaceClient } from "../service/public-api";
1919
import { GetWorkspaceRequest, WorkspacePhase_Phase } from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";
20-
import { disposableWatchWorkspaceStatus } from "../data/workspaces/listen-to-workspace-ws-messages";
20+
import { Prebuild, PrebuildPhase_Phase } from "@gitpod/public-api/lib/gitpod/v1/prebuild_pb";
2121

2222
const WorkspaceLogs = React.lazy(() => import("./WorkspaceLogs"));
2323

@@ -38,15 +38,19 @@ export default function PrebuildLogs(props: PrebuildLogsProps) {
3838
>();
3939
const [error, setError] = useState<Error | undefined>();
4040
const [logsEmitter] = useState(new EventEmitter());
41-
const [prebuild, setPrebuild] = useState<PrebuildWithStatus | undefined>();
41+
const [prebuild, setPrebuild] = useState<Prebuild | undefined>();
4242

4343
const handlePrebuildUpdate = useCallback(
44-
(prebuild: PrebuildWithStatus) => {
45-
if (prebuild.info.buildWorkspaceId === props.workspaceId) {
44+
(prebuild: Prebuild) => {
45+
if (prebuild.workspaceId === props.workspaceId) {
4646
setPrebuild(prebuild);
4747

4848
// In case the Prebuild got "aborted" or "time(d)out" we want to user to proceed anyway
49-
if (props.onIgnorePrebuild && (prebuild.status === "aborted" || prebuild.status === "timeout")) {
49+
if (
50+
props.onIgnorePrebuild &&
51+
(prebuild.status?.phase?.name === PrebuildPhase_Phase.ABORTED ||
52+
prebuild.status?.phase?.name === PrebuildPhase_Phase.TIMEOUT)
53+
) {
5054
props.onIgnorePrebuild();
5155
}
5256
// TODO(gpl) We likely want to move the "happy path" logic (for status "available")
@@ -79,30 +83,17 @@ export default function PrebuildLogs(props: PrebuildLogsProps) {
7983
setError(err);
8084
}
8185

82-
// Try get hold of a recent Prebuild
83-
try {
84-
const pbws = await getGitpodService().server.findPrebuildByWorkspaceID(props.workspaceId);
85-
if (pbws) {
86-
const foundPrebuild = await getGitpodService().server.getPrebuild(pbws.id);
87-
if (foundPrebuild) {
88-
handlePrebuildUpdate(foundPrebuild);
89-
}
90-
}
91-
} catch (err) {
92-
console.error(err);
93-
setError(err);
94-
}
95-
96-
const watchDispose = disposableWatchWorkspaceStatus(props.workspaceId, (resp) => {
97-
if (resp.status?.instanceId && resp.status?.phase?.name) {
98-
setWorkspace({
99-
instanceId: resp.status.instanceId,
100-
phase: resp.status.phase.name,
101-
});
102-
}
103-
});
10486
// Register for future updates
105-
disposables.push(watchDispose);
87+
disposables.push(
88+
watchWorkspaceStatus(props.workspaceId, (resp) => {
89+
if (resp.status?.instanceId && resp.status?.phase?.name) {
90+
setWorkspace({
91+
instanceId: resp.status.instanceId,
92+
phase: resp.status.phase.name,
93+
});
94+
}
95+
}),
96+
);
10697
disposables.push(
10798
getGitpodService().registerClient({
10899
onWorkspaceImageBuildLogs: (
@@ -114,13 +105,32 @@ export default function PrebuildLogs(props: PrebuildLogsProps) {
114105
}
115106
logsEmitter.emit("logs", content.text);
116107
},
117-
onPrebuildUpdate(update: PrebuildWithStatus) {
118-
if (update.info) {
119-
handlePrebuildUpdate(update);
120-
}
121-
},
122108
}),
123109
);
110+
111+
try {
112+
const response = await prebuildClient.listPrebuilds({ workspaceId: props.workspaceId });
113+
const prebuild = response.prebuilds[0];
114+
if (prebuild) {
115+
handlePrebuildUpdate(prebuild);
116+
disposables.push(
117+
watchPrebuild(
118+
{
119+
scope: {
120+
case: "prebuildId",
121+
value: prebuild.id,
122+
},
123+
},
124+
handlePrebuildUpdate,
125+
),
126+
);
127+
} else {
128+
setError(new Error("Prebuild not found"));
129+
}
130+
} catch (err) {
131+
console.error(err);
132+
setError(err);
133+
}
124134
})();
125135
return function cleanup() {
126136
disposables.dispose();

components/dashboard/src/data/prebuilds/latest-project-prebuild-query.ts

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

7-
import { PrebuildWithStatus } from "@gitpod/gitpod-protocol";
87
import { useQuery } from "@tanstack/react-query";
9-
import { getGitpodService } from "../../service/service";
10-
11-
export type LatestProjectPrebuildQueryResult = PrebuildWithStatus;
8+
import { prebuildClient } from "../../service/public-api";
9+
import { Prebuild } from "@gitpod/public-api/lib/gitpod/v1/prebuild_pb";
10+
import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
1211

1312
type Args = {
1413
projectId: string;
1514
};
1615
export const useLatestProjectPrebuildQuery = ({ projectId }: Args) => {
17-
return useQuery<LatestProjectPrebuildQueryResult>({
16+
return useQuery<Prebuild | null>({
1817
queryKey: getLatestProjectPrebuildQueryKey(projectId),
1918
// Prevent bursting for latest project prebuilds too frequently
2019
staleTime: 1000 * 60 * 1, // 1 minute
2120
queryFn: async () => {
22-
const latestPrebuilds = await getGitpodService().server.findPrebuilds({
23-
projectId,
24-
latest: true,
25-
});
26-
27-
return latestPrebuilds[0] || null;
21+
try {
22+
const response = await prebuildClient.listPrebuilds({
23+
configurationId: projectId,
24+
pagination: {
25+
pageSize: 1,
26+
},
27+
});
28+
return response.prebuilds[0] || null;
29+
} catch (e) {
30+
if (ApplicationError.hasErrorCode(e) && e.code === ErrorCodes.NOT_FOUND) {
31+
return null;
32+
}
33+
throw e;
34+
}
2835
},
2936
});
3037
};

components/dashboard/src/data/setup.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ import * as PaginationClasses from "@gitpod/public-api/lib/gitpod/v1/pagination_
2323
import * as ConfigurationClasses from "@gitpod/public-api/lib/gitpod/v1/configuration_pb";
2424
import * as AuthProviderClasses from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";
2525
import * as EnvVarClasses from "@gitpod/public-api/lib/gitpod/v1/envvar_pb";
26+
import * as PrebuildClasses from "@gitpod/public-api/lib/gitpod/v1/prebuild_pb";
27+
import * as SCMClasses from "@gitpod/public-api/lib/gitpod/v1/scm_pb";
2628

2729
// This is used to version the cache
2830
// If data we cache changes in a non-backwards compatible way, increment this version
2931
// That will bust any previous cache versions a client may have stored
30-
const CACHE_VERSION = "6";
32+
const CACHE_VERSION = "7";
3133

3234
export function noPersistence(queryKey: QueryKey): QueryKey {
3335
return [...queryKey, "no-persistence"];
@@ -148,6 +150,8 @@ function initializeMessages() {
148150
...Object.values(ConfigurationClasses),
149151
...Object.values(AuthProviderClasses),
150152
...Object.values(EnvVarClasses),
153+
...Object.values(PrebuildClasses),
154+
...Object.values(SCMClasses),
151155
];
152156
for (const c of constr) {
153157
if ((c as any).prototype instanceof Message) {

components/dashboard/src/data/workspaces/listen-to-workspace-ws-messages.ts

Lines changed: 14 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,19 @@ import { useQueryClient } from "@tanstack/react-query";
99
import { useEffect } from "react";
1010
import { getListWorkspacesQueryKey, ListWorkspacesQueryResult } from "./list-workspaces-query";
1111
import { useCurrentOrg } from "../organizations/orgs-query";
12-
import { workspaceClient } from "../../service/public-api";
13-
import { WatchWorkspaceStatusResponse, Workspace } from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";
12+
import { stream, workspaceClient } from "../../service/public-api";
13+
import {
14+
WatchWorkspaceStatusRequest,
15+
WatchWorkspaceStatusResponse,
16+
Workspace,
17+
} from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";
1418

1519
export const useListenToWorkspacesWSMessages = () => {
1620
const queryClient = useQueryClient();
1721
const organizationId = useCurrentOrg().data?.id;
1822

1923
useEffect(() => {
20-
const disposable = disposableWatchWorkspaceStatus(undefined, (status) => {
24+
const disposable = watchWorkspaceStatus(undefined, (status) => {
2125
const queryKey = getListWorkspacesQueryKey(organizationId);
2226
let foundWorkspaces = false;
2327

@@ -47,38 +51,12 @@ export const useListenToWorkspacesWSMessages = () => {
4751
}, [organizationId, queryClient]);
4852
};
4953

50-
export const disposableWatchWorkspaceStatus = (
54+
export function watchWorkspaceStatus(
5155
workspaceId: string | undefined,
5256
cb: (response: WatchWorkspaceStatusResponse) => void,
53-
): Disposable => {
54-
const MAX_BACKOFF = 60000;
55-
const BASE_BACKOFF = 3000;
56-
let backoff = BASE_BACKOFF;
57-
const abortController = new AbortController();
58-
59-
(async () => {
60-
while (!abortController.signal.aborted) {
61-
try {
62-
const it = workspaceClient.watchWorkspaceStatus(
63-
{ workspaceId },
64-
{
65-
signal: abortController.signal,
66-
},
67-
);
68-
for await (const response of it) {
69-
cb(response);
70-
backoff = BASE_BACKOFF;
71-
}
72-
} catch (e) {
73-
backoff = Math.min(2 * backoff, MAX_BACKOFF);
74-
console.error("failed to watch workspace status, retrying", e);
75-
}
76-
const jitter = Math.random() * 0.3 * backoff;
77-
const delay = backoff + jitter;
78-
await new Promise((resolve) => setTimeout(resolve, delay));
79-
}
80-
})();
81-
return {
82-
dispose: () => abortController.abort(),
83-
};
84-
};
57+
): Disposable {
58+
return stream<WatchWorkspaceStatusRequest>(
59+
(options) => workspaceClient.watchWorkspaceStatus({ workspaceId }, options),
60+
cb,
61+
);
62+
}

0 commit comments

Comments
 (0)