Merge pull request #248 from influxdata/feature/manage-sources

Feature/manage sources
pull/10616/head
Will Piers 2016-10-18 10:21:00 -07:00 committed by GitHub
commit cf68ca807f
7 changed files with 103 additions and 56 deletions

View File

@ -12,12 +12,13 @@ import RetentionPoliciesPage from 'src/retention_policies';
import DataExplorer from 'src/chronograf';
import DatabaseManager from 'src/database_manager';
import SignUp from 'src/sign_up';
import SelectSourcePage from 'src/select_source';
import {CreateSource, ManageSources} from 'src/sources';
import {ClusterAccountsPage, ClusterAccountPage} from 'src/cluster_accounts';
import {RolesPageContainer, RolePageContainer} from 'src/access_control';
import NotFound from 'src/shared/components/NotFound';
import NoClusterError from 'src/shared/components/NoClusterError';
import configureStore from 'src/store/configureStore';
import {getSources} from 'shared/apis';
import 'src/style/enterprise_style/application.scss';
@ -48,47 +49,6 @@ const Root = React.createClass({
};
},
componentDidMount() {
// meShow().then(({data: me}) => {
// const match = window.location.pathname.match(/\/clusters\/(\d*)/);
// const clusterID = match && match[1];
// const clusterLink = me.cluster_links.find(link => link.cluster_id === clusterID);
// if (clusterLink) {
// Promise.all([
// getClusterAccount(clusterID, clusterLink.cluster_user),
// getRoles(clusterID),
// ]).then(([{data: {users}}, {data: {roles}}]) => {
// const account = buildClusterAccounts(users, roles)[0];
// const canViewChronograf = hasPermission(account, VIEW_CHRONOGRAF);
// const hasReadPermission = hasPermission(account, READ);
// this.setState({
// me,
// canViewChronograf,
// isFetching: false,
// hasReadPermission,
// });
// }).catch((err) => {
// console.error(err); // eslint-disable-line no-console
// this.setState({
// canViewChronograf: false,
// isFetching: false,
// clusterStatus: err.response.status,
// });
// });
// } else {
// this.setState({
// me,
// isFetching: false,
// });
// }
// }).catch((err) => {
// console.error(err); // eslint-disable-line no-console
// this.setState({
// isFetching: false,
// });
// });
},
childContextTypes: {
me: shape({
id: number.isRequired,
@ -104,11 +64,17 @@ const Root = React.createClass({
};
},
hasDefaultSource(_, replace) {
const defaultSource = JSON.parse(localStorage.getItem('defaultSource'));
if (!!defaultSource && defaultSource.id) {
return replace(`/sources/${defaultSource.id}/hosts`);
}
hasSources(_, replace, callback) {
getSources().then(({data: {sources}}) => {
if (sources && sources.length) {
const defaultSource = sources.find((s) => s.default);
if (defaultSource && defaultSource.id) {
replace(`/sources/${defaultSource.id}/hosts`);
}
replace(`/sources/${sources[0].id}/hosts`);
}
callback();
}).catch(callback);
},
render() {
@ -124,10 +90,10 @@ const Root = React.createClass({
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/signup/admin/:step" component={SignUp} />
<Route path="/" component={SelectSourcePage} onEnter={this.hasDefaultSource} />
<Route path="/sources" component={SelectSourcePage} />
<Route path="/" component={CreateSource} onEnter={this.hasSources} />
<Route path="/sources/:sourceID" component={App}>
<Route component={CheckDataNodes}>
<Route path="manage-sources" component={ManageSources} />
<Route path="queries" component={QueriesPage} />
<Route path="accounts" component={ClusterAccountsPage} />
<Route path="accounts/:accountID" component={ClusterAccountPage} />

View File

@ -1,2 +0,0 @@
import SelectSourcePage from './containers/SelectSourcePage';
export default SelectSourcePage;

View File

@ -6,7 +6,7 @@ export function getSources() {
});
}
export function createSource({url, name, username, password}) {
export function createSource({url, name, username, password, isDefault}) {
return AJAX({
url: '/chronograf/v1/sources',
method: 'POST',
@ -15,6 +15,7 @@ export function createSource({url, name, username, password}) {
name,
username,
password,
'default': isDefault,
},
});
}

View File

@ -27,7 +27,7 @@ const SideNav = React.createClass({
</NavBlock>
<NavBlock matcher="overview" icon="crown" link={`${sourcePrefix}/overview`}>
<NavHeader link={`${sourcePrefix}/overview`} title="Sources" />
<NavListItem matcher="sources$" link={`/sources`}>Manage Sources</NavListItem>
<NavListItem matcher="manage-sources$" link={`${sourcePrefix}/manage-sources`}>Manage Sources</NavListItem>
<NavListItem matcher="queries" link={`${sourcePrefix}/queries`}>Queries</NavListItem>
<NavListItem matcher="tasks" link={`${sourcePrefix}/tasks`}>Tasks</NavListItem>
<NavListItem matcher="roles" link={`${sourcePrefix}/roles`}>Roles</NavListItem>

View File

@ -3,7 +3,7 @@ import {withRouter} from 'react-router';
import FlashMessages from 'shared/components/FlashMessages';
import {createSource} from 'shared/apis';
export const SelectSourcePage = React.createClass({
export const CreateSource = React.createClass({
propTypes: {
router: PropTypes.shape({
push: PropTypes.func.isRequired,
@ -22,9 +22,9 @@ export const SelectSourcePage = React.createClass({
name: this.sourceName.value,
username: this.sourceUser.value,
password: this.sourcePassword.value,
isDefault: true,
};
createSource(source).then(({data: sourceFromServer}) => {
localStorage.setItem('defaultSource', JSON.stringify(sourceFromServer));
this.redirectToApp(sourceFromServer);
});
},
@ -87,4 +87,4 @@ export const SelectSourcePage = React.createClass({
},
});
export default FlashMessages(withRouter(SelectSourcePage));
export default FlashMessages(withRouter(CreateSource));

View File

@ -0,0 +1,79 @@
import React, {PropTypes} from 'react';
import {withRouter, Link} from 'react-router';
import FlashMessages from 'shared/components/FlashMessages';
import {getSources} from 'shared/apis';
export const ManageSources = React.createClass({
propTypes: {
location: PropTypes.shape({
pathname: PropTypes.string.isRequired,
}).isRequired,
},
getInitialState() {
return {
sources: [],
};
},
componentDidMount() {
getSources().then(({data: {sources}}) => {
this.setState({sources});
});
},
changeSort() {},
render() {
const {sources} = this.state;
const {pathname} = this.props.location;
return (
<div id="manage-sources-page">
<div className="enterprise-header">
<div className="enterprise-header__container">
<div className="enterprise-header__left">
<h1>
Manage Sources
</h1>
</div>
</div>
</div>
<div className="container">
<div className="row">
<div className="col-md-8 col-md-offset-2">
<table className="table v-center">
<thead>
<tr>
<th onClick={this.changeSort} className="sortable-header">Name</th>
<th>Host</th>
<th>Kapacitor</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{
sources.map((source) => {
return (
<tr key={source.id}>
<td>{source.name}{source.default ? <span className="label label-primary">Default</span> : null}</td>
<td>{source.url}</td>
<td>{source.links.kapacitors}</td>
<td><Link className="btn btn-default btn-xs" to={`${pathname}/${source.id}/edit`}>Edit</Link></td>
<td><Link className="btn btn-success btn-xs" to={`/sources/${source.id}/hosts`}>Connect</Link></td>
</tr>
);
})
}
</tbody>
</table>
<div className="btn btn-primary">Add</div>
</div>
</div>
</div>
</div>
);
},
});
export default FlashMessages(withRouter(ManageSources));

3
ui/src/sources/index.js Normal file
View File

@ -0,0 +1,3 @@
import CreateSource from './containers/CreateSource';
import ManageSources from './containers/ManageSources';
export {CreateSource, ManageSources};