Skip to content

Commit

Permalink
Merge pull request vmware-tanzu#25 from prydonius/2.0-chart-install
Browse files Browse the repository at this point in the history
add ChartDeployButton component for deploying charts
  • Loading branch information
prydonius committed Jan 18, 2018
2 parents a3c3765 + 63503d5 commit e56b4d9
Show file tree
Hide file tree
Showing 10 changed files with 195 additions and 26 deletions.
2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -3,12 +3,14 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@types/react-modal": "^3.1.1",
"@types/react-redux": "^5.0.14",
"@types/react-router": "^4.0.20",
"@types/react-router-dom": "^4.2.3",
"@types/react-router-redux": "^5.0.11",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-modal": "^3.1.11",
"react-redux": "^5.0.6",
"react-router-dom": "^4.2.2",
"react-router-redux": "^5.0.0-alpha.9",
Expand Down
27 changes: 27 additions & 0 deletions src/actions/index.ts
Expand Up @@ -35,5 +35,32 @@ export function getChart(id: string) {
};
}

export function deployChart(chart: Chart, releaseName: string, namespace: string) {
return (dispatch: Dispatch<StoreState>): Promise<{}> => {
return fetch(`/api/kube/apis/helm.bitnami.com/v1/namespaces/${namespace}/helmreleases`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
apiVersion: 'helm.bitnami.com/v1',
kind: 'HelmRelease',
metadata: {
name: releaseName,
},
spec: {
repoUrl: chart.attributes.repo.url,
chartName: chart.attributes.name,
version: chart.relationships.latestChartVersion.data.version,
}
}),
}).then(response => response.json())
.then(json => {
if (json.status === 'Failure') {
throw new Error(json.message);
}
return json;
});
};
}

const allActions = [requestCharts, receiveCharts, selectChart].map(getReturnOfExpression);
export type RootAction = typeof allActions[number];
110 changes: 110 additions & 0 deletions src/components/ChartDeployButton.tsx
@@ -0,0 +1,110 @@
import * as React from 'react';
import { Chart } from '../store/types';
import { RouterAction } from 'react-router-redux';
import * as Modal from 'react-modal';

interface Props {
chart: Chart;
deployChart: (chart: Chart, releaseName: string, namespace: string) => Promise<{}>;
push: (location: string) => RouterAction;
}

interface State {
isDeploying: boolean;
modalIsOpen: boolean;
// deployment options
releaseName: string;
namespace: string;
error: string | null;
}

class ChartDeployButton extends React.Component<Props> {
state: State = {
isDeploying: false,
modalIsOpen: false,
releaseName: '',
namespace: 'default',
error: null,
};

render() {
return (
<div className="ChartDeployButton">
{this.state.isDeploying &&
<div>Deploying...</div>
}
<button
className="button button-primary"
onClick={this.openModel}
disabled={this.state.isDeploying}
>
Deploy
</button>
<Modal
isOpen={this.state.modalIsOpen}
onRequestClose={this.closeModal}
contentLabel="Modal"
>
{this.state.error &&
<div className="container padding-v-bigger bg-action">
{this.state.error}
</div>}
<form onSubmit={this.handleDeploy}>
<div>
<label htmlFor="releaseName">Name</label>
<input
id="releaseName"
onChange={this.handleReleaseNameChange}
value={this.state.releaseName}
required={true}
/>
</div>
<div>
<label htmlFor="namespace">Namespace</label>
<input
name="namespace"
onChange={this.handleNamespaceChange}
value={this.state.namespace}
/>
</div>
<div>
<button className="button button-primary" type="submit">Submit</button>
<button className="button" onClick={this.closeModal}>Cancel</button>
</div>
</form>
</Modal>
</div>
);
}

openModel = () => {
this.setState({
modalIsOpen: true
});
}

closeModal = () => {
this.setState({
modalIsOpen: false
});
}

handleDeploy = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const { chart, deployChart, push } = this.props;
this.setState({isDeploying: true});
const { releaseName, namespace } = this.state;
deployChart(chart, releaseName, namespace)
.then(() => push(`/apps/${releaseName}`))
.catch(err => this.setState({isDeploying: false, error: err.toString()}));
}

handleReleaseNameChange = (e: React.FormEvent<HTMLInputElement>) => {
this.setState({ releaseName: e.currentTarget.value });
}
handleNamespaceChange = (e: React.FormEvent<HTMLInputElement>) => {
this.setState({ namespace: e.currentTarget.value });
}
}

export default ChartDeployButton;
4 changes: 2 additions & 2 deletions src/components/ChartList.tsx
@@ -1,13 +1,13 @@
import * as React from 'react';

import './ChartList.css';
import { ChartState, AsyncAction } from '../store/types';
import { ChartState } from '../store/types';
import ChartListItem from './ChartListItem';

interface Props {
charts: ChartState;
repo: string;
fetchCharts: (repo: string) => AsyncAction;
fetchCharts: (repo: string) => Promise<{}>;
}

class ChartList extends React.Component<Props> {
Expand Down
22 changes: 18 additions & 4 deletions src/components/ChartView.tsx
@@ -1,9 +1,13 @@
import * as React from 'react';
import { AsyncAction, Chart, ChartVersion } from '../store/types';
import { Chart, ChartVersion } from '../store/types';
import ChartDeployButton from './ChartDeployButton';
import { RouterAction } from 'react-router-redux';

interface Props {
chartID: string;
getChart: (id: string) => AsyncAction;
getChart: (id: string) => Promise<{}>;
deployChart: (chart: Chart, releaseName: string, namespace: string) => Promise<{}>;
push: (location: string) => RouterAction;
isFetching: boolean;
chart: Chart;
version: ChartVersion;
Expand All @@ -16,11 +20,21 @@ class ChartView extends React.Component<Props> {
}

render() {
const { isFetching, chart } = this.props;
const { isFetching, chart, deployChart, push } = this.props;
if (isFetching || !chart) {
return <div>Loading</div>;
}
return <div>{chart.id}</div>;
return (
<section className="ChartListView">
<header className="ChartListView__header">
<h1>{chart.id}</h1>
<hr />
</header>
<main className="text-c">
<ChartDeployButton push={push} chart={chart} deployChart={deployChart} />
</main>
</section>
);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/Layout.tsx
Expand Up @@ -15,7 +15,7 @@ class Layout extends React.Component {
<div className="col-1">
<Sidebar />
</div>
<div className="col-10">
<div className="col-11">
{this.props.children}
</div>
</div>
Expand Down
10 changes: 4 additions & 6 deletions src/containers/ChartListContainer.tsx
@@ -1,5 +1,5 @@
import { connect } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';
import { Dispatch } from 'redux';

import * as actions from '../actions';
import ChartList from '../components/ChartList';
Expand All @@ -21,11 +21,9 @@ function mapStateToProps({ charts }: StoreState, { match: { params } }: RoutePro
}

function mapDispatchToProps(dispatch: Dispatch<StoreState>) {
return bindActionCreators(
{
fetchCharts: actions.fetchCharts
},
dispatch);
return {
fetchCharts: (repo: string) => dispatch(actions.fetchCharts(repo))
};
}

export default connect(mapStateToProps, mapDispatchToProps)(ChartList);
16 changes: 9 additions & 7 deletions src/containers/ChartViewContainer.tsx
@@ -1,9 +1,10 @@
import { connect } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';
import { Dispatch } from 'redux';

import * as actions from '../actions';
import ChartView from '../components/ChartView';
import { StoreState } from '../store/types';
import { Chart, StoreState } from '../store/types';
import { push } from 'react-router-redux';

interface RouteProps {
match: {
Expand All @@ -24,11 +25,12 @@ function mapStateToProps({ charts }: StoreState, { match: { params } }: RoutePro
}

function mapDispatchToProps(dispatch: Dispatch<StoreState>) {
return bindActionCreators(
{
getChart: actions.getChart
},
dispatch);
return {
getChart: (id: string) => dispatch(actions.getChart(id)),
deployChart: (chart: Chart, releaseName: string, namespace: string) =>
dispatch(actions.deployChart(chart, releaseName, namespace)),
push: (location: string) => dispatch(push(location)),
};
}

export default connect(mapStateToProps, mapDispatchToProps)(ChartView);
10 changes: 4 additions & 6 deletions src/store/types.ts
@@ -1,8 +1,6 @@
import { Dispatch } from 'redux';

export interface ChartVersion {
id: string;
attributes: ChartAttributes;
attributes: ChartVersionAttributes;
relationships: {
chart: {
data: ChartAttributes;
Expand Down Expand Up @@ -32,7 +30,9 @@ export interface ChartAttributes {
icon: string;
keywords: string[];
maintainers: {}[];
repo: {};
repo: {
url: string;
};
sources: string[];
}

Expand All @@ -46,5 +46,3 @@ export interface ChartState {
export interface StoreState {
charts: ChartState;
}

export type AsyncAction = (dispatch: Dispatch<StoreState>) => Promise<{}>;
18 changes: 18 additions & 0 deletions yarn.lock
Expand Up @@ -21,6 +21,12 @@
"@types/node" "*"
"@types/react" "*"

"@types/react-modal@^3.1.1":
version "3.1.1"
resolved "https://registry.yarnpkg.com/@types/react-modal/-/react-modal-3.1.1.tgz#cba50fc253560bbefdbc2cbd5dda9ed6aadd1917"
dependencies:
"@types/react" "*"

"@types/react-redux@^5.0.14":
version "5.0.14"
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-5.0.14.tgz#f3fc30dcbb2d20455a714f591cc27f77b4df09bb"
Expand Down Expand Up @@ -1682,6 +1688,10 @@ execa@^0.7.0:
signal-exit "^3.0.0"
strip-eof "^1.0.0"

exenv@^1.2.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"

expand-brackets@^0.1.4:
version "0.1.5"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
Expand Down Expand Up @@ -4388,6 +4398,14 @@ react-error-overlay@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-3.0.0.tgz#c2bc8f4d91f1375b3dad6d75265d51cd5eeaf655"

react-modal@^3.1.11:
version "3.1.11"
resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.1.11.tgz#95c8223fcee7013258ad2d149c38c9f870c89958"
dependencies:
exenv "^1.2.0"
prop-types "^15.5.10"
warning "^3.0.0"

react-redux@^5.0.6:
version "5.0.6"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.6.tgz#23ed3a4f986359d68b5212eaaa681e60d6574946"
Expand Down

0 comments on commit e56b4d9

Please sign in to comment.