From 79666cd24db3c5e3373bd2e8e15e9793a439a942 Mon Sep 17 00:00:00 2001 From: Alex Tugarev Date: Wed, 22 Nov 2023 17:26:00 +0100 Subject: [PATCH] [dashboard] Use SCM Service (gRPC) (#19101) * [dashboard] Use SCM Service (gRPC) * address review comments * bump cache version --- .../search-repositories-query.ts | 6 +- .../suggested-repositories-query.ts | 5 +- .../src/service/json-rpc-scm-client.ts | 79 +++++++++++++++++++ .../dashboard/src/service/public-api.ts | 4 + .../src/user-settings/Integrations.tsx | 6 +- .../gitpod-protocol/src/gitpod-service.ts | 3 +- 6 files changed, 94 insertions(+), 9 deletions(-) create mode 100644 components/dashboard/src/service/json-rpc-scm-client.ts diff --git a/components/dashboard/src/data/git-providers/search-repositories-query.ts b/components/dashboard/src/data/git-providers/search-repositories-query.ts index 9ec13ca8bad105..bca9703335ad5b 100644 --- a/components/dashboard/src/data/git-providers/search-repositories-query.ts +++ b/components/dashboard/src/data/git-providers/search-repositories-query.ts @@ -5,10 +5,10 @@ */ import { useQuery } from "@tanstack/react-query"; -import { getGitpodService } from "../../service/service"; import { useCurrentOrg } from "../organizations/orgs-query"; import { useDebounce } from "../../hooks/use-debounce"; import { useFeatureFlag } from "../featureflag-query"; +import { scmClient } from "../../service/public-api"; export const useSearchRepositories = ({ searchString, limit }: { searchString: string; limit: number }) => { // This disables the search behavior when flag is disabled @@ -19,11 +19,11 @@ export const useSearchRepositories = ({ searchString, limit }: { searchString: s return useQuery( ["search-repositories", { organizationId: org?.id || "", searchString: debouncedSearchString, limit }], async () => { - return await getGitpodService().server.searchRepositories({ + const { repositories } = await scmClient.searchRepositories({ searchString, - organizationId: org?.id ?? "", limit, }); + return repositories; }, { enabled: repositoryFinderSearchEnabled && !!org && debouncedSearchString.length >= 3, diff --git a/components/dashboard/src/data/git-providers/suggested-repositories-query.ts b/components/dashboard/src/data/git-providers/suggested-repositories-query.ts index e52d48c22b0aa3..51de37830e8363 100644 --- a/components/dashboard/src/data/git-providers/suggested-repositories-query.ts +++ b/components/dashboard/src/data/git-providers/suggested-repositories-query.ts @@ -6,7 +6,7 @@ import { useQuery } from "@tanstack/react-query"; import { useCurrentOrg } from "../organizations/orgs-query"; -import { getGitpodService } from "../../service/service"; +import { scmClient } from "../../service/public-api"; export const useSuggestedRepositories = () => { const { data: org } = useCurrentOrg(); @@ -18,7 +18,8 @@ export const useSuggestedRepositories = () => { throw new Error("No org selected"); } - return await getGitpodService().server.getSuggestedRepositories(org.id); + const { repositories } = await scmClient.listSuggestedRepositories({ organizationId: org.id }); + return repositories; }, { // Keeps data in cache for 7 days - will still refresh though diff --git a/components/dashboard/src/service/json-rpc-scm-client.ts b/components/dashboard/src/service/json-rpc-scm-client.ts new file mode 100644 index 00000000000000..67e745578772b5 --- /dev/null +++ b/components/dashboard/src/service/json-rpc-scm-client.ts @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2023 Gitpod GmbH. All rights reserved. + * Licensed under the GNU Affero General Public License (AGPL). + * See License.AGPL.txt in the project root for license information. + */ + +import { PartialMessage } from "@bufbuild/protobuf"; +import { PromiseClient } from "@connectrpc/connect"; +import { SCMService } from "@gitpod/public-api/lib/gitpod/v1/scm_connect"; +import { converter } from "./public-api"; +import { getGitpodService } from "./service"; +import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; +import { + SearchSCMTokensRequest, + SearchSCMTokensResponse, + GuessTokenScopesRequest, + SearchRepositoriesRequest, + ListSuggestedRepositoriesRequest, + ListSuggestedRepositoriesResponse, + SearchRepositoriesResponse, + GuessTokenScopesResponse, +} from "@gitpod/public-api/lib/gitpod/v1/scm_pb"; + +export class JsonRpcScmClient implements PromiseClient { + async searchSCMTokens({ host }: PartialMessage): Promise { + if (!host) { + throw new ApplicationError(ErrorCodes.BAD_REQUEST, "host is required"); + } + const response = new SearchSCMTokensResponse(); + const token = await getGitpodService().server.getToken({ host }); + if (token) { + response.tokens.push(converter.toSCMToken(token)); + } + return response; + } + + async guessTokenScopes({ + gitCommand, + host, + repoUrl, + }: PartialMessage): Promise { + if (!host) { + throw new ApplicationError(ErrorCodes.BAD_REQUEST, "host is required"); + } + const response = await getGitpodService().server.guessGitTokenScopes({ + gitCommand: gitCommand || "", + host, + repoUrl: repoUrl || "", + }); + return new GuessTokenScopesResponse({ + message: response.message, + scopes: response.scopes || [], + }); + } + + async searchRepositories(request: PartialMessage): Promise { + const { limit, searchString } = request; + if (!searchString) { + throw new ApplicationError(ErrorCodes.BAD_REQUEST, "searchString is required"); + } + const repos = await getGitpodService().server.searchRepositories({ searchString, limit }); + return new SearchRepositoriesResponse({ + repositories: repos.map((r) => converter.toSuggestedRepository(r)), + }); + } + + async listSuggestedRepositories( + request: PartialMessage, + ): Promise { + const { organizationId } = request; + if (!organizationId) { + throw new ApplicationError(ErrorCodes.BAD_REQUEST, "organizationId is required"); + } + const repos = await getGitpodService().server.getSuggestedRepositories(organizationId); + return new SearchRepositoriesResponse({ + repositories: repos.map((r) => converter.toSuggestedRepository(r)), + }); + } +} diff --git a/components/dashboard/src/service/public-api.ts b/components/dashboard/src/service/public-api.ts index fb1e90a96c3883..22c5358a67cf5e 100644 --- a/components/dashboard/src/service/public-api.ts +++ b/components/dashboard/src/service/public-api.ts @@ -32,6 +32,8 @@ import { JsonRpcEnvvarClient } from "./json-rpc-envvar-client"; import { Prebuild, WatchPrebuildRequest, WatchPrebuildResponse } from "@gitpod/public-api/lib/gitpod/v1/prebuild_pb"; import { JsonRpcPrebuildClient } from "./json-rpc-prebuild-client"; import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; +import { JsonRpcScmClient } from "./json-rpc-scm-client"; +import { SCMService } from "@gitpod/public-api/lib/gitpod/v1/scm_connect"; const transport = createConnectTransport({ baseUrl: `${window.location.protocol}//${window.location.host}/public-api`, @@ -61,6 +63,8 @@ export const prebuildClient = createServiceClient(PrebuildService, new JsonRpcPr export const authProviderClient = createServiceClient(AuthProviderService, new JsonRpcAuthProviderClient()); +export const scmClient = createServiceClient(SCMService, new JsonRpcScmClient()); + export const envVarClient = createServiceClient(EnvironmentVariableService, new JsonRpcEnvvarClient()); export async function listAllProjects(opts: { orgId: string }): Promise { diff --git a/components/dashboard/src/user-settings/Integrations.tsx b/components/dashboard/src/user-settings/Integrations.tsx index 6a9ecbe2588f35..2b3b517969090b 100644 --- a/components/dashboard/src/user-settings/Integrations.tsx +++ b/components/dashboard/src/user-settings/Integrations.tsx @@ -35,7 +35,7 @@ import { AuthProviderDescription, AuthProviderType, } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb"; -import { authProviderClient } from "../service/public-api"; +import { authProviderClient, scmClient } from "../service/public-api"; import { useCreateUserAuthProviderMutation } from "../data/auth-providers/create-user-auth-provider-mutation"; import { useUpdateUserAuthProviderMutation } from "../data/auth-providers/update-user-auth-provider-mutation"; import { useDeleteUserAuthProviderMutation } from "../data/auth-providers/delete-user-auth-provider-mutation"; @@ -77,7 +77,7 @@ function GitProviders() { if (!provider) { continue; } - const token = await getGitpodService().server.getToken({ host: provider.host }); + const token = (await scmClient.searchSCMTokens({ host: provider.host })).tokens[0]; scopesByProvider.set(provider.id, token?.scopes?.slice() || []); } setAllScopes(scopesByProvider); @@ -182,7 +182,7 @@ function GitProviders() { const startEditPermissions = async (provider: AuthProviderDescription) => { // todo: add spinner - const token = await getGitpodService().server.getToken({ host: provider.host }); + const token = (await scmClient.searchSCMTokens({ host: provider.host })).tokens[0]; if (token) { setEditModal({ provider, prevScopes: new Set(token.scopes), nextScopes: new Set(token.scopes) }); } diff --git a/components/gitpod-protocol/src/gitpod-service.ts b/components/gitpod-protocol/src/gitpod-service.ts index 23fa5c9c1fc77f..c9ff727bd06acd 100644 --- a/components/gitpod-protocol/src/gitpod-service.ts +++ b/components/gitpod-protocol/src/gitpod-service.ts @@ -325,7 +325,8 @@ export interface GetProviderRepositoriesParams { maxPages?: number; } export interface SearchRepositoriesParams { - organizationId: string; + /** @deprecated unused */ + organizationId?: string; searchString: string; limit?: number; // defaults to 30 }