removing old API dependencies

pull/71/head
Jade McGough 2016-09-19 16:43:59 -07:00
parent 0a583b2492
commit 3a0b233d04
13 changed files with 112 additions and 218 deletions

View File

@ -4,9 +4,6 @@ import SideNavContainer from 'src/side_nav';
const App = React.createClass({
propTypes: {
params: PropTypes.shape({
clusterID: PropTypes.string.isRequired,
}).isRequired,
addFlashMessage: PropTypes.func.isRequired, // Injected by the `FlashMessages` wrapper
children: PropTypes.node.isRequired,
location: PropTypes.shape({
@ -15,11 +12,9 @@ const App = React.createClass({
},
render() {
const {clusterID} = this.props.params;
return (
<div className="enterprise-wrapper--flex">
<SideNavContainer addFlashMessage={this.props.addFlashMessage} clusterID={clusterID} currentLocation={this.props.location.pathname} />
<SideNavContainer addFlashMessage={this.props.addFlashMessage} currentLocation={this.props.location.pathname} />
<div className="page-wrapper">
{this.props.children && React.cloneElement(this.props.children, {
addFlashMessage: this.props.addFlashMessage,

View File

@ -2,16 +2,13 @@ import React, {PropTypes} from 'react';
import NoClusterError from 'shared/components/NoClusterError';
import NoClusterLinksError from 'shared/components/NoClusterLinksError';
import {webUserShape} from 'src/utils/propTypes';
import {showCluster} from 'src/shared/apis';
import {getSources} from 'src/shared/apis';
// Acts as a 'router middleware'. The main `App` component is responsible for
// getting the list of data nodes, but not every page requires them to function.
// Routes that do require data nodes can be nested under this component.
const CheckDataNodes = React.createClass({
propTypes: {
params: PropTypes.shape({
clusterID: PropTypes.string.isRequired,
}).isRequired,
addFlashMessage: PropTypes.func,
children: PropTypes.node,
},
@ -44,8 +41,8 @@ const CheckDataNodes = React.createClass({
},
componentDidMount() {
const {clusterID} = this.props.params;
showCluster(clusterID).then((resp) => {
getSources().then((resp) => {
// TODO: get this wired up correctly once getSources is working.
const dataNodes = this.getHealthyDataNodes(resp.data.data);
this.setState({
dataNodes,

View File

@ -104,7 +104,7 @@ export const RolePageContainer = React.createClass({
deleteRole(clusterID, roleSlug).then(() => {
// TODO: add success notification when we're implementing them higher in the tree.
// Right now the notification just gets swallowed when we transition to a new route.
this.props.router.push(`/clusters/${clusterID}/roles`);
this.props.router.push(`/roles`);
}).catch(err => {
console.error(err.toString()); // eslint-disable-line no-console
this.addErrorNotification(err);

View File

@ -158,7 +158,7 @@ export function createExplorer(clusterID, push) {
}).then((resp) => {
const explorer = parseRawExplorer(resp.data);
dispatch(loadExplorer(explorer));
push(`/clusters/${clusterID}/chronograf/data_explorer/${explorer.id}`);
push(`/chronograf/data_explorer/${explorer.id}`);
});
};
}
@ -179,7 +179,7 @@ export function deleteExplorer(clusterID, explorerID, push) {
// explorer and should create a new one.
if (explorer) {
dispatch(loadExplorer(explorer));
push(`/clusters/${clusterID}/chronograf/data_explorer/${explorer.id}`);
push(`/chronograf/data_explorer/${explorer.id}`);
} else {
dispatch(createExplorer(clusterID, push));
}
@ -249,7 +249,7 @@ export function fetchExplorers({clusterID, explorerID, push}) {
if (!explorerID) {
const explorer = _.maxBy(explorers, (ex) => ex.updated_at);
dispatch(loadExplorer(explorer));
push(`/clusters/${clusterID}/chronograf/data_explorer/${explorer.id}`);
push(`/chronograf/data_explorer/${explorer.id}`);
return;
}
@ -306,7 +306,7 @@ export function chooseExplorer(clusterID, explorerID, push) {
}).then((resp) => {
const explorer = parseRawExplorer(resp.data);
dispatch(loadExplorer(explorer));
push(`/clusters/${clusterID}/chronograf/data_explorer/${explorerID}`);
push(`/chronograf/data_explorer/${explorerID}`);
});
};
}

View File

@ -235,7 +235,7 @@ export const ClusterAccountContainer = React.createClass({
handleDeleteAccount() {
const {clusterID, accountID} = this.props.params;
deleteClusterAccount(clusterID, accountID).then(() => {
this.props.router.push(`/clusters/${clusterID}/accounts`);
this.props.router.push(`/accounts`);
this.props.addFlashMessage({
type: 'success',
text: 'Cluster account deleted!',

View File

@ -8,7 +8,6 @@ const {number, string, shape, arrayOf, func} = PropTypes;
const DatabaseManager = React.createClass({
propTypes: {
clusterID: string.isRequired,
database: string.isRequired,
databases: arrayOf(shape({})).isRequired,
dbStats: shape({
@ -24,7 +23,7 @@ const DatabaseManager = React.createClass({
},
render() {
const {database, clusterID, databases, dbStats, queries, users,
const {database, databases, dbStats, queries, users,
replicationFactors, onClickDatabase, onCreateDatabase} = this.props;
return (
@ -40,7 +39,7 @@ const DatabaseManager = React.createClass({
<ul className="dropdown-menu" aria-labelledby="dropdownMenu1">
{
databases.map((db) => {
return <li onClick={() => onClickDatabase(db.Name)} key={db.Name}><Link to={`/clusters/${clusterID}/databases/manager/${db.Name}`}>{db.Name}</Link></li>;
return <li onClick={() => onClickDatabase(db.Name)} key={db.Name}><Link to={`/databases/manager/${db.Name}`}>{db.Name}</Link></li>;
})
}
</ul>
@ -96,7 +95,7 @@ const DatabaseManager = React.createClass({
users.map((user) => {
return (
<tr key={user.name}>
<td><Link title="Manage Access" to={`/clusters/${clusterID}/accounts/${user.name}`}>{user.name}</Link></td>
<td><Link title="Manage Access" to={`/accounts/${user.name}`}>{user.name}</Link></td>
<td>{user.roles}</td>
</tr>
);

View File

@ -1,4 +1,4 @@
import React, {PropTypes} from 'react';
import React from 'react';
import {render} from 'react-dom';
import {Provider} from 'react-redux';
import {Router, Route, browserHistory} from 'react-router';
@ -17,18 +17,12 @@ import {ClusterAccountsPage, ClusterAccountPage} from 'src/cluster_accounts';
import {RolesPageContainer, RolePageContainer} from 'src/access_control';
import AccountSettingsPage from 'src/account_settings';
import NotFound from 'src/shared/components/NotFound';
import InsufficientPermissions from 'src/shared/components/InsufficientPermissions';
import NoClusterError from 'src/shared/components/NoClusterError';
import configureStore from 'src/store/configureStore';
import {webUserShape} from 'src/utils/propTypes';
import {meShow, getClusterAccount, getRoles} from 'shared/apis';
import {buildClusterAccounts} from 'src/shared/presenters';
import 'src/style/enterprise_style/application.scss';
const VIEW_CHRONOGRAF = 'ViewChronograf';
const READ = 'ReadData';
const defaultTimeRange = {upper: null, lower: 'now() - 15m'};
const lsTimeRange = window.localStorage.getItem('timeRange');
const parsedTimeRange = JSON.parse(lsTimeRange) || {};
@ -37,85 +31,75 @@ const timeRange = Object.assign(defaultTimeRange, parsedTimeRange);
const store = configureStore({timeRange});
const rootNode = document.getElementById('react-root');
function hasPermission(account, permission) {
const hasNakedPermission = !!account.permissions.find(p => p.name === permission);
const hasPermissionInRole = account.roles.some((role) => {
return role.permissions.some(p => p.name === permission);
});
return hasNakedPermission || hasPermissionInRole;
}
const HTTP_SERVER_ERROR = 500;
const Root = React.createClass({
getInitialState() {
return {
me: null,
canViewChronograf: false,
isFetching: true,
me: {
id: 1,
name: 'MrFusion',
email: 'foo@example.com',
admin: true,
},
isFetching: false,
hasReadPermission: false,
clusterStatus: null,
};
},
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,
});
});
// 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: webUserShape,
canViewChronograf: PropTypes.bool,
},
getChildContext() {
return {
me: this.state.me,
canViewChronograf: this.state.canViewChronograf,
};
},
render() {
const {me, canViewChronograf} = this.state;
const isAdmin = me && me.admin;
if (this.state.isFetching) {
return null;
}
@ -124,29 +108,25 @@ const Root = React.createClass({
return <NoClusterError />;
}
if (me && !this.state.hasReadPermission) {
return <InsufficientPermissions />;
}
return (
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/signup/admin/:step" component={SignUp} />
<Route path="/clusters/:clusterID" component={App}>
<Route path="/" component={App}>
<Route component={CheckDataNodes}>
<Route path="overview" component={OverviewPage} />
<Route path="queries" component={QueriesPage} />
{isAdmin ? <Route path="accounts" component={ClusterAccountsPage} /> : null}
{isAdmin ? <Route path="accounts/:accountID" component={ClusterAccountPage} /> : null}
{isAdmin ? <Route path="databases/manager/:database" component={DatabaseManager} /> : null}
<Route path="accounts" component={ClusterAccountsPage} />
<Route path="accounts/:accountID" component={ClusterAccountPage} />
<Route path="databases/manager/:database" component={DatabaseManager} />
<Route path="databases/retentionpolicies/:database" component={RetentionPoliciesPage} />
{canViewChronograf ? <Route path="chronograf/data_explorer" component={DataExplorer} /> : null}
{canViewChronograf ? <Route path="chronograf/data_explorer/:explorerID" component={DataExplorer} /> : null}
<Route path="chronograf/data_explorer" component={DataExplorer} />
<Route path="chronograf/data_explorer/:explorerID" component={DataExplorer} />
<Route path="roles" component={RolesPageContainer} />
<Route path="roles/:roleSlug" component={RolePageContainer} />
</Route>
{isAdmin ? <Route path="users" component={UsersPage} /> : null}
{isAdmin ? <Route path="users/:userID" component={UserEditPage} /> : null}
<Route path="users" component={UsersPage} />
<Route path="users/:userID" component={UserEditPage} />
<Route path="tasks" component={TasksPage} />
<Route path="account/settings" component={AccountSettingsPage} />
<Route path="*" component={NotFound} />

View File

@ -1,8 +1,8 @@
import AJAX from 'utils/ajax';
export function showCluster(clusterID) {
export function getSources() {
return AJAX({
url: metaProxy(clusterID, '/show-cluster'),
url: '/chronograf/v1/sources',
});
}
@ -39,7 +39,7 @@ export function createDatabase({database, rpName, duration, replicaN}) {
export function getClusters() {
return AJAX({
url: `/api/int/v1/clusters`,
url: ``,
});
}
@ -64,9 +64,9 @@ export function meUpdate({firstName, lastName, email, password, confirmation, ol
});
}
export function getWebUsers(clusterID) {
export function getWebUsers() {
return AJAX({
url: `/api/int/v1/clusters/${clusterID}/users`,
url: `/api/int/v1/users`,
});
}
@ -242,7 +242,7 @@ export function deleteClusterAccount(clusterID, accountName) {
}),
// Remove any cluster user links that are tied to this cluster account.
AJAX({
url: `/api/int/v1/clusters/${clusterID}/user_links/batch/${accountName}`,
url: `/api/int/v1/user_links/batch/${accountName}`,
method: 'DELETE',
}),
]);
@ -383,20 +383,20 @@ export function deleteRole(clusterID, roleName) {
export function deleteUserClusterLink(clusterID, userClusterLinkID) {
return AJAX({
url: `/api/int/v1/clusters/${clusterID}/user_links/${userClusterLinkID}`,
url: `/api/int/v1/user_links/${userClusterLinkID}`,
method: `DELETE`,
});
}
export function getUserClusterLinks(clusterID) {
export function getUserClusterLinks() {
return AJAX({
url: `/api/int/v1/clusters/${clusterID}/user_links`,
url: `/api/int/v1/user_links`,
});
}
export function createUserClusterLink({userID, clusterID, clusterUser}) {
return AJAX({
url: `/api/int/v1/clusters/${clusterID}/user_links`,
url: `/api/int/v1/user_links`,
method: 'POST',
data: {
user_id: userID,
@ -408,7 +408,7 @@ export function createUserClusterLink({userID, clusterID, clusterUser}) {
export function getWebUsersByClusterAccount(clusterID, clusterAccount) {
return AJAX({
url: `/api/int/v1/clusters/${clusterID}/user_links/batch/${encodeURIComponent(clusterAccount)}`,
url: `/api/int/v1/user_links/batch/${encodeURIComponent(clusterAccount)}`,
});
}
@ -422,12 +422,12 @@ export function batchCreateUserClusterLink(userID, clusterLinks) {
export function addWebUsersToClusterAccount(clusterID, clusterAccount, userIDs) {
return AJAX({
url: `/api/int/v1/clusters/${clusterID}/user_links/batch/${encodeURIComponent(clusterAccount)}`,
url: `/api/int/v1/user_links/batch/${encodeURIComponent(clusterAccount)}`,
method: 'POST',
data: userIDs,
});
}
function metaProxy(clusterID, slug) {
return `/api/int/v1/clusters/${clusterID}/meta${slug}`;
return `/api/int/v1/meta${slug}`;
}

View File

@ -42,9 +42,9 @@ export function showRetentionPolicies(host, databases, clusterID) {
return proxy(url, clusterID);
}
export function showShards(clusterID) {
export function showShards() {
return AJAX({
url: `/api/int/v1/clusters/${clusterID}/show-shards`,
url: `/api/int/v1/show-shards`,
});
}

View File

@ -3,11 +3,10 @@ import {Link} from 'react-router';
import PermissionsTable from 'src/shared/components/PermissionsTable';
import {roleShape} from 'utils/propTypes';
const {arrayOf, string, bool, func} = PropTypes;
const {arrayOf, bool, func} = PropTypes;
const RolePanels = React.createClass({
propTypes: {
roles: arrayOf(roleShape).isRequired,
clusterID: string.isRequired,
showUserCount: bool,
onRemoveAccountFromRole: func,
},
@ -19,7 +18,7 @@ const RolePanels = React.createClass({
},
render() {
const {roles, clusterID} = this.props;
const {roles} = this.props;
if (!roles.length) {
return (
@ -58,7 +57,7 @@ const RolePanels = React.createClass({
Remove
</button>
) : null}
<Link to={`/clusters/${clusterID}/roles/${encodeURIComponent(role.name)}`} className="btn btn-xs btn-link">
<Link to={`/roles/${encodeURIComponent(role.name)}`} className="btn btn-xs btn-link">
Go To Role
</Link>
</div>

View File

@ -1,81 +1,43 @@
import React, {PropTypes} from 'react';
import RenameClusterModal from '../modals/RenameCluster';
import {NavBar, NavBlock, NavHeader, NavListItem} from 'src/side_nav/components/NavItems';
const {func, string, arrayOf, shape, bool} = PropTypes;
const {string} = PropTypes;
const SideNav = React.createClass({
propTypes: {
isAdmin: bool.isRequired,
canViewChronograf: bool.isRequired,
clusterID: string.isRequired,
location: string.isRequired,
onRenameCluster: func.isRequired,
onOpenRenameModal: func.isRequired,
clusters: arrayOf(shape()).isRequired,
},
handleOpenRenameModal(clusterID) {
this.props.onOpenRenameModal(clusterID);
},
render() {
const {clusterID, location, onRenameCluster, clusters, isAdmin, canViewChronograf} = this.props;
const {location} = this.props;
return (
<NavBar location={location}>
<div className="sidebar__logo">
<span className="icon cubo-uniform"></span>
</div>
{
isAdmin ?
<NavBlock matcher={'users'} icon={"access-key"} link={`/clusters/${clusterID}/users`}>
<NavHeader link={`/clusters/${clusterID}/users`} title="Web Admin" />
<NavListItem matcher={'users'} link={`/clusters/${clusterID}/users`}>Users</NavListItem>
</NavBlock>
: null
}
<NavBlock matcher="overview" icon="crown" link={`/clusters/${clusterID}/overview`}>
<NavHeader link={`/clusters/${clusterID}/overview`} title="Cluster" />
<NavListItem matcher="overview" link={`/clusters/${clusterID}/overview`}>Overview</NavListItem>
<NavListItem matcher="queries" link={`/clusters/${clusterID}/queries`}>Queries</NavListItem>
<NavListItem matcher="tasks" link={`/clusters/${clusterID}/tasks`}>Tasks</NavListItem>
<NavListItem matcher="roles" link={`/clusters/${clusterID}/roles`}>Roles</NavListItem>
{isAdmin ? <NavListItem matcher="accounts" link={`/clusters/${clusterID}/accounts`}>Cluster Accounts</NavListItem> : null}
{isAdmin ? <NavListItem matcher="manager" link={`/clusters/${clusterID}/databases/manager/_internal`}>Database Manager</NavListItem> : null}
<NavListItem matcher="retentionpolicies" link={`/clusters/${clusterID}/databases/retentionpolicies/_internal`}>Retention Policies</NavListItem>
<NavBlock matcher={'users'} icon={"access-key"} link={`/users`}>
<NavHeader link={`/users`} title="Web Admin" />
<NavListItem matcher={'users'} link={`/users`}>Users</NavListItem>
</NavBlock>
{
canViewChronograf ?
<NavBlock matcher="chronograf" icon="graphline" link={`/clusters/${clusterID}/chronograf/data_explorer`}>
<NavHeader link={`/clusters/${clusterID}/chronograf/data_explorer`} title={'Chronograf'} />
<NavListItem matcher={'data_explorer'} link={`/clusters/${clusterID}/chronograf/data_explorer`}>Data Explorer</NavListItem>
</NavBlock>
: null
}
<NavBlock matcher="settings" icon="user-outline" link={`/clusters/${clusterID}/account/settings`}>
<NavHeader link={`/clusters/${clusterID}/account/settings`} title="My Account" />
<NavListItem matcher="settings" link={`/clusters/${clusterID}/account/settings`}>Settings</NavListItem>
<NavBlock matcher="overview" icon="crown" link={`/overview`}>
<NavHeader link={`/overview`} title="Cluster" />
<NavListItem matcher="overview" link={`/overview`}>Overview</NavListItem>
<NavListItem matcher="queries" link={`/queries`}>Queries</NavListItem>
<NavListItem matcher="tasks" link={`/tasks`}>Tasks</NavListItem>
<NavListItem matcher="roles" link={`/roles`}>Roles</NavListItem>
<NavListItem matcher="accounts" link={`/accounts`}>Cluster Accounts</NavListItem>
<NavListItem matcher="manager" link={`/databases/manager/_internal`}>Database Manager</NavListItem>
<NavListItem matcher="retentionpolicies" link={`/databases/retentionpolicies/_internal`}>Retention Policies</NavListItem>
</NavBlock>
<NavBlock matcher="chronograf" icon="graphline" link={`/chronograf/data_explorer`}>
<NavHeader link={`/chronograf/data_explorer`} title={'Chronograf'} />
<NavListItem matcher={'data_explorer'} link={`/chronograf/data_explorer`}>Data Explorer</NavListItem>
</NavBlock>
<NavBlock matcher="settings" icon="user-outline" link={`/account/settings`}>
<NavHeader link={`/account/settings`} title="My Account" />
<NavListItem matcher="settings" link={`/account/settings`}>Settings</NavListItem>
<a className="sidebar__menu-item" href="/logout">Logout</a>
</NavBlock>
<NavBlock icon="server" className="sidebar__square-last" wrapperClassName="sidebar__menu-wrapper-bottom">
{
clusters.map(({cluster_id: id, display_name: displayName}, i) => {
return (
<NavListItem key={i} matcher={id} link={`/clusters/${id}/overview`}>
{displayName || id}
{
isAdmin ?
<button className="btn btn-sm js-open-rename-cluster-modal" onClick={() => this.handleOpenRenameModal(id)} data-toggle="modal" data-target="#rename-cluster-modal">
<span className="icon pencil"></span>
</button>
: null
}
</NavListItem>
);
})
}
</NavBlock>
{isAdmin ? <RenameClusterModal clusterID={clusterID} onRenameCluster={onRenameCluster}/> : null}
</NavBar>
);
},

View File

@ -1,18 +1,14 @@
import React, {PropTypes} from 'react';
import {getClusters, updateCluster} from 'shared/apis';
import SideNav from '../components/SideNav';
import {webUserShape} from 'src/utils/propTypes';
const {func, string} = PropTypes;
const SideNavApp = React.createClass({
propTypes: {
clusterID: string.isRequired,
currentLocation: string.isRequired,
addFlashMessage: func.isRequired,
},
contextTypes: {
me: webUserShape,
canViewChronograf: PropTypes.bool,
},
@ -23,49 +19,15 @@ const SideNavApp = React.createClass({
};
},
getPageData() {
getClusters().then(({data: clusters}) => {
this.setState({clusters});
});
},
componentDidMount() {
this.getPageData();
},
handleRenameCluster(displayName) {
const {addFlashMessage} = this.props;
updateCluster(this.state.clusterToUpdate, displayName).then(() => {
this.getPageData();
addFlashMessage({
type: 'success',
text: 'Cluster name changed successully',
});
}).catch(() => {
addFlashMessage({
type: 'error',
text: 'Something went wrong while renaming the cluster',
});
});
},
handleOpenRenameModal(clusterToUpdate) {
this.setState({clusterToUpdate});
},
render() {
const {clusterID, currentLocation} = this.props;
const {me, canViewChronograf} = this.context;
const {currentLocation} = this.props;
const {canViewChronograf} = this.context;
return (
<SideNav
isAdmin={!!me && me.admin}
isAdmin={true}
canViewChronograf={canViewChronograf}
location={currentLocation}
clusterID={clusterID}
onRenameCluster={this.handleRenameCluster}
onOpenRenameModal={this.handleOpenRenameModal}
clusters={this.state.clusters}
/>
);
},

View File

@ -18,9 +18,9 @@ export function buildInfluxUrl({host, statement, database, retentionPolicy}) {
return encodeURIComponent(url);
}
export function proxy(url, clusterID) {
export function proxy(url) {
return AJAX({
url: `/clusters/${clusterID}/proxy?proxy_url=${url}`,
url: `/proxy?proxy_url=${url}`,
});
}