Skip to content

Commit

Permalink
Enable deleting, resynching and resync all across namespaces. (#1506)
Browse files Browse the repository at this point in the history
  • Loading branch information
absoludity authored Feb 10, 2020
1 parent 5352574 commit 7df8971
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 50 deletions.
36 changes: 31 additions & 5 deletions dashboard/src/actions/repos.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ const appRepo = { spec: { resyncRequests: 10000 } };
const kubeappsNamespace = "kubeapps-namespace";

beforeEach(() => {
store = mockStore({ config: { namespace: kubeappsNamespace } });
store = mockStore({
config: { namespace: kubeappsNamespace },
namespace: { current: kubeappsNamespace },
});
AppRepository.list = jest.fn().mockImplementationOnce(() => {
return { items: { foo: "bar" } };
});
Expand Down Expand Up @@ -94,7 +97,7 @@ describe("deleteRepo", () => {
},
];

await store.dispatch(repoActions.deleteRepo("foo"));
await store.dispatch(repoActions.deleteRepo("foo", "my-namespace"));
expect(store.getActions()).toEqual(expectedActions);
});

Expand All @@ -110,7 +113,7 @@ describe("deleteRepo", () => {
},
];

await store.dispatch(repoActions.deleteRepo("foo"));
await store.dispatch(repoActions.deleteRepo("foo", "my-namespace"));
expect(store.getActions()).toEqual(expectedActions);
});
});
Expand All @@ -128,7 +131,7 @@ describe("resyncRepo", () => {
},
];

await store.dispatch(repoActions.resyncRepo("foo"));
await store.dispatch(repoActions.resyncRepo("foo", "my-namespace"));
expect(store.getActions()).toEqual(expectedActions);
});

Expand All @@ -144,11 +147,34 @@ describe("resyncRepo", () => {
},
];

await store.dispatch(repoActions.resyncRepo("foo"));
await store.dispatch(repoActions.resyncRepo("foo", "my-namespace"));
expect(store.getActions()).toEqual(expectedActions);
});
});

describe("resyncAllRepos", () => {
it("resyncs each repo using its namespace", async () => {
const appRepoGetMock = jest.fn();
AppRepository.get = appRepoGetMock;
await store.dispatch(
repoActions.resyncAllRepos([
{
name: "foo",
namespace: "namespace-1",
},
{
name: "bar",
namespace: "namespace-2",
},
]),
);

expect(appRepoGetMock).toHaveBeenCalledTimes(2);
expect(appRepoGetMock.mock.calls[0]).toEqual(["foo", "namespace-1"]);
expect(appRepoGetMock.mock.calls[1]).toEqual(["bar", "namespace-2"]);
});
});

describe("fetchRepos", () => {
const namespace = "default";
it("dispatches requestRepos and receivedRepos if no error", async () => {
Expand Down
23 changes: 11 additions & 12 deletions dashboard/src/actions/repos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Chart from "../shared/Chart";
import { definedNamespaces } from "../shared/Namespace";
import { errorChart } from "./charts";

import { IAppRepository, IStoreState, NotFoundError } from "../shared/types";
import { IAppRepository, IAppRepositoryKey, IStoreState, NotFoundError } from "../shared/types";

export const addRepo = createAction("ADD_REPO");
export const addedRepo = createAction("ADDED_REPO", resolve => {
Expand Down Expand Up @@ -65,14 +65,15 @@ export type AppReposAction = ActionType<typeof allActions[number]>;

export const deleteRepo = (
name: string,
namespace: string,
): ThunkAction<Promise<boolean>, IStoreState, null, AppReposAction> => {
return async (dispatch, getState) => {
const {
namespace: { current },
} = getState();
try {
const {
config: { namespace },
} = getState();
await AppRepository.delete(name, namespace);
dispatch(fetchRepos());
dispatch(fetchRepos(current));
return true;
} catch (e) {
dispatch(errorRepos(e, "delete"));
Expand All @@ -83,12 +84,10 @@ export const deleteRepo = (

export const resyncRepo = (
name: string,
namespace: string,
): ThunkAction<Promise<void>, IStoreState, null, AppReposAction> => {
return async (dispatch, getState) => {
return async dispatch => {
try {
const {
config: { namespace },
} = getState();
const repo = await AppRepository.get(name, namespace);
repo.spec.resyncRequests = repo.spec.resyncRequests || 0;
repo.spec.resyncRequests++;
Expand All @@ -101,11 +100,11 @@ export const resyncRepo = (
};

export const resyncAllRepos = (
repoNames: string[],
repos: IAppRepositoryKey[],
): ThunkAction<Promise<void>, IStoreState, null, AppReposAction> => {
return async (dispatch, getState) => {
repoNames.forEach(name => {
dispatch(resyncRepo(name));
repos.forEach(repo => {
dispatch(resyncRepo(repo.name, repo.namespace));
});
};
};
Expand Down
20 changes: 8 additions & 12 deletions dashboard/src/components/Config/AppRepoList/AppRepoList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from "react";

import { definedNamespaces } from "../../../shared/Namespace";
import { IAppRepository, IRBACRole } from "../../../shared/types";
import { IAppRepository, IAppRepositoryKey, IRBACRole } from "../../../shared/types";
import { ErrorSelector, MessageAlert } from "../../ErrorAlert";
import LoadingWrapper from "../../LoadingWrapper";
import { AppRepoAddButton } from "./AppRepoButton";
Expand All @@ -17,9 +17,9 @@ export interface IAppRepoListProps {
};
repos: IAppRepository[];
fetchRepos: (namespace: string) => void;
deleteRepo: (name: string) => Promise<boolean>;
resyncRepo: (name: string) => void;
resyncAllRepos: (names: string[]) => void;
deleteRepo: (name: string, namespace: string) => Promise<boolean>;
resyncRepo: (name: string, namespace: string) => void;
resyncAllRepos: (repos: IAppRepositoryKey[]) => void;
install: (
name: string,
namespace: string,
Expand Down Expand Up @@ -79,12 +79,12 @@ class AppRepoList extends React.Component<IAppRepoListProps> {
errors,
repos,
install,
deleteRepo,
resyncRepo,
resyncAllRepos,
namespace,
displayReposPerNamespaceMsg,
isFetching,
deleteRepo,
resyncRepo,
resyncAllRepos,
} = this.props;
const renderNamespace = namespace === definedNamespaces.all;
return (
Expand Down Expand Up @@ -117,11 +117,7 @@ class AppRepoList extends React.Component<IAppRepoListProps> {
</table>
</LoadingWrapper>
<AppRepoAddButton error={errors.create} install={install} namespace={namespace} />
<AppRepoRefreshAllButton
resyncAllRepos={resyncAllRepos}
repos={repos}
namespace={namespace}
/>
<AppRepoRefreshAllButton resyncAllRepos={resyncAllRepos} repos={repos} />
{displayReposPerNamespaceMsg && (
<MessageAlert header="Looking for other app repositories?">
<div>
Expand Down
16 changes: 8 additions & 8 deletions dashboard/src/components/Config/AppRepoList/AppRepoListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import ConfirmDialog from "../../ConfirmDialog";
interface IAppRepoListItemProps {
repo: IAppRepository;
renderNamespace: boolean;
deleteRepo: (name: string) => Promise<boolean>;
resyncRepo: (name: string) => void;
deleteRepo: (name: string, namespace: string) => Promise<boolean>;
resyncRepo: (name: string, namespace: string) => void;
}

interface IAppRepoListItemState {
Expand All @@ -31,7 +31,7 @@ export class AppRepoListItem extends React.Component<IAppRepoListItemProps, IApp
<td>{repo.spec && repo.spec.url}</td>
<td>
<ConfirmDialog
onConfirm={this.handleDeleteClick(repo.metadata.name)}
onConfirm={this.handleDeleteClick(repo.metadata.name, repo.metadata.namespace)}
modalIsOpen={this.state.modalIsOpen}
loading={false}
closeModal={this.closeModal}
Expand All @@ -43,7 +43,7 @@ export class AppRepoListItem extends React.Component<IAppRepoListItemProps, IApp

<button
className="button button-secondary"
onClick={this.handleResyncClick(repo.metadata.name)}
onClick={this.handleResyncClick(repo.metadata.name, repo.metadata.namespace)}
>
Refresh
</button>
Expand All @@ -64,16 +64,16 @@ export class AppRepoListItem extends React.Component<IAppRepoListItemProps, IApp
});
};

private handleDeleteClick(repoName: string) {
private handleDeleteClick(repoName: string, namespace: string) {
return async () => {
this.props.deleteRepo(repoName);
this.props.deleteRepo(repoName, namespace);
this.setState({ modalIsOpen: false });
};
}

private handleResyncClick(repoName: string) {
private handleResyncClick(repoName: string, namespace: string) {
return () => {
this.props.resyncRepo(repoName);
this.props.resyncRepo(repoName, namespace);
};
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import * as React from "react";

import { IAppRepository } from "shared/types";
import { IAppRepository, IAppRepositoryKey } from "shared/types";
import "./AppRepo.css";

interface IAppRepoRefreshAllButtonProps {
resyncAllRepos: (names: string[]) => void;
resyncAllRepos: (repos: IAppRepositoryKey[]) => void;
repos: IAppRepository[];
namespace: string;
}

export class AppRepoRefreshAllButton extends React.Component<IAppRepoRefreshAllButtonProps> {
Expand All @@ -24,10 +23,13 @@ export class AppRepoRefreshAllButton extends React.Component<IAppRepoRefreshAllB

private handleResyncAllClick = async () => {
if (this.props.repos) {
const repoNames = this.props.repos.map(repo => {
return repo.metadata.name;
const repos = this.props.repos.map(repo => {
return {
name: repo.metadata.name,
namespace: repo.metadata.namespace,
};
});
this.props.resyncAllRepos(repoNames);
this.props.resyncAllRepos(repos);
}
};
}
15 changes: 8 additions & 7 deletions dashboard/src/containers/RepoListContainer/RepoListContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ThunkDispatch } from "redux-thunk";
import actions from "../../actions";
import AppRepoList from "../../components/Config/AppRepoList";
import { definedNamespaces } from "../../shared/Namespace";
import { IStoreState } from "../../shared/types";
import { IAppRepositoryKey, IStoreState } from "../../shared/types";

function mapStateToProps({ config, namespace, repos }: IStoreState) {
let repoNamespace = config.namespace;
Expand All @@ -27,8 +27,8 @@ function mapStateToProps({ config, namespace, repos }: IStoreState) {

function mapDispatchToProps(dispatch: ThunkDispatch<IStoreState, null, Action>) {
return {
deleteRepo: async (name: string) => {
return dispatch(actions.repos.deleteRepo(name));
deleteRepo: async (name: string, namespace: string) => {
return dispatch(actions.repos.deleteRepo(name, namespace));
},
fetchRepos: async (namespace: string) => {
return dispatch(actions.repos.fetchRepos(namespace));
Expand All @@ -45,11 +45,12 @@ function mapDispatchToProps(dispatch: ThunkDispatch<IStoreState, null, Action>)
actions.repos.installRepo(name, namespace, url, authHeader, customCA, syncJobPodTemplate),
);
},
resyncRepo: async (name: string) => {
return dispatch(actions.repos.resyncRepo(name));
resyncRepo: async (name: string, namespace: string) => {
return dispatch(actions.repos.resyncRepo(name, namespace));
},
resyncAllRepos: async (names: string[]) => {
return dispatch(actions.repos.resyncAllRepos(names));
// Update here after actions
resyncAllRepos: async (repos: IAppRepositoryKey[]) => {
return dispatch(actions.repos.resyncAllRepos(repos));
},
};
}
Expand Down
5 changes: 5 additions & 0 deletions dashboard/src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,11 @@ export interface IAppRepositoryList
}
> {}

export interface IAppRepositoryKey {
name: string;
namespace: string;
}

/** @see https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#response-status-kind */
export interface IStatus extends IK8sResource {
kind: "Status";
Expand Down

0 comments on commit 7df8971

Please sign in to comment.