fix(code/frontend): repo not exists error page (#36093) (#37462)

fix(code/frontend): repo not exists error page
This commit is contained in:
WangQianliang 2019-05-30 16:41:33 +08:00 committed by GitHub
parent 737a4df891
commit 94f5de9542
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 71 additions and 18 deletions

View file

@ -69,7 +69,8 @@ export const fetchFileFailed = createAction<Error>('FETCH FILE ERROR');
export const fetchDirectory = createAction<FetchRepoTreePayload>('FETCH REPO DIR');
export const fetchDirectorySuccess = createAction<FileTree>('FETCH REPO DIR SUCCESS');
export const fetchDirectoryFailed = createAction<Error>('FETCH REPO DIR FAILED');
export const setNotFound = createAction<boolean>('SET NOT FOUND');
export const setNotFound = createAction<boolean>('SET FILE NOT FOUND');
export const dirNotFound = createAction<string>('DIR NOT FOUND');
export const fetchTreeCommits = createAction<FetchFilePayload>('FETCH TREE COMMITS');
export const fetchTreeCommitsSuccess = createAction<{

View file

@ -40,6 +40,7 @@ export const fetchRepoConfigFailed = createAction<Error>('FETCH REPO CONFIGS FAI
export const initRepoCommand = createAction<string>('INIT REPO CMD');
export const gotoRepo = createAction<string>('GOTO REPO');
export const gotoRepoFailed = createAction('GOTO REPO FAILED');
export const switchLanguageServer = createAction<RepoConfigPayload>('SWITCH LANGUAGE SERVER');
export const switchLanguageServerSuccess = createAction('SWITCH LANGUAGE SERVER SUCCESS');

View file

@ -18,8 +18,6 @@ import { Route } from './route';
import * as ROUTES from './routes';
import { Search } from './search_page/search';
const Empty = () => null;
const RooComponent = (props: { setupOk?: boolean }) => {
if (props.setupOk) {
return <Redirect to={'/admin'} />;
@ -33,6 +31,8 @@ const mapStateToProps = (state: RootState) => ({
const Root = connect(mapStateToProps)(RooComponent);
const Empty = () => null;
export const App = () => {
return (
<Router>

View file

@ -52,6 +52,7 @@ interface Props extends RouteComponentProps<MainRouteParams> {
loadingCommits: boolean;
onSearchScopeChanged: (s: SearchScope) => void;
repoScope: string[];
notFoundDirs: string[];
searchOptions: SearchOptions;
fileTreeLoading: boolean;
query: string;
@ -265,15 +266,15 @@ class CodeContent extends React.PureComponent<Props> {
}
public renderContent() {
if (this.props.isNotFound) {
const { file, match, tree, fileTreeLoading, isNotFound, notFoundDirs } = this.props;
const { path, pathType, resource, org, repo, revision } = match.params;
if (isNotFound || notFoundDirs.includes(path || '')) {
return <NotFound />;
}
if (this.shouldRenderProgress()) {
return this.renderProgress();
}
const { file, match, tree, fileTreeLoading } = this.props;
const { path, pathType, resource, org, repo, revision } = match.params;
const repoUri = `${resource}/${org}/${repo}`;
switch (pathType) {
case PathTypes.tree:
@ -386,6 +387,7 @@ class CodeContent extends React.PureComponent<Props> {
const mapStateToProps = (state: RootState) => ({
isNotFound: state.file.isNotFound,
notFoundDirs: state.file.notFoundDirs,
file: state.file.file,
tree: state.file.tree,
fileTreeLoading: state.file.fileTreeLoading,

View file

@ -11,7 +11,7 @@ import { ErrorIcon } from '../shared/icons';
export const ErrorPanel = (props: { title: ReactNode; content: string }) => {
return (
<div className="codePanel__error">
<div className="codePanel__error" data-test-subj="codeNotFoundErrorPage">
<EuiPanel>
<EuiSpacer />
<EuiText textAlign="center">

View file

@ -31,6 +31,7 @@ import {
setNotFound,
fetchRootRepoTreeSuccess,
fetchRootRepoTreeFailed,
dirNotFound,
} from '../actions';
export interface FileState {
@ -38,6 +39,8 @@ export interface FileState {
fileTreeLoading: boolean;
rootFileTreeLoading: boolean;
openedPaths: string[];
// store not found directory as an array to calculate `notFound` flag by finding whether path is in this array
notFoundDirs: string[];
branches: ReferenceInfo[];
tags: ReferenceInfo[];
commits: CommitInfo[];
@ -58,6 +61,7 @@ const initialState: FileState = {
type: FileTreeItemType.Directory,
},
openedPaths: [],
notFoundDirs: [],
rootFileTreeLoading: true,
fileTreeLoading: false,
branches: [],
@ -117,6 +121,7 @@ export const file = handleActions(
produce<FileState>(state, (draft: FileState) => {
draft.fileTreeLoading = false;
draft.rootFileTreeLoading = false;
draft.notFoundDirs = draft.notFoundDirs.filter(dir => dir !== action.payload!.path);
const { tree, path, withParents } = action.payload!;
if (withParents || path === '/' || path === '') {
draft.tree = mergeNode(draft.tree, tree);
@ -144,6 +149,10 @@ export const file = handleActions(
produce<FileState>(state, (draft: FileState) => {
draft.rootFileTreeLoading = false;
}),
[String(dirNotFound)]: (state: FileState, action: any) =>
produce<FileState>(state, (draft: FileState) => {
draft.notFoundDirs.push(action.payload);
}),
[String(resetRepoTree)]: (state: FileState) =>
produce<FileState>(state, (draft: FileState) => {
draft.tree = initialState.tree;

View file

@ -22,6 +22,7 @@ import {
loadConfigsSuccess,
loadRepoSuccess,
loadRepoFailed,
loadRepo,
} from '../actions';
export enum ToastType {
@ -41,6 +42,7 @@ export interface RepositoryState {
toastType?: ToastType;
projectConfigs: { [key: string]: RepositoryConfig };
currentRepository?: Repository;
repoNotFound: boolean;
}
const initialState: RepositoryState = {
@ -49,6 +51,7 @@ const initialState: RepositoryState = {
importLoading: false,
showToast: false,
projectConfigs: {},
repoNotFound: false,
};
export const repository = handleActions(
@ -116,13 +119,20 @@ export const repository = handleActions(
produce<RepositoryState>(state, draft => {
draft.projectConfigs = action.payload;
}),
[String(loadRepo)]: (state: RepositoryState, action: Action<any>) =>
produce<RepositoryState>(state, draft => {
draft.currentRepository = undefined;
draft.repoNotFound = false;
}),
[String(loadRepoSuccess)]: (state: RepositoryState, action: Action<any>) =>
produce<RepositoryState>(state, draft => {
draft.currentRepository = action.payload;
draft.repoNotFound = false;
}),
[String(loadRepoFailed)]: (state: RepositoryState, action: Action<any>) =>
produce<RepositoryState>(state, draft => {
draft.currentRepository = undefined;
draft.repoNotFound = true;
}),
},
initialState

View file

@ -202,7 +202,7 @@ function* handleMainRouteChange(action: Action<Match>) {
.split('/')
.slice(0, -1)
.join('/');
yield put(openTreePath(openPath));
yield put(openTreePath(openPath || ''));
yield put(
fetchRepoTree({
uri: repoUri,

View file

@ -42,6 +42,7 @@ import {
fetchRootRepoTree,
fetchRootRepoTreeSuccess,
fetchRootRepoTreeFailed,
dirNotFound,
} from '../actions';
import { RootState } from '../reducers';
import { treeCommitsSelector, createTreeSelector } from '../selectors';
@ -65,6 +66,9 @@ function* handleFetchRepoTree(action: Action<FetchRepoTreePayload>) {
yield call(fetchPath, action.payload!);
}
} catch (err) {
if (action.payload!.isDir && err.body && err.body.statusCode === 404) {
yield put(dirNotFound(action.payload!.path));
}
yield put(fetchRepoTreeFailed(err));
}
}
@ -284,8 +288,9 @@ export function* watchFetchBranchesAndCommits() {
}
function* handleRepoRouteChange(action: Action<Match>) {
const { url } = action.payload!;
yield put(gotoRepo(url));
const { repo, org, resource } = action.payload!.params;
const uri = `${resource}/${org}/${repo}`;
yield put(gotoRepo(uri));
}
export function* watchRepoRouteChange() {

View file

@ -6,7 +6,7 @@
import { kfetch } from 'ui/kfetch';
import { Action } from 'redux-actions';
import { call, put, takeEvery, takeLatest } from 'redux-saga/effects';
import { call, put, takeEvery, takeLatest, take } from 'redux-saga/effects';
import {
deleteRepo,
deleteRepoFailed,
@ -27,6 +27,10 @@ import {
initRepoCommand,
updateDeleteProgress,
updateIndexProgress,
gotoRepoFailed,
loadRepo,
loadRepoSuccess,
loadRepoFailed,
} from '../actions';
import { loadLanguageServers } from '../actions/language_server';
import { history } from '../utils/url';
@ -125,13 +129,20 @@ function requestRepoInitCmd(repoUri: string) {
});
}
function* handleGotoRepo(action: Action<string>) {
const repoUri = action.payload as string;
const repo = yield call(requestRepo, repoUri);
history.replace(`${repoUri}/tree/${repo.defaultBranch || 'master'}`);
}
function requestRepo(uri: string) {
return kfetch({ pathname: `/api/code/repo${uri}`, method: 'get' });
try {
const repoUri = action.payload as string;
yield put(loadRepo(repoUri));
const loadRepoDoneAction = yield take([String(loadRepoSuccess), String(loadRepoFailed)]);
if (loadRepoDoneAction.type === String(loadRepoSuccess)) {
history.replace(`/${repoUri}/tree/${loadRepoDoneAction.payload.defaultBranch || 'master'}`);
} else {
// redirect to root project path if repo not found to show 404 page
history.replace(`/${action.payload}/tree/master`);
}
} catch (e) {
history.replace(`/${action.payload}/tree/master`);
yield put(gotoRepoFailed(e));
}
}
export function* watchImportRepo() {

View file

@ -294,6 +294,20 @@ export default function exploreRepositoryFunctonalTests({
// });
// });
// });
it('goes to a repository which does not exist should render the 404 error page', async () => {
log.debug('it goes to a repository which does not exist');
const notExistRepoUri = 'github.com/I_DO_NOT_EXIST/I_DO_NOT_EXIST';
const url = `${PageObjects.common.getHostPort()}/app/code#/${notExistRepoUri}`;
await browser.get(url);
await retry.try(async () => {
const currentUrl: string = await browser.getCurrentUrl();
expect(currentUrl.indexOf(notExistRepoUri)).to.greaterThan(0);
});
await retry.try(async () => {
expect(await testSubjects.exists('codeNotFoundErrorPage')).ok();
});
});
});
});
}