Fetch live data from /chronograf/v1/users & load into Chronograf Admin Table

Move DUMMY_USERS data into admin/chronograf/loadUsers spec.
This adds a full roundtrip for populating the Chronograf Admin Table
with live data from the server. It includes a reducer & test, action
creator, and API request, which then loads the successful data into
the Redux and then the React component.
remotes/origin/multitenancy_temp_stash
Jared Scheib 2017-11-01 00:30:30 -07:00
parent 0376dc32b7
commit 616e4bbb98
7 changed files with 257 additions and 171 deletions

View File

@ -0,0 +1,176 @@
import reducer from 'src/admin/reducers/chronograf'
import {loadUsers} from 'src/admin/actions/chronograf'
let state
const users = [
{
id: 666,
name: 'bob@billietta.com',
provider: 'GitHub',
scheme: 'OAuth2',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Green Team', organizationID: 1234, name: 'admin'},
{organizationName: 'Blue Team', organizationID: 1235, name: 'editor'},
],
links: {self: '/chronograf/v1/users/666'},
},
{
id: 667,
name: 'billybob@gmail.com',
provider: 'Auth0',
scheme: 'OAuth2',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Green Team', organizationID: 1234, name: 'viewer'},
{organizationName: 'Red Team', organizationID: 1236, name: 'editor'},
],
links: {self: '/chronograf/v1/users/667'},
},
{
id: 720,
name: 'shorty@gmail.com',
provider: 'Heroku',
scheme: 'LDAP',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Green Team', organizationID: 1234, name: 'editor'},
],
links: {self: '/chronograf/v1/users/720'},
},
{
id: 271,
name: 'shawn.ofthe.dead@gmail.com',
provider: 'GitHub',
scheme: 'OAuth2',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Blue Team', organizationID: 1235, name: 'editor'},
],
links: {self: '/chronograf/v1/users/271'},
},
{
id: 6389,
name: 'swogglez@gmail.com',
provider: 'Heroku',
scheme: 'OAuth2',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Red Team', organizationID: 1236, name: 'viewer'},
{organizationName: 'Blue Team', organizationID: 1235, name: 'viewer'},
],
links: {self: '/chronograf/v1/users/6389'},
},
{
id: 99181,
name: 'whiskey.elbow@gmail.com',
provider: 'GitHub',
scheme: 'OAuth2',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Green Team', organizationID: 1234, name: 'viewer'},
{organizationName: 'Blue Team', organizationID: 1235, name: 'viewer'},
{organizationName: 'Red Team', organizationID: 1236, name: 'viewer'},
],
links: {self: '/chronograf/v1/users/99181'},
},
{
id: 3786,
name: 'bob.builder@gmail.com',
provider: 'Generic',
scheme: 'LDAP',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Red Team', organizationID: 1236, name: 'editor'},
],
links: {self: '/chronograf/v1/users/3786'},
},
{
id: 112345,
name: 'lost.in.translation@gmail.com',
provider: 'Generic',
scheme: 'LDAP',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
],
links: {self: '/chronograf/v1/users/112345'},
},
{
id: 23,
name: 'wandering.soul@gmail.com',
provider: 'Heroku',
scheme: 'LDAP',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
],
links: {self: '/chronograf/v1/users/23'},
},
{
id: 7,
name: 'disembodied@gmail.com',
provider: 'Auth0',
scheme: 'OAuth2',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
],
links: {self: '/chronograf/v1/users/7'},
},
{
id: 0,
name: 'bob.builder@gmail.com',
provider: 'Heroku',
scheme: 'LDAP',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Red Team', organizationID: 1236, name: 'editor'},
],
links: {self: '/chronograf/v1/users/0'},
},
{
id: 2891,
name: 'swag.bandit@gmail.com',
provider: 'Google',
scheme: 'OAuth2',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Blue Team', organizationID: 1234, name: 'admin'},
],
links: {self: '/chronograf/v1/users/2891'},
},
{
id: 2645,
name: 'lord.ofthe.dance@gmail.com',
provider: 'GitHub',
scheme: 'OAuth2',
superadmin: true,
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
],
links: {self: '/chronograf/v1/users/2645'},
},
{
id: 47119,
name: 'ohnooeezzz@gmail.com',
provider: 'Google',
scheme: 'OAuth2',
superadmin: true,
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Blue Team', organizationID: 1234, name: NO_ROLE},
],
links: {self: '/chronograf/v1/users/47119'},
},
]
describe('Admin.Chronograf.Reducers', () => {
it('it can load all users', () => {
const actual = reducer(state, loadUsers({users}))
const expected = {
users,
}
expect(actual.users).to.deep.equal(expected.users)
})
})

View File

@ -0,0 +1,23 @@
import {getUsers as getUsersAJAX} from 'src/admin/apis/chronograf'
import {errorThrown} from 'shared/actions/errors'
// action creators
// response contains `users` and `links`
export const loadUsers = ({users}) => ({
type: 'CHRONOGRAF_LOAD_USERS',
payload: {
users,
},
})
// async actions (thunks)
export const loadUsersAsync = url => async dispatch => {
try {
const {data} = await getUsersAJAX(url)
dispatch(loadUsers(data))
} catch (error) {
dispatch(errorThrown(error))
}
}

View File

@ -0,0 +1,13 @@
import AJAX from 'src/utils/ajax'
export const getUsers = async url => {
try {
return await AJAX({
method: 'GET',
url,
})
} catch (error) {
console.error(error)
throw error
}
}

View File

@ -1,165 +1,5 @@
export const NO_ROLE = 'No Role'
export const DUMMY_USERS = [
{
id: 666,
name: 'bob@billietta.com',
provider: 'GitHub',
scheme: 'OAuth2',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Green Team', organizationID: 1234, name: 'admin'},
{organizationName: 'Blue Team', organizationID: 1235, name: 'editor'},
],
links: {self: '/chronograf/v1/users/666'},
},
{
id: 667,
name: 'billybob@gmail.com',
provider: 'Auth0',
scheme: 'OAuth2',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Green Team', organizationID: 1234, name: 'viewer'},
{organizationName: 'Red Team', organizationID: 1236, name: 'editor'},
],
links: {self: '/chronograf/v1/users/667'},
},
{
id: 720,
name: 'shorty@gmail.com',
provider: 'Heroku',
scheme: 'LDAP',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Green Team', organizationID: 1234, name: 'editor'},
],
links: {self: '/chronograf/v1/users/720'},
},
{
id: 271,
name: 'shawn.ofthe.dead@gmail.com',
provider: 'GitHub',
scheme: 'OAuth2',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Blue Team', organizationID: 1235, name: 'editor'},
],
links: {self: '/chronograf/v1/users/271'},
},
{
id: 6389,
name: 'swogglez@gmail.com',
provider: 'Heroku',
scheme: 'OAuth2',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Red Team', organizationID: 1236, name: 'viewer'},
{organizationName: 'Blue Team', organizationID: 1235, name: 'viewer'},
],
links: {self: '/chronograf/v1/users/6389'},
},
{
id: 99181,
name: 'whiskey.elbow@gmail.com',
provider: 'GitHub',
scheme: 'OAuth2',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Green Team', organizationID: 1234, name: 'viewer'},
{organizationName: 'Blue Team', organizationID: 1235, name: 'viewer'},
{organizationName: 'Red Team', organizationID: 1236, name: 'viewer'},
],
links: {self: '/chronograf/v1/users/99181'},
},
{
id: 3786,
name: 'bob.builder@gmail.com',
provider: 'Generic',
scheme: 'LDAP',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Red Team', organizationID: 1236, name: 'editor'},
],
links: {self: '/chronograf/v1/users/3786'},
},
{
id: 112345,
name: 'lost.in.translation@gmail.com',
provider: 'Generic',
scheme: 'LDAP',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
],
links: {self: '/chronograf/v1/users/112345'},
},
{
id: 23,
name: 'wandering.soul@gmail.com',
provider: 'Heroku',
scheme: 'LDAP',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
],
links: {self: '/chronograf/v1/users/23'},
},
{
id: 7,
name: 'disembodied@gmail.com',
provider: 'Auth0',
scheme: 'OAuth2',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
],
links: {self: '/chronograf/v1/users/7'},
},
{
id: 0,
name: 'bob.builder@gmail.com',
provider: 'Heroku',
scheme: 'LDAP',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Red Team', organizationID: 1236, name: 'editor'},
],
links: {self: '/chronograf/v1/users/0'},
},
{
id: 2891,
name: 'swag.bandit@gmail.com',
provider: 'Google',
scheme: 'OAuth2',
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Blue Team', organizationID: 1234, name: 'admin'},
],
links: {self: '/chronograf/v1/users/2891'},
},
{
id: 2645,
name: 'lord.ofthe.dance@gmail.com',
provider: 'GitHub',
scheme: 'OAuth2',
superadmin: true,
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
],
links: {self: '/chronograf/v1/users/2645'},
},
{
id: 47119,
name: 'ohnooeezzz@gmail.com',
provider: 'Google',
scheme: 'OAuth2',
superadmin: true,
roles: [
{organizationName: 'All Users', organizationID: 666, name: NO_ROLE},
{organizationName: 'Blue Team', organizationID: 1234, name: NO_ROLE},
],
links: {self: '/chronograf/v1/users/47119'},
},
]
export const USER_ROLES = [
{name: NO_ROLE},
{name: 'viewer'},

View File

@ -1,4 +1,8 @@
import React, {Component, PropTypes} from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import {loadUsersAsync} from 'src/admin/actions/chronograf'
import PageHeader from 'src/admin/components/chronograf/PageHeader'
import UsersTableHeader from 'src/admin/components/chronograf/UsersTableHeader'
@ -8,12 +12,7 @@ import CreateOrgOverlay from 'src/admin/components/chronograf/CreateOrgOverlay'
import FancyScrollbar from 'shared/components/FancyScrollbar'
import {
DUMMY_USERS,
DUMMY_ORGS,
DEFAULT_ORG,
NO_ORG,
} from 'src/admin/constants/dummyUsers'
import {DUMMY_ORGS, DEFAULT_ORG, NO_ORG} from 'src/admin/constants/dummyUsers'
class AdminChronografPage extends Component {
constructor(props) {
@ -28,6 +27,14 @@ class AdminChronografPage extends Component {
}
}
componentDidMount() {
const {loadUsers} = this.props
// TODO: determine this url from server
const urlThatsProbablySourceLinksUsersChronograf = '/chronograf/v1/users'
loadUsers(urlThatsProbablySourceLinksUsersChronograf)
}
isSameUser = (userA, userB) => {
return (
userA.name === userB.name &&
@ -106,7 +113,9 @@ class AdminChronografPage extends Component {
filteredUsers,
showCreateOverlay,
} = this.state
const numUsersSelected = Object.keys(selectedUsers).length
return (
<div className="page">
<PageHeader onShowCreateOrgOverlay={this.handleShowCreateOrgOverlay} />
@ -132,7 +141,7 @@ class AdminChronografPage extends Component {
/>
<div className="panel-body chronograf-admin-table--panel">
<UsersTable
filteredUsers={filteredUsers}
filteredUsers={filteredUsers} // TODO: change to users upon separating Orgs & Users views
organizationName={organizationName}
onFilterUsers={this.handleFilterUsers}
onToggleUserSelected={this.handleToggleUserSelected}
@ -161,16 +170,24 @@ class AdminChronografPage extends Component {
}
}
const {arrayOf, shape} = PropTypes
const {arrayOf, func, shape} = PropTypes
AdminChronografPage.propTypes = {
users: arrayOf(shape),
organizations: arrayOf(shape),
loadUsers: func.isRequired,
}
AdminChronografPage.defaultProps = {
users: DUMMY_USERS,
organizations: DUMMY_ORGS,
}
export default AdminChronografPage
const mapStateToProps = ({adminChronograf: {users}}) => ({
users,
})
const mapDispatchToProps = dispatch => ({
loadUsers: bindActionCreators(loadUsersAsync, dispatch),
})
export default connect(mapStateToProps, mapDispatchToProps)(AdminChronografPage)

View File

@ -0,0 +1,16 @@
const initialState = {
users: [],
organizations: [],
}
const adminChronograf = (state = initialState, action) => {
switch (action.type) {
case 'CHRONOGRAF_LOAD_USERS': {
return {...state, ...action.payload}
}
}
return state
}
export default adminChronograf

View File

@ -1,3 +1,4 @@
import adminChronograf from './chronograf'
import adminInfluxDB from './influxdb'
export default {adminInfluxDB}
export default {adminChronograf, adminInfluxDB}