Skip to content

Commit

Permalink
Update redux namespace(s) state to clusters state. (#1804)
Browse files Browse the repository at this point in the history
* Update namespace reducer to support multiple clusters.
  • Loading branch information
absoludity committed Jun 22, 2020
1 parent 0b45b6a commit ad182b5
Show file tree
Hide file tree
Showing 29 changed files with 445 additions and 292 deletions.
18 changes: 16 additions & 2 deletions dashboard/src/actions/repos.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ spec:
beforeEach(() => {
store = mockStore({
config: { namespace: kubeappsNamespace },
namespace: { current: kubeappsNamespace },
clusters: {
currentCluster: "default",
clusters: {
default: {
currentNamespace: kubeappsNamespace,
},
},
},
});
AppRepository.list = jest.fn().mockImplementationOnce(() => {
return { items: { foo: "bar" } };
Expand Down Expand Up @@ -100,7 +107,14 @@ describe("deleteRepo", () => {
const currentNamespace = "current-namespace";
it("dispatches requestRepos with current namespace", async () => {
const storeWithFlag: any = mockStore({
namespace: { current: currentNamespace },
clusters: {
currentCluster: "default",
clusters: {
default: {
currentNamespace,
},
},
},
});
const expectedActions = [
{
Expand Down
8 changes: 6 additions & 2 deletions dashboard/src/actions/repos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,15 @@ export const deleteRepo = (
): ThunkAction<Promise<boolean>, IStoreState, null, AppReposAction> => {
return async (dispatch, getState) => {
const {
namespace: { current },
clusters: {
clusters: {
default: { currentNamespace },
},
},
} = getState();
try {
await AppRepository.delete(name, namespace);
dispatch(fetchRepos(current));
dispatch(fetchRepos(currentNamespace));
return true;
} catch (e) {
dispatch(errorRepos(e, "delete"));
Expand Down
24 changes: 11 additions & 13 deletions dashboard/src/components/Header/Header.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { shallow } from "enzyme";
import * as React from "react";
import { INamespaceState } from "../../reducers/namespace";
import { IClusterState } from "../../reducers/cluster";
import { app } from "../../shared/url";
import Header from "./Header";

Expand All @@ -9,10 +9,10 @@ const defaultProps = {
authenticated: true,
fetchNamespaces: jest.fn(),
logout: jest.fn(),
namespace: {
current: "default",
cluster: {
currentNamespace: "default",
namespaces: ["default", "other"],
} as INamespaceState,
} as IClusterState,
defaultNamespace: "kubeapps-user",
pathname: "",
push: jest.fn(),
Expand Down Expand Up @@ -87,7 +87,7 @@ it("renders the namespace switcher", () => {
expect(namespaceSelector.props()).toEqual(
expect.objectContaining({
defaultNamespace: defaultProps.defaultNamespace,
namespace: defaultProps.namespace,
cluster: defaultProps.cluster,
}),
);
});
Expand All @@ -96,16 +96,15 @@ it("call setNamespace and getNamespace when selecting a namespace", () => {
const setNamespace = jest.fn();
const createNamespace = jest.fn();
const getNamespace = jest.fn();
const namespace = {
cluster: "default",
current: "foo",
const cluster = {
currentNamespace: "foo",
namespaces: ["foo", "bar"],
};
const wrapper = shallow(
<Header
{...defaultProps}
setNamespace={setNamespace}
namespace={namespace}
cluster={cluster}
createNamespace={createNamespace}
getNamespace={getNamespace}
/>,
Expand All @@ -124,16 +123,15 @@ it("call setNamespace and getNamespace when selecting a namespace", () => {
it("doesn't call getNamespace when selecting all namespaces", () => {
const setNamespace = jest.fn();
const getNamespace = jest.fn();
const namespace = {
cluster: "defalut",
current: "foo",
const cluster = {
currentNamespace: "foo",
namespaces: ["foo", "bar"],
};
const wrapper = shallow(
<Header
{...defaultProps}
setNamespace={setNamespace}
namespace={namespace}
cluster={cluster}
getNamespace={getNamespace}
/>,
);
Expand Down
18 changes: 9 additions & 9 deletions dashboard/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { LogOut, Settings } from "react-feather";
import { NavLink } from "react-router-dom";
import "react-select/dist/react-select.css";
import logo from "../../logo.svg";
import { INamespaceState } from "../../reducers/namespace";
import { IClusterState } from "../../reducers/cluster";
import { definedNamespaces } from "../../shared/Namespace";
import { app } from "../../shared/url";
import "./Header.css";
Expand All @@ -14,7 +14,7 @@ interface IHeaderProps {
authenticated: boolean;
fetchNamespaces: () => void;
logout: () => void;
namespace: INamespaceState;
cluster: IClusterState;
defaultNamespace: string;
pathname: string;
push: (path: string) => void;
Expand Down Expand Up @@ -55,7 +55,7 @@ class Header extends React.Component<IHeaderProps, IHeaderState> {
public render() {
const {
fetchNamespaces,
namespace,
cluster,
defaultNamespace,
authenticated: showNav,
createNamespace,
Expand All @@ -66,7 +66,7 @@ class Header extends React.Component<IHeaderProps, IHeaderState> {
this.state.configOpen ? "header__nav__submenu-open" : ""
}`;

const reposPath = `/config/ns/${namespace.current}/repos`;
const reposPath = `/config/ns/${cluster.currentNamespace}/repos`;
return (
<section className="gradient-135-brand type-color-reverse type-color-reverse-anchor-reset">
<div className="container">
Expand All @@ -91,21 +91,21 @@ class Header extends React.Component<IHeaderProps, IHeaderState> {
</button>
<ul className="header__nav__menu" role="menubar">
<li>
<HeaderLink to={app.apps.list(namespace.current, namespace.cluster)} exact={true}>Applications</HeaderLink>
<HeaderLink to={app.apps.list(cluster.currentNamespace)} exact={true}>Applications</HeaderLink>
</li>
<li>
<HeaderLink to={app.catalog(namespace.current)}>Catalog</HeaderLink>
<HeaderLink to={app.catalog(cluster.currentNamespace)}>Catalog</HeaderLink>
</li>
<li>
<HeaderLink to={app.servicesInstances(namespace.current)}>Service Instances (alpha)</HeaderLink>
<HeaderLink to={app.servicesInstances(cluster.currentNamespace)}>Service Instances (alpha)</HeaderLink>
</li>
</ul>
</nav>
)}
{showNav && (
<div className="header__nav header__nav-config">
<NamespaceSelector
namespace={namespace}
cluster={cluster}
defaultNamespace={defaultNamespace}
onChange={this.handleNamespaceChange}
fetchNamespaces={fetchNamespaces}
Expand All @@ -130,7 +130,7 @@ class Header extends React.Component<IHeaderProps, IHeaderState> {
</li>
{this.props.featureFlags.operators && (
<li role="none">
<NavLink to={`/ns/${namespace.current}/operators`}>Operators</NavLink>
<NavLink to={`/ns/${cluster.currentNamespace}/operators`}>Operators</NavLink>
</li>
)}
</ul>
Expand Down
28 changes: 14 additions & 14 deletions dashboard/src/components/Header/NamespaceSelector.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,29 @@ import * as React from "react";
import * as ReactModal from "react-modal";
import * as Select from "react-select";

import { INamespaceState } from "../../reducers/namespace";
import NamespaceSelector from "./NamespaceSelector";
import { IClusterState } from "../../reducers/cluster";
import NamespaceSelector, { INamespaceSelectorProps } from "./NamespaceSelector";
import NewNamespace from "./NewNamespace";

const defaultProps = {
fetchNamespaces: jest.fn(),
namespace: {
current: "namespace-two",
cluster: {
currentNamespace: "namespace-two",
namespaces: ["namespace-one", "namespace-two"],
} as INamespaceState,
} as IClusterState,
defaultNamespace: "kubeapps-user",
onChange: jest.fn(),
createNamespace: jest.fn(),
getNamespace: jest.fn(),
};
} as INamespaceSelectorProps;

it("renders the given namespaces with current selection", () => {
const wrapper = shallow(<NamespaceSelector {...defaultProps} />);
const select = wrapper.find(".NamespaceSelector__select").first();

const expectedValue = {
label: defaultProps.namespace.current,
value: defaultProps.namespace.current,
label: defaultProps.cluster.currentNamespace,
value: defaultProps.cluster.currentNamespace,
};
expect(select.props()).toMatchObject({
value: expectedValue,
Expand All @@ -41,11 +41,11 @@ it("renders the given namespaces with current selection", () => {
it("render with the default namespace selected if no current selection", () => {
const props = {
...defaultProps,
namespace: {
...defaultProps.namespace,
current: "",
cluster: {
...defaultProps.cluster,
currentNamespace: "",
},
};
} as INamespaceSelectorProps;
const wrapper = shallow(<NamespaceSelector {...props} />);
const select = wrapper.find(".NamespaceSelector__select").first();

Expand Down Expand Up @@ -88,7 +88,7 @@ it("fetches namespaces and retrive the current namespace", () => {
{...defaultProps}
fetchNamespaces={fetchNamespaces}
getNamespace={getNamespace}
namespace={{ cluster: "default", current: "foo", namespaces: [] }}
cluster={{ currentNamespace: "foo", namespaces: [] }}
/>,
);
expect(fetchNamespaces).toHaveBeenCalled();
Expand All @@ -103,7 +103,7 @@ it("doesnt' get the current namespace if all namespaces is selected", () => {
{...defaultProps}
fetchNamespaces={fetchNamespaces}
getNamespace={getNamespace}
namespace={{ cluster: "default", current: "_all", namespaces: [] }}
cluster={{ currentNamespace: "_all", namespaces: [] }}
/>,
);
expect(fetchNamespaces).toHaveBeenCalled();
Expand Down
10 changes: 5 additions & 5 deletions dashboard/src/components/Header/NamespaceSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import * as React from "react";
import * as Select from "react-select";

import { INamespaceState } from "../../reducers/namespace";
import { IClusterState } from "../../reducers/cluster";
import { definedNamespaces } from "../../shared/Namespace";

import "./NamespaceSelector.css";
import NewNamespace from "./NewNamespace";

interface INamespaceSelectorProps {
namespace: INamespaceState;
export interface INamespaceSelectorProps {
cluster: IClusterState;
defaultNamespace: string;
onChange: (ns: string) => any;
fetchNamespaces: () => void;
Expand All @@ -28,7 +28,7 @@ class NamespaceSelector extends React.Component<INamespaceSelectorProps, INamesp
};

get selected() {
return this.props.namespace.current || this.props.defaultNamespace;
return this.props.cluster.currentNamespace || this.props.defaultNamespace;
}

public componentDidMount() {
Expand All @@ -40,7 +40,7 @@ class NamespaceSelector extends React.Component<INamespaceSelectorProps, INamesp

public render() {
const {
namespace: { namespaces, error },
cluster: { namespaces, error },
} = this.props;
const options = namespaces.length > 0 ? namespaces.map(n => ({ value: n, label: n })) : [];
const allOption = { value: definedNamespaces.all, label: "All Namespaces" };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import AppList from "../../components/AppList";
import { IStoreState } from "../../shared/types";

function mapStateToProps(
{ apps, namespace, operators, config }: IStoreState,
{ apps, clusters: { currentCluster, clusters}, operators, config }: IStoreState,
{ location }: RouteComponentProps<{}>,
) {
return {
apps,
filter: qs.parse(location.search, { ignoreQueryPrefix: true }).q || "",
cluster: namespace.cluster,
namespace: namespace.current,
cluster: currentCluster,
namespace: clusters[currentCluster].currentNamespace,
customResources: operators.resources,
isFetchingResources: operators.isFetching,
csvs: operators.csvs,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import { mount } from "enzyme";
import * as React from "react";
import { INamespaceState } from "reducers/namespace";
import { IClustersState } from "reducers/cluster";
import configureMockStore from "redux-mock-store";
import thunk from "redux-thunk";
import ErrorBoundaryContainer from ".";
import UnexpectedErrorPage from "../../components/ErrorAlert/UnexpectedErrorAlert";

const mockStore = configureMockStore([thunk]);
const makeStore = (error?: { action: string; error: Error }) => {
const state: INamespaceState = {
cluster: "default",
current: "default",
namespaces: ["default"],
error,
const state: IClustersState = {
currentCluster: "default",
clusters: {
default: {
currentNamespace: "default",
namespaces: ["default"],
error,
},
},
};
return mockStore({ namespace: state });
return mockStore({ clusters: state });
};

describe("LoginFormContainer props", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ interface IErrorBoundaryProps {
children: React.ReactChildren | React.ReactNode | string;
}

function mapStateToProps({ namespace }: IStoreState, { children }: IErrorBoundaryProps) {
function mapStateToProps({ clusters: { currentCluster, clusters } }: IStoreState, { children }: IErrorBoundaryProps) {
const cluster = clusters[currentCluster];
return {
error: namespace.error && namespace.error.error,
error: cluster.error && cluster.error.error,
children,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ const defaultState = {
config: {
featureFlags: { operators: true },
},
clusters: {
currentCluster: "default",
clusters: {
default: {
currentNamespace: "default",
namespaces: [],
},
},
},
};

describe("HeaderContainer props", () => {
Expand Down
4 changes: 2 additions & 2 deletions dashboard/src/containers/HeaderContainer/HeaderContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ interface IState extends IStoreState {

function mapStateToProps({
auth: { authenticated, defaultNamespace },
namespace,
clusters,
router: {
location: { pathname },
},
config: { featureFlags },
}: IState) {
return {
authenticated,
namespace,
cluster: clusters.clusters.default,
defaultNamespace,
pathname,
featureFlags,
Expand Down

0 comments on commit ad182b5

Please sign in to comment.