|
|
@@ -1,160 +1,143 @@
|
|
|
-import React, { useContext, useEffect, useState } from "react";
|
|
|
+import React, { Component } from "react";
|
|
|
+import styled from "styled-components";
|
|
|
import { Context } from "shared/Context";
|
|
|
-import { useHistory, useLocation, useRouteMatch } from "react-router";
|
|
|
+import { RouteComponentProps, withRouter } from "react-router";
|
|
|
|
|
|
import {
|
|
|
+ ResourceType,
|
|
|
ChartType,
|
|
|
StorageType,
|
|
|
+ ClusterType,
|
|
|
} from "shared/types";
|
|
|
import api from "shared/api";
|
|
|
-import { pushFiltered } from "shared/routing";
|
|
|
+import { PorterUrl, pushQueryParams, pushFiltered } from "shared/routing";
|
|
|
import ExpandedJobChart from "./ExpandedJobChart";
|
|
|
import ExpandedChart from "./ExpandedChart";
|
|
|
import Loading from "components/Loading";
|
|
|
import PageNotFound from "components/PageNotFound";
|
|
|
-import { useWebsockets } from "shared/hooks/useWebsockets";
|
|
|
|
|
|
-
|
|
|
-type Props = {
|
|
|
+type PropsType = RouteComponentProps & {
|
|
|
setSidebar: (x: boolean) => void;
|
|
|
isMetricsInstalled: boolean;
|
|
|
-}
|
|
|
-
|
|
|
-const ExpandedChartWrapper: React.FunctionComponent<Props> = ({ setSidebar, isMetricsInstalled }) => {
|
|
|
- // Router based state
|
|
|
- const location = useLocation();
|
|
|
- const history = useHistory();
|
|
|
- const { params: {baseRoute, namespace, chartName}} = useRouteMatch<{baseRoute: string, namespace: string, chartName: string}>();
|
|
|
-
|
|
|
- // Context hooks
|
|
|
- const { currentCluster, currentProject} = useContext(Context)
|
|
|
-
|
|
|
- // Component state
|
|
|
- const [revisions, setRevisions] = useState<ChartType[]>([]);
|
|
|
- const [currentChart, setCurrentChart] = useState<ChartType | null>(null);
|
|
|
- const [isLoading, setIsLoading] = useState<boolean>(true);
|
|
|
-
|
|
|
- // Websocket hook
|
|
|
- const { newWebsocket, openWebsocket, closeAllWebsockets, closeWebsocket } = useWebsockets();
|
|
|
-
|
|
|
- useEffect(() => {
|
|
|
- getRevisions()
|
|
|
- .then(() => {
|
|
|
- connectToChartLiveUpdates();
|
|
|
- setIsLoading(false);
|
|
|
- })
|
|
|
-
|
|
|
- return () => closeAllWebsockets();
|
|
|
- }, [currentCluster.id, currentProject.id])
|
|
|
-
|
|
|
-
|
|
|
- useEffect(() => {
|
|
|
- setCurrentChart(revisions[0])
|
|
|
- }, [revisions])
|
|
|
-
|
|
|
- const getRevisions = async () => {
|
|
|
- try {
|
|
|
- if (currentProject && currentCluster) {
|
|
|
- const res = await api.getRevisions(
|
|
|
+};
|
|
|
+
|
|
|
+type StateType = {
|
|
|
+ loading: boolean;
|
|
|
+ currentChart: ChartType;
|
|
|
+};
|
|
|
+
|
|
|
+class ExpandedChartWrapper extends Component<PropsType, StateType> {
|
|
|
+ state = {
|
|
|
+ loading: true,
|
|
|
+ currentChart: null as ChartType,
|
|
|
+ };
|
|
|
+
|
|
|
+ // Retrieve full chart data (includes form and values)
|
|
|
+ getChartData = () => {
|
|
|
+ let { match } = this.props;
|
|
|
+ let { namespace, chartName } = match.params as any;
|
|
|
+ let { currentProject, currentCluster } = this.context;
|
|
|
+ if (currentProject && currentCluster) {
|
|
|
+ // TODO: add query for retrieving max revision #
|
|
|
+ api
|
|
|
+ .getRevisions(
|
|
|
"<token>",
|
|
|
- {
|
|
|
- namespace: namespace,
|
|
|
- cluster_id: currentCluster.id,
|
|
|
- storage: StorageType.Secret,
|
|
|
- },
|
|
|
- { id: currentProject.id, name: chartName }
|
|
|
- );
|
|
|
-
|
|
|
- res.data.sort((a: ChartType, b: ChartType) => {
|
|
|
- return -(a.version - b.version);
|
|
|
- });
|
|
|
-
|
|
|
- setRevisions(res.data);
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.log("err", error.response.data);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- const connectToChartLiveUpdates = () => {
|
|
|
- const apiPath = `/api/projects/${currentProject.id}/k8s/helm_releases?cluster_id=${currentCluster.id}&charts=${chartName}`;
|
|
|
-
|
|
|
- const wsConfig = {
|
|
|
- onopen: () => console.log("connected to live chart updates websocket"),
|
|
|
- onmessage: (evt: MessageEvent) => {
|
|
|
- const event = JSON.parse(evt.data);
|
|
|
- // We ignore ADD events as the initial fetch of revisions will get all of those
|
|
|
- if (event.event_type === "UPDATE") {
|
|
|
- const object = event.Object as ChartType;
|
|
|
- setRevisions((oldRevisions) => {
|
|
|
- // Copy old array to clean up references
|
|
|
- const prevRevisions = [...oldRevisions];
|
|
|
-
|
|
|
- // Check if it's an update of a revision or if it's a new one
|
|
|
- const revisionIndex = prevRevisions.findIndex((rev) => {
|
|
|
- if (rev.version === object.version) {
|
|
|
- return true;
|
|
|
+ {
|
|
|
+ namespace: namespace,
|
|
|
+ cluster_id: currentCluster.id,
|
|
|
+ storage: StorageType.Secret,
|
|
|
+ },
|
|
|
+ { id: currentProject.id, name: chartName }
|
|
|
+ )
|
|
|
+ .then((res) => {
|
|
|
+ res.data.sort((a: ChartType, b: ChartType) => {
|
|
|
+ return -(a.version - b.version);
|
|
|
+ });
|
|
|
+ let maxVersion = res.data[0].version;
|
|
|
+ api
|
|
|
+ .getChart(
|
|
|
+ "<token>",
|
|
|
+ {
|
|
|
+ namespace: namespace,
|
|
|
+ cluster_id: currentCluster.id,
|
|
|
+ storage: StorageType.Secret,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: chartName,
|
|
|
+ revision: maxVersion,
|
|
|
+ id: currentProject.id,
|
|
|
}
|
|
|
+ )
|
|
|
+ .then((res) => {
|
|
|
+ this.setState({ currentChart: res.data, loading: false });
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ console.log("err", err.response.data);
|
|
|
+ this.setState({ loading: false });
|
|
|
});
|
|
|
-
|
|
|
- // Place new one at top of the array or update the old one
|
|
|
- if (revisionIndex > -1) {
|
|
|
- prevRevisions.splice(revisionIndex, 1, object);
|
|
|
- } else {
|
|
|
- return [object, ...prevRevisions];
|
|
|
- }
|
|
|
-
|
|
|
- return prevRevisions;
|
|
|
- })
|
|
|
- }
|
|
|
- },
|
|
|
- onclose: () => console.log("closing live chart updates websocket"),
|
|
|
- onerror: (err: ErrorEvent) => {
|
|
|
- console.log(err);
|
|
|
- closeWebsocket(chartName);
|
|
|
- }
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ console.log("err", err.response.data);
|
|
|
+ this.setState({ loading: false });
|
|
|
+ });
|
|
|
}
|
|
|
+ };
|
|
|
|
|
|
- newWebsocket(chartName, apiPath, wsConfig);
|
|
|
- openWebsocket(chartName);
|
|
|
+ componentDidMount() {
|
|
|
+ this.setState({ loading: true });
|
|
|
+ this.getChartData();
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- if (isLoading) {
|
|
|
- return <Loading />;
|
|
|
- } else if (currentChart && baseRoute === "jobs") {
|
|
|
- return (
|
|
|
- <ExpandedJobChart
|
|
|
- namespace={namespace}
|
|
|
- currentChart={currentChart}
|
|
|
- currentCluster={currentCluster}
|
|
|
- closeChart={() =>
|
|
|
- pushFiltered({location, history}, "/jobs", ["project_id"], {
|
|
|
- cluster: currentCluster.name,
|
|
|
- namespace: namespace,
|
|
|
- })
|
|
|
- }
|
|
|
- setSidebar={setSidebar}
|
|
|
- />
|
|
|
- );
|
|
|
- } else if (currentChart && baseRoute === "applications") {
|
|
|
- return (
|
|
|
- <ExpandedChart
|
|
|
- namespace={namespace}
|
|
|
- isMetricsInstalled={isMetricsInstalled}
|
|
|
- currentChart={currentChart}
|
|
|
- currentCluster={currentCluster}
|
|
|
- closeChart={() =>
|
|
|
- pushFiltered({location, history}, "/applications", ["project_id"], {
|
|
|
- cluster: currentCluster.name,
|
|
|
- namespace: namespace,
|
|
|
- })
|
|
|
- }
|
|
|
- setSidebar={setSidebar}
|
|
|
- />
|
|
|
- );
|
|
|
+ render() {
|
|
|
+ let { setSidebar, location, match } = this.props;
|
|
|
+ let { baseRoute, namespace } = match.params as any;
|
|
|
+ let { loading, currentChart } = this.state;
|
|
|
+ if (loading) {
|
|
|
+ return <Loading />;
|
|
|
+ } else if (currentChart && baseRoute === "jobs") {
|
|
|
+ return (
|
|
|
+ <ExpandedJobChart
|
|
|
+ namespace={namespace}
|
|
|
+ currentChart={currentChart}
|
|
|
+ currentCluster={this.context.currentCluster}
|
|
|
+ closeChart={() =>
|
|
|
+ pushFiltered(this.props, "/jobs", ["project_id"], {
|
|
|
+ cluster: this.context.currentCluster.name,
|
|
|
+ namespace: namespace,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ setSidebar={setSidebar}
|
|
|
+ />
|
|
|
+ );
|
|
|
+ } else if (currentChart && baseRoute === "applications") {
|
|
|
+ return (
|
|
|
+ <ExpandedChart
|
|
|
+ namespace={namespace}
|
|
|
+ isMetricsInstalled={this.props.isMetricsInstalled}
|
|
|
+ currentChart={currentChart}
|
|
|
+ currentCluster={this.context.currentCluster}
|
|
|
+ closeChart={() =>
|
|
|
+ pushFiltered(this.props, "/applications", ["project_id"], {
|
|
|
+ cluster: this.context.currentCluster.name,
|
|
|
+ namespace: namespace,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ setSidebar={setSidebar}
|
|
|
+ />
|
|
|
+ );
|
|
|
+ }
|
|
|
+ return <PageNotFound />;
|
|
|
}
|
|
|
- return <PageNotFound />;
|
|
|
}
|
|
|
|
|
|
-export default ExpandedChartWrapper;
|
|
|
+ExpandedChartWrapper.contextType = Context;
|
|
|
+
|
|
|
+export default withRouter(ExpandedChartWrapper);
|
|
|
+
|
|
|
+const NotFoundPlaceholder = styled.div`
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+`;
|