Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexTugarev committed Nov 13, 2023
1 parent 1b81555 commit fe333d2
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 148 deletions.
15 changes: 7 additions & 8 deletions components/dashboard/src/components/AuthorizeGit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
* See License.AGPL.txt in the project root for license information.
*/

import { AuthProviderInfo } from "@gitpod/gitpod-protocol";
import { FC, useCallback, useContext } from "react";
import { Link } from "react-router-dom";
import { useAuthProviders } from "../data/auth-providers/auth-provider-query";
import { useAuthProviderDescriptions } from "../data/auth-providers/auth-provider-query";
import { openAuthorizeWindow } from "../provider-utils";
import { getGitpodService } from "../service/service";
import { UserContext, useCurrentUser } from "../user-context";
Expand All @@ -16,29 +15,29 @@ import { Heading2, Heading3, Subheading } from "./typography/headings";
import classNames from "classnames";
import { iconForAuthProvider, simplifyProviderName } from "../provider-utils";
import { useIsOwner } from "../data/organizations/members-query";
import { AuthProviderDescription } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";

export function useNeedsGitAuthorization() {
const authProviders = useAuthProviders();
const authProviders = useAuthProviderDescriptions();
const user = useCurrentUser();
if (!user || !authProviders.data) {
return false;
}
return !authProviders.data.some((ap) => user.identities.some((i) => ap.authProviderId === i.authProviderId));
return !authProviders.data.some((ap) => user.identities.some((i) => ap.id === i.authProviderId));
}

export const AuthorizeGit: FC<{ className?: string }> = ({ className }) => {
const { setUser } = useContext(UserContext);
const owner = useIsOwner();
const authProviders = useAuthProviders();
const authProviders = useAuthProviderDescriptions();
const updateUser = useCallback(() => {
getGitpodService().server.getLoggedInUser().then(setUser);
}, [setUser]);

const connect = useCallback(
(ap: AuthProviderInfo) => {
(ap: AuthProviderDescription) => {
openAuthorizeWindow({
host: ap.host,
scopes: ap.requirements?.default,
overrideScopes: true,
onSuccess: updateUser,
});
Expand Down Expand Up @@ -91,7 +90,7 @@ export const AuthorizeGit: FC<{ className?: string }> = ({ className }) => {
className="mt-3 btn-login flex-none w-56 px-0 py-0.5 inline-flex"
>
<div className="flex relative -left-4 w-56">
{iconForAuthProvider(ap.authProviderType)}
{iconForAuthProvider(ap.type)}
<span className="pt-2 pb-2 mr-3 text-sm my-auto font-medium truncate overflow-ellipsis">
Continue with {simplifyProviderName(ap.host)}
</span>
Expand Down
10 changes: 7 additions & 3 deletions components/dashboard/src/components/RepositoryFinder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import { ReactComponent as RepositoryIcon } from "../icons/RepositoryWithColor.s
import { SuggestedRepository } from "@gitpod/gitpod-protocol";
import { MiddleDot } from "./typography/MiddleDot";
import { useUnifiedRepositorySearch } from "../data/git-providers/unified-repositories-search-query";
import { useAuthProviders } from "../data/auth-providers/auth-provider-query";
import { useAuthProviderDescriptions } from "../data/auth-providers/auth-provider-query";
import { ReactComponent as Exclamation2 } from "../images/exclamation2.svg";
import { AuthProviderType } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";

interface RepositoryFinderProps {
selectedContextURL?: string;
Expand All @@ -39,7 +40,7 @@ export default function RepositoryFinder({
hasMore,
} = useUnifiedRepositorySearch({ searchString, excludeProjects });

const authProviders = useAuthProviders();
const authProviders = useAuthProviderDescriptions();

const handleSelectionChange = useCallback(
(selectedID: string) => {
Expand Down Expand Up @@ -115,7 +116,10 @@ export default function RepositoryFinder({
isSelectable: false,
} as ComboboxElement);
}
if (searchString.length >= 3 && authProviders.data?.some((p) => p.authProviderType === "BitbucketServer")) {
if (
searchString.length >= 3 &&
authProviders.data?.some((p) => p.type === AuthProviderType.BITBUCKET_SERVER)
) {
// add an element that tells the user that the Bitbucket Server does only support prefix search
result.push({
id: "bitbucket-server",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,14 @@
* See License.AGPL.txt in the project root for license information.
*/

import { AuthProviderInfo } from "@gitpod/gitpod-protocol";
import { useQuery } from "@tanstack/react-query";
import { getGitpodService } from "../../service/service";
import { authProviderClient } from "../../service/public-api";
import { useCurrentUser } from "../../user-context";
import {
AuthProviderDescription,
ListAuthProviderDescriptionsRequest,
} from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";

export const useAuthProviders = () => {
return useQuery<AuthProviderInfo[]>({
queryKey: ["auth-providers"],
queryFn: async () => {
return await getGitpodService().server.getAuthProviders();
},
});
};

export const useAuthProviderDescriptions = () => {
const user = useCurrentUser();
const query = useQuery<AuthProviderDescription[]>({
Expand Down
38 changes: 18 additions & 20 deletions components/dashboard/src/projects/NewProject.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* See License.AGPL.txt in the project root for license information.
*/

import { AuthProviderInfo, Project } from "@gitpod/gitpod-protocol";
import { Project } from "@gitpod/gitpod-protocol";
import { FC, useCallback, useContext, useEffect, useMemo, useState } from "react";
import ErrorMessage from "../components/ErrorMessage";
import { useCurrentOrg } from "../data/organizations/orgs-query";
Expand All @@ -13,19 +13,20 @@ import { iconForAuthProvider, openAuthorizeWindow, simplifyProviderName } from "
import { getGitpodService } from "../service/service";
import { UserContext, useCurrentUser } from "../user-context";
import { Heading1, Subheading } from "../components/typography/headings";
import { useAuthProviders } from "../data/auth-providers/auth-provider-query";
import { useAuthProviderDescriptions } from "../data/auth-providers/auth-provider-query";
import { AuthorizeGit, useNeedsGitAuthorization } from "../components/AuthorizeGit";
import { NewProjectRepoSelection } from "./new-project/NewProjectRepoSelection";
import { NewProjectSubheading } from "./new-project/NewProjectSubheading";
import { Button } from "../components/Button";
import { AuthProviderDescription, AuthProviderType } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";

export default function NewProject() {
const currentTeam = useCurrentOrg()?.data;
const user = useCurrentUser();
const authProviders = useAuthProviders();
const authProviders = useAuthProviderDescriptions();

// State this component manages
const [selectedProvider, setSelectedProvider] = useState<AuthProviderInfo>();
const [selectedProvider, setSelectedProvider] = useState<AuthProviderDescription>();
const [project, setProject] = useState<Project>();

// Defaults selectedProviderHost if not set yet
Expand All @@ -34,9 +35,7 @@ export default function NewProject() {
for (let i = user.identities.length - 1; i >= 0; i--) {
const candidate = user.identities[i];
if (candidate) {
const authProvider = authProviders.data.find(
(ap) => ap.authProviderId === candidate.authProviderId,
);
const authProvider = authProviders.data.find((ap) => ap.id === candidate.authProviderId);
if (authProvider) {
setSelectedProvider(authProvider);
break;
Expand Down Expand Up @@ -107,8 +106,8 @@ export default function NewProject() {
}

type NewProjectMainContentProps = {
selectedProvider?: AuthProviderInfo;
onProviderSelected: (ap: AuthProviderInfo, updateUser?: boolean) => void;
selectedProvider?: AuthProviderDescription;
onProviderSelected: (ap: AuthProviderDescription, updateUser?: boolean) => void;
onProjectCreated: (project: Project) => void;
};
const NewProjectMainContent: FC<NewProjectMainContentProps> = ({
Expand All @@ -117,12 +116,12 @@ const NewProjectMainContent: FC<NewProjectMainContentProps> = ({
onProjectCreated,
}) => {
const { setUser } = useContext(UserContext);
const authProviders = useAuthProviders();
const authProviders = useAuthProviderDescriptions();
const needsGitAuth = useNeedsGitAuthorization();
const [showGitProviders, setShowGitProviders] = useState(false);

const onGitProviderSeleted = useCallback(
async (ap: AuthProviderInfo, updateUser?: boolean) => {
async (ap: AuthProviderDescription, updateUser?: boolean) => {
// TODO: Can we push this down into where sends updateUser=true?
if (updateUser) {
setUser(await getGitpodService().server.getLoggedInUser());
Expand Down Expand Up @@ -151,23 +150,22 @@ const NewProjectMainContent: FC<NewProjectMainContentProps> = ({
};

const GitProviders: FC<{
authProviders: AuthProviderInfo[];
onProviderSelected: (ap: AuthProviderInfo, updateUser?: boolean) => void;
authProviders: AuthProviderDescription[];
onProviderSelected: (ap: AuthProviderDescription, updateUser?: boolean) => void;
}> = ({ authProviders, onProviderSelected }) => {
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined);

const selectProvider = useCallback(
async (ap: AuthProviderInfo) => {
async (ap: AuthProviderDescription) => {
setErrorMessage(undefined);

const token = await getGitpodService().server.getToken({ host: ap.host });
if (token && !(ap.authProviderType === "GitHub" && !token.scopes.includes("repo"))) {
if (token && !(ap.type === AuthProviderType.GITHUB && !token.scopes.includes("repo"))) {
onProviderSelected(ap);
return;
}
await openAuthorizeWindow({
host: ap.host,
scopes: ap.authProviderType === "GitHub" ? ["repo"] : ap.requirements?.default,
onSuccess: async () => {
onProviderSelected(ap, true);
},
Expand All @@ -194,10 +192,10 @@ const GitProviders: FC<{
() =>
authProviders.filter(
(p) =>
p.authProviderType === "GitHub" ||
p.type === AuthProviderType.GITHUB ||
p.host === "bitbucket.org" ||
p.authProviderType === "GitLab" ||
p.authProviderType === "BitbucketServer",
p.type === AuthProviderType.GITLAB ||
p.type === AuthProviderType.BITBUCKET_SERVER,
),
[authProviders],
);
Expand All @@ -216,7 +214,7 @@ const GitProviders: FC<{
className="btn-login flex-none w-56 h-10 p-0 inline-flex"
onClick={() => selectProvider(ap)}
>
{iconForAuthProvider(ap.authProviderType)}
{iconForAuthProvider(ap.type)}
<span className="pt-2 pb-2 mr-3 text-sm my-auto font-medium truncate overflow-ellipsis">
Continue with {simplifyProviderName(ap.host)}
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { FC, useCallback } from "react";
import { useAuthProviders } from "../../data/auth-providers/auth-provider-query";
import { useAuthProviderDescriptions } from "../../data/auth-providers/auth-provider-query";
import { openAuthorizeWindow } from "../../provider-utils";

type Props = {
Expand All @@ -18,7 +18,7 @@ export const NewProjectAuthRequired: FC<Props> = ({
areGitHubWebhooksUnauthorized = false,
onReconfigure,
}) => {
const authProviders = useAuthProviders();
const authProviders = useAuthProviderDescriptions();

const handleAuthorize = useCallback(() => {
const ap = authProviders.data?.find((ap) => ap.host === selectedProviderHost);
Expand All @@ -27,12 +27,8 @@ export const NewProjectAuthRequired: FC<Props> = ({
}
openAuthorizeWindow({
host: ap.host,
scopes: ap.authProviderType === "GitHub" ? ["repo"] : ap.requirements?.default,
onSuccess: async () => {
// TODO: Verify this works correctly
if (ap.authProviderType === "GitHub") {
authProviders.refetch();
}
authProviders.refetch();
},
onError: (payload) => {
console.error("Authorization failed", selectedProviderHost, payload);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* See License.AGPL.txt in the project root for license information.
*/

import { AuthProviderInfo, Project } from "@gitpod/gitpod-protocol";
import { Project } from "@gitpod/gitpod-protocol";
import { FC, useCallback, useEffect, useMemo, useState } from "react";
import { useAreGithubWebhooksUnauthorized, useIsGithubAppEnabled } from "../../data/git-providers/github-queries";
import { LinkButton } from "../../components/LinkButton";
Expand All @@ -22,9 +22,10 @@ import { NewProjectCreateFromURL } from "./NewProjectCreateFromURL";
import { useStateWithDebounce } from "../../hooks/use-state-with-debounce";
import { useFeatureFlag } from "../../data/featureflag-query";
import Alert from "../../components/Alert";
import { AuthProviderDescription, AuthProviderType } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";

type Props = {
selectedProvider?: AuthProviderInfo;
selectedProvider?: AuthProviderDescription;
onProjectCreated: (project: Project) => void;
onChangeGitProvider: () => void;
};
Expand Down Expand Up @@ -59,7 +60,7 @@ export const NewProjectRepoSelection: FC<Props> = ({ selectedProvider, onProject
// Memoized & derived values
const noReposAvailable = !!(reposInAccounts?.length === 0 || areGitHubWebhooksUnauthorized);
const isGitHub = selectedProvider?.host === "github.com";
const isBitbucketServer = selectedProvider?.authProviderType === "BitbucketServer";
const isBitbucketServer = selectedProvider?.type === AuthProviderType.BITBUCKET_SERVER;
const enableBBSIncrementalSearch = isBitbucketServer && newProjectIncrementalRepoSearchBBS;

const accounts = useMemo(() => {
Expand Down
15 changes: 15 additions & 0 deletions components/dashboard/src/provider-utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,21 @@ function iconForAuthProvider(type: string | AuthProviderType) {
}
}

export function toAuthProviderLabel(type: AuthProviderType) {
switch (type) {
case AuthProviderType.GITHUB:
return "GitHub";
case AuthProviderType.GITLAB:
return "GitLab";
case AuthProviderType.BITBUCKET:
return "Bitbucket Cloud";
case AuthProviderType.BITBUCKET_SERVER:
return "Bitbucket Server";
default:
return "Unknown type";
}
}

function simplifyProviderName(host: string) {
switch (host) {
case "github.com":
Expand Down
16 changes: 8 additions & 8 deletions components/dashboard/src/user-settings/AuthEntryItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
* See License.AGPL.txt in the project root for license information.
*/

import { AuthProviderInfo } from "@gitpod/gitpod-protocol";
import { useState } from "react";
import { ContextMenuEntry } from "../components/ContextMenu";
import { Item, ItemFieldIcon, ItemField, ItemFieldContextMenu } from "../components/ItemsList";
import { AuthProviderDescription } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";

interface AuthEntryItemParams {
ap: AuthProviderInfo;
ap: AuthProviderDescription;
isConnected: (authProviderId: string) => boolean;
getUsername: (authProviderId: string) => string | undefined;
getPermissions: (authProviderId: string) => string[] | undefined;
gitProviderMenu: (provider: AuthProviderInfo) => ContextMenuEntry[];
gitProviderMenu: (provider: AuthProviderDescription) => ContextMenuEntry[];
}

export const AuthEntryItem = (props: AuthEntryItemParams) => {
Expand All @@ -25,32 +25,32 @@ export const AuthEntryItem = (props: AuthEntryItemParams) => {
};

return (
<Item key={"ap-" + props.ap.authProviderId} className="h-16" solid={menuVisible}>
<Item key={"ap-" + props.ap.id} className="h-16" solid={menuVisible}>
<ItemFieldIcon>
<div
className={
"rounded-full w-3 h-3 text-sm align-middle m-auto " +
(props.isConnected(props.ap.authProviderId) ? "bg-green-500" : "bg-gray-400")
(props.isConnected(props.ap.id) ? "bg-green-500" : "bg-gray-400")
}
>
&nbsp;
</div>
</ItemFieldIcon>
<ItemField className="w-4/12 xl:w-3/12 flex flex-col my-auto">
<span className="my-auto font-medium truncate overflow-ellipsis">{props.ap.authProviderType}</span>
<span className="my-auto font-medium truncate overflow-ellipsis">{props.ap.type}</span>
<span className="text-sm my-auto text-gray-400 truncate overflow-ellipsis dark:text-gray-500">
{props.ap.host}
</span>
</ItemField>
<ItemField className="w-6/12 xl:w-3/12 flex flex-col my-auto">
<span className="my-auto truncate text-gray-500 overflow-ellipsis dark:text-gray-400">
{props.getUsername(props.ap.authProviderId) || "–"}
{props.getUsername(props.ap.id) || "–"}
</span>
<span className="text-sm my-auto text-gray-400 dark:text-gray-500">Username</span>
</ItemField>
<ItemField className="hidden xl:w-1/3 xl:flex xl:flex-col my-auto">
<span className="my-auto truncate text-gray-500 overflow-ellipsis dark:text-gray-400">
{props.getPermissions(props.ap.authProviderId)?.join(", ") || "–"}
{props.getPermissions(props.ap.id)?.join(", ") || "–"}
</span>
<span className="text-sm my-auto text-gray-400 dark:text-gray-500">Permissions</span>
</ItemField>
Expand Down
Loading

0 comments on commit fe333d2

Please sign in to comment.