mirror of
https://github.com/snowball-tools/snowballtools-base.git
synced 2024-11-17 12:19:20 +00:00
Check for GitHub authentication in template create page and handle GitHub unauthorized errors (#173)
* Fix GitHub auth check on reloading template create page * Fix error handling for unauthenticated GitHub token
This commit is contained in:
parent
47231a6eab
commit
cc8f9527da
@ -74,12 +74,12 @@ export const MockConnectGitCard = () => {
|
||||
return (
|
||||
<div className="flex flex-col gap-2 relative z-0">
|
||||
{IMPORT_CONTENT.map((repo, index) => (
|
||||
<>
|
||||
<MockProjectCard key={index} {...repo} />
|
||||
<React.Fragment key={index}>
|
||||
<MockProjectCard {...repo} />
|
||||
{index !== IMPORT_CONTENT.length - 1 && (
|
||||
<div className="border-b border-base-border" />
|
||||
)}
|
||||
</>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Octokit } from 'octokit';
|
||||
import assert from 'assert';
|
||||
import { useDebounce } from 'usehooks-ts';
|
||||
|
||||
@ -14,20 +13,18 @@ import {
|
||||
import { Select, SelectOption } from 'components/shared/Select';
|
||||
import { Input } from 'components/shared/Input';
|
||||
import { Button } from 'components/shared/Button';
|
||||
import { useOctokit } from 'context/OctokitContext';
|
||||
|
||||
const DEFAULT_SEARCHED_REPO = '';
|
||||
const REPOS_PER_PAGE = 5;
|
||||
|
||||
interface RepositoryListProps {
|
||||
octokit: Octokit;
|
||||
}
|
||||
|
||||
export const RepositoryList = ({ octokit }: RepositoryListProps) => {
|
||||
export const RepositoryList = () => {
|
||||
const [searchedRepo, setSearchedRepo] = useState(DEFAULT_SEARCHED_REPO);
|
||||
const [selectedAccount, setSelectedAccount] = useState<SelectOption>();
|
||||
const [orgs, setOrgs] = useState<GitOrgDetails[]>([]);
|
||||
// TODO: Add new type for Git user when required
|
||||
const [gitUser, setGitUser] = useState<GitOrgDetails>();
|
||||
const { octokit, isAuth } = useOctokit();
|
||||
|
||||
const [repositoryDetails, setRepositoryDetails] = useState<
|
||||
GitRepositoryDetails[]
|
||||
@ -35,15 +32,23 @@ export const RepositoryList = ({ octokit }: RepositoryListProps) => {
|
||||
|
||||
useEffect(() => {
|
||||
const fetchUserAndOrgs = async () => {
|
||||
const user = await octokit.rest.users.getAuthenticated();
|
||||
const orgs = await octokit.rest.orgs.listForAuthenticatedUser();
|
||||
setOrgs(orgs.data);
|
||||
setGitUser(user.data);
|
||||
setSelectedAccount({ label: user.data.login, value: user.data.login });
|
||||
try {
|
||||
const user = await octokit.rest.users.getAuthenticated();
|
||||
const orgs = await octokit.rest.orgs.listForAuthenticatedUser();
|
||||
setOrgs(orgs.data);
|
||||
setGitUser(user.data);
|
||||
setSelectedAccount({ label: user.data.login, value: user.data.login });
|
||||
} catch (error) {
|
||||
// Error handled by octokit error hook interceptor in Octokit context
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
fetchUserAndOrgs();
|
||||
}, [octokit]);
|
||||
if (isAuth) {
|
||||
fetchUserAndOrgs();
|
||||
}
|
||||
}, [octokit, isAuth]);
|
||||
|
||||
const debouncedSearchedRepo = useDebounce<string>(searchedRepo, 500);
|
||||
|
||||
|
@ -8,8 +8,11 @@ import React, {
|
||||
useEffect,
|
||||
} from 'react';
|
||||
import { Octokit, RequestError } from 'octokit';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { useDebounceCallback } from 'usehooks-ts';
|
||||
|
||||
import { useGQLClient } from './GQLClientContext';
|
||||
import { useToast } from 'components/shared/Toast';
|
||||
|
||||
const UNAUTHORIZED_ERROR_CODE = 401;
|
||||
|
||||
@ -26,17 +29,17 @@ const OctokitContext = createContext<ContextValue>({
|
||||
});
|
||||
|
||||
export const OctokitProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [authToken, setAuthToken] = useState<string>('');
|
||||
const [authToken, setAuthToken] = useState<string | null>(null);
|
||||
const [isAuth, setIsAuth] = useState(false);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { orgSlug } = useParams();
|
||||
const { toast, dismiss } = useToast();
|
||||
const client = useGQLClient();
|
||||
|
||||
const fetchUser = useCallback(async () => {
|
||||
const { user } = await client.getUser();
|
||||
|
||||
if (user.gitHubToken) {
|
||||
setAuthToken(user.gitHubToken);
|
||||
}
|
||||
setAuthToken(user.gitHubToken);
|
||||
}, []);
|
||||
|
||||
const updateAuth = useCallback(() => {
|
||||
@ -57,6 +60,23 @@ export const OctokitProvider = ({ children }: { children: ReactNode }) => {
|
||||
fetchUser();
|
||||
}, []);
|
||||
|
||||
const debouncedUnauthorizedGithubHandler = useDebounceCallback(
|
||||
useCallback(
|
||||
(error: RequestError) => {
|
||||
toast({
|
||||
id: 'unauthorized-github-token',
|
||||
title: `GitHub authentication error: ${error.message}`,
|
||||
variant: 'error',
|
||||
onDismiss: dismiss,
|
||||
});
|
||||
|
||||
navigate(`/${orgSlug}/projects/create`);
|
||||
},
|
||||
[toast, navigate, orgSlug],
|
||||
),
|
||||
500,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// TODO: Handle React component error
|
||||
const interceptor = async (error: RequestError | Error) => {
|
||||
@ -66,6 +86,8 @@ export const OctokitProvider = ({ children }: { children: ReactNode }) => {
|
||||
) {
|
||||
await client.unauthenticateGithub();
|
||||
await fetchUser();
|
||||
|
||||
debouncedUnauthorizedGithubHandler(error);
|
||||
}
|
||||
|
||||
throw error;
|
||||
@ -77,7 +99,7 @@ export const OctokitProvider = ({ children }: { children: ReactNode }) => {
|
||||
// Remove the interceptor when the component unmounts
|
||||
octokit.hook.remove('request', interceptor);
|
||||
};
|
||||
}, [octokit, client]);
|
||||
}, [octokit, client, debouncedUnauthorizedGithubHandler]);
|
||||
|
||||
return (
|
||||
<OctokitContext.Provider value={{ octokit, updateAuth, isAuth }}>
|
||||
|
@ -31,7 +31,7 @@ const NewProject = () => {
|
||||
<Heading as="h3" className="font-medium text-lg mt-10 pl-1 mb-3">
|
||||
Import a repository
|
||||
</Heading>
|
||||
<RepositoryList octokit={octokit} />
|
||||
<RepositoryList />
|
||||
</>
|
||||
) : (
|
||||
<ConnectAccount onAuth={updateAuth} />
|
||||
|
@ -26,7 +26,7 @@ type SubmitRepoValues = {
|
||||
};
|
||||
|
||||
const CreateRepo = () => {
|
||||
const { octokit } = useOctokit();
|
||||
const { octokit, isAuth } = useOctokit();
|
||||
const { template } = useOutletContext<{ template: Template }>();
|
||||
const client = useGQLClient();
|
||||
|
||||
@ -104,18 +104,26 @@ const CreateRepo = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const fetchUserAndOrgs = async () => {
|
||||
const user = await octokit?.rest.users.getAuthenticated();
|
||||
const orgs = await octokit?.rest.orgs.listForAuthenticatedUser();
|
||||
try {
|
||||
const user = await octokit?.rest.users.getAuthenticated();
|
||||
const orgs = await octokit?.rest.orgs.listForAuthenticatedUser();
|
||||
|
||||
if (user && orgs) {
|
||||
const orgsLoginArr = orgs.data.map((org) => org.login);
|
||||
if (user && orgs) {
|
||||
const orgsLoginArr = orgs.data.map((org) => org.login);
|
||||
|
||||
setGitAccounts([user.data.login, ...orgsLoginArr]);
|
||||
setGitAccounts([user.data.login, ...orgsLoginArr]);
|
||||
}
|
||||
} catch (error) {
|
||||
// Error handled by octokit error hook interceptor in Octokit context
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
fetchUserAndOrgs();
|
||||
}, [octokit]);
|
||||
if (isAuth) {
|
||||
fetchUserAndOrgs();
|
||||
}
|
||||
}, [octokit, isAuth]);
|
||||
|
||||
const { handleSubmit, control, reset } = useForm<SubmitRepoValues>({
|
||||
defaultValues: {
|
||||
|
Loading…
Reference in New Issue
Block a user