From 996e053f2618d9e42500b9cbde61bb07d320d01e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serta=C3=A7=20=C3=96zercan?= Date: Thu, 15 Feb 2018 13:51:59 +0100 Subject: [PATCH] Service Instances view (#79) * service instances view * updating class list view * moving provision button * renaming instance list view container --- src/components/BrokerView/index.tsx | 95 --------------------- src/components/ClassList/index.tsx | 7 +- src/components/Header/Header.tsx | 4 + src/components/InstanceListView/index.tsx | 85 ++++++++++++++++++ src/containers/BrokerView.ts | 56 ------------ src/containers/ClassListContainer.ts | 12 +-- src/containers/InstanceListViewContainer.ts | 37 ++++++++ src/containers/Root.tsx | 9 +- 8 files changed, 136 insertions(+), 169 deletions(-) delete mode 100644 src/components/BrokerView/index.tsx create mode 100644 src/components/InstanceListView/index.tsx delete mode 100644 src/containers/BrokerView.ts create mode 100644 src/containers/InstanceListViewContainer.ts diff --git a/src/components/BrokerView/index.tsx b/src/components/BrokerView/index.tsx deleted file mode 100644 index b28e7ebb54c..00000000000 --- a/src/components/BrokerView/index.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import * as React from "react"; -import { Link } from "react-router-dom"; - -import { IClusterServiceClass } from "../../shared/ClusterServiceClass"; -import { IServiceBinding } from "../../shared/ServiceBinding"; -import { IServiceBroker, IServicePlan } from "../../shared/ServiceCatalog"; -import { IServiceInstance } from "../../shared/ServiceInstance"; -import SyncButton from "../SyncButton"; - -export interface IBrokerViewProps { - bindings: IServiceBinding[]; - broker: IServiceBroker | undefined; - classes: IClusterServiceClass[]; - getCatalog: () => Promise; - instances: IServiceInstance[]; - plans: IServicePlan[]; - sync: (broker: IServiceBroker) => Promise; - deprovision: (instance: IServiceInstance) => Promise; -} - -export class BrokerView extends React.PureComponent { - public async componentDidMount() { - this.props.getCatalog(); - } - - public render() { - const { broker, instances } = this.props; - - return ( -
- {broker && ( -
-

{broker.metadata.name}

-
Catalog last updated at {broker.status.lastCatalogRetrievalTime}
-
- - - - -
-

Service Instances

-

Most recent statuses for your brokers:

- - - - - - - - - - {instances.map(instance => { - const conditions = [...instance.status.conditions]; - const status = conditions.shift(); // first in list is most recent - const reason = status ? status.reason : ""; - const message = status ? status.message : ""; - const { name, namespace } = instance.metadata; - - return ( - - - - - - - ); - })} - -
InstanceStatusMessage -
- - {instance.metadata.namespace}/{instance.metadata.name} - - - {reason} - - {message} - -
- - - -
-
-
- )} -
- ); - } -} diff --git a/src/components/ClassList/index.tsx b/src/components/ClassList/index.tsx index 07d2d307144..7560562b6ce 100644 --- a/src/components/ClassList/index.tsx +++ b/src/components/ClassList/index.tsx @@ -5,7 +5,6 @@ import { IServiceBroker } from "../../shared/ServiceCatalog"; import { Card, CardContainer } from "../Card"; export interface IClassListProps { - broker: IServiceBroker | undefined; classes: IClusterServiceClass[]; getBrokers: () => Promise; getClasses: () => Promise; @@ -22,7 +21,7 @@ export class ClassList extends React.Component { return (

Classes

-

Types of services available from this broker

+

Types of services available from all brokers

{classes .sort((a, b) => a.spec.externalName.localeCompare(b.spec.externalName)) @@ -45,7 +44,9 @@ export class ClassList extends React.Component { icon={imageUrl} body={description} buttonText="View Plans" - linkTo={`${location.pathname}/${svcClass.spec.externalName}`} + linkTo={`/services/brokers/${svcClass.spec.clusterServiceBrokerName}/classes/${ + svcClass.spec.externalName + }`} notes={ Tags: {tags} diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index 584953666ab..913d4390125 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -38,6 +38,10 @@ class Header extends React.Component { external: true, to: "/kubeless/", }, + { + children: "Service Instances", + to: "/services/instances", + }, ]; constructor(props: any) { diff --git a/src/components/InstanceListView/index.tsx b/src/components/InstanceListView/index.tsx new file mode 100644 index 00000000000..f8f69c51a13 --- /dev/null +++ b/src/components/InstanceListView/index.tsx @@ -0,0 +1,85 @@ +import * as React from "react"; + +import { Link } from "react-router-dom"; +import { IClusterServiceClass } from "../../shared/ClusterServiceClass"; +import { IServiceBroker, IServicePlan } from "../../shared/ServiceCatalog"; +import { IServiceInstance } from "../../shared/ServiceInstance"; +import { Card, CardContainer } from "../Card"; + +export interface InstanceListViewProps { + brokers: IServiceBroker[]; + classes: IClusterServiceClass[]; + getCatalog: () => Promise; + instances: IServiceInstance[]; + plans: IServicePlan[]; +} + +export class InstanceListView extends React.PureComponent { + public async componentDidMount() { + this.props.getCatalog(); + } + + public render() { + const { brokers, instances, classes } = this.props; + + return ( +
+ {brokers && ( +
+
+
+

Service Instances

+

Service instances from your brokers:

+
+
+ + + +
+
+ + + + {instances.length > 0 && + instances.map(instance => { + const conditions = [...instance.status.conditions]; + const status = conditions.shift(); // first in list is most recent + const message = status ? status.message : ""; + const svcClass = classes.find( + potential => + potential.metadata.name === instance.spec.clusterServiceClassRef.name, + ); + const broker = svcClass && svcClass.spec.clusterServiceBrokerName; + const icon = + svcClass && + svcClass.spec.externalMetadata && + svcClass.spec.externalMetadata.imageUrl; + + const card = ( + + {instance.metadata.namespace}/{instance.metadata.name} + + } + icon={icon} + body={message} + buttonText="Details" + linkTo={`/services/brokers/${broker}/instances/${ + instance.metadata.namespace + }/${instance.metadata.name}/`} + notes={{instance.spec.clusterServicePlanExternalName}} + /> + ); + return card; + })} + + +
+
+ )} +
+ ); + } +} diff --git a/src/containers/BrokerView.ts b/src/containers/BrokerView.ts deleted file mode 100644 index ed90d72ac2b..00000000000 --- a/src/containers/BrokerView.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { connect } from "react-redux"; -import { Dispatch } from "redux"; - -import actions from "../actions"; -import { BrokerView } from "../components/BrokerView"; -import { IServiceBroker } from "../shared/ServiceCatalog"; -import { IStoreState } from "../shared/types"; - -interface IRouteProps { - match: { - params: { - brokerName: string; - }; - }; -} - -function mapStateToProps({ catalog }: IStoreState, { match: { params } }: IRouteProps) { - const broker = - catalog.brokers.find( - potental => !!potental.metadata.name.match(new RegExp(params.brokerName, "i")), - ) || undefined; - const plans = broker - ? catalog.plans.filter( - plan => !!plan.spec.clusterServiceBrokerName.match(new RegExp(broker.metadata.name, "i")), - ) - : []; - const classes = broker - ? catalog.classes.filter( - serviceClass => - !!serviceClass.spec.clusterServiceBrokerName.match(new RegExp(broker.metadata.name, "i")), - ) - : []; - const instances = broker ? catalog.instances : []; - const bindings = broker ? catalog.bindings : []; - return { - bindings, - broker, - classes, - instances, - plans, - }; -} - -function mapDispatchToProps(dispatch: Dispatch) { - return { - getCatalog: async () => { - dispatch(actions.catalog.getCatalog()); - }, - sync: async (broker: IServiceBroker) => { - await dispatch(actions.catalog.sync(broker)); - await dispatch(actions.catalog.getCatalog()); - }, - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(BrokerView); diff --git a/src/containers/ClassListContainer.ts b/src/containers/ClassListContainer.ts index edd09806ccc..45dc671729d 100644 --- a/src/containers/ClassListContainer.ts +++ b/src/containers/ClassListContainer.ts @@ -15,19 +15,9 @@ interface IRouteProps { } function mapStateToProps({ catalog }: IStoreState, props: IRouteProps) { - const broker = - catalog.brokers.find( - potental => !!potental.metadata.name.match(new RegExp(props.match.params.brokerName, "i")), - ) || undefined; - const classes = broker - ? catalog.classes.filter( - serviceClass => - !!serviceClass.spec.clusterServiceBrokerName.match(new RegExp(broker.metadata.name, "i")), - ) - : []; + const classes = catalog.classes; return { - broker, classes, }; } diff --git a/src/containers/InstanceListViewContainer.ts b/src/containers/InstanceListViewContainer.ts new file mode 100644 index 00000000000..e4e88673711 --- /dev/null +++ b/src/containers/InstanceListViewContainer.ts @@ -0,0 +1,37 @@ +import { connect } from "react-redux"; +import { Dispatch } from "redux"; + +import actions from "../actions"; +import { InstanceListView } from "../components/InstanceListView"; +import { IStoreState } from "../shared/types"; + +interface IRouteProps { + match: { + params: { + brokerName: string; + }; + }; +} + +function mapStateToProps({ catalog }: IStoreState, { match: { params } }: IRouteProps) { + const brokers = catalog.brokers; + const plans = catalog.plans; + const classes = catalog.classes; + const instances = catalog.instances; + return { + brokers, + classes, + instances, + plans, + }; +} + +function mapDispatchToProps(dispatch: Dispatch) { + return { + getCatalog: async () => { + dispatch(actions.catalog.getCatalog()); + }, + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(InstanceListView); diff --git a/src/containers/Root.tsx b/src/containers/Root.tsx index 3c7a8d4833d..74a9f7e004e 100644 --- a/src/containers/Root.tsx +++ b/src/containers/Root.tsx @@ -3,18 +3,19 @@ import * as React from "react"; import { Provider } from "react-redux"; import { Route, RouteComponentProps } from "react-router"; import { ConnectedRouter } from "react-router-redux"; +import { ClassViewContainer } from "./ClassView"; import Layout from "../components/Layout"; import configureStore from "../store"; import AppList from "./AppListContainer"; import AppNew from "./AppNewContainer"; import AppView from "./AppViewContainer"; -import BrokerView from "./BrokerView"; import ChartList from "./ChartListContainer"; import ChartView from "./ChartViewContainer"; import ClassListContainer from "./ClassListContainer"; -import { ClassViewContainer } from "./ClassView"; +import InstanceListViewContainer from "./InstanceListViewContainer"; import InstanceView from "./InstanceView"; + import RepoListContainer from "./RepoListContainer"; import ServiceCatalogContainer from "./ServiceCatalogContainer"; @@ -34,10 +35,10 @@ class Root extends React.Component { "/charts/:repo/:id/versions/:version": ChartView, "/config/brokers": ServiceCatalogContainer, "/config/repos": RepoListContainer, - "/services/brokers/:brokerName/classes": ClassListContainer, "/services/brokers/:brokerName/classes/:className": ClassViewContainer, "/services/brokers/:brokerName/instances/:namespace/:instanceName": InstanceView, - "/services/brokers/:name": BrokerView, + "/services/classes": ClassListContainer, + "/services/instances": InstanceListViewContainer, }; public render() {