Hook up DBRP manager to backend

pull/1029/head
Andrew Watkins 2017-03-23 15:06:22 -07:00
parent 2c874e4e53
commit 9280741773
7 changed files with 136 additions and 54 deletions

View File

@ -1,25 +1,25 @@
import uuid from 'node-uuid';
import { import {
getUsers as getUsersAJAX, getUsers as getUsersAJAX,
getRoles as getRolesAJAX, getRoles as getRolesAJAX,
getPermissions as getPermissionsAJAX, getPermissions as getPermissionsAJAX,
getDbsAndRps as getDbsAndRpsAJAX,
createUser as createUserAJAX, createUser as createUserAJAX,
createRole as createRoleAJAX, createRole as createRoleAJAX,
createDatabase as createDatabaseAJAX,
createRetentionPolicy as createRetentionPolicyAJAX,
deleteUser as deleteUserAJAX, deleteUser as deleteUserAJAX,
deleteRole as deleteRoleAJAX, deleteRole as deleteRoleAJAX,
deleteDatabase as deleteDatabaseAJAX,
deleteRetentionPolicy as deleteRetentionPolicyAJAX,
updateRole as updateRoleAJAX, updateRole as updateRoleAJAX,
updateUser as updateUserAJAX, updateUser as updateUserAJAX,
updateRetentionPolicy as updateRetentionPolicyAJAX,
} from 'src/admin/apis' } from 'src/admin/apis'
import { import {
killQuery as killQueryProxy, killQuery as killQueryProxy,
showDatabases,
showRetentionPolicies,
} from 'shared/apis/metaQuery' } from 'shared/apis/metaQuery'
import parseShowDatabases from 'src/shared/parsing/showDatabases'
import parseShowRetentionPolicies from 'src/shared/parsing/showRetentionPolicies'
import {publishNotification} from 'src/shared/actions/notifications'; import {publishNotification} from 'src/shared/actions/notifications';
import {ADMIN_NOTIFICATION_DELAY} from 'src/admin/constants' import {ADMIN_NOTIFICATION_DELAY} from 'src/admin/constants'
@ -94,6 +94,16 @@ export const syncDatabase = (stale, synced) => ({
}, },
}) })
export const syncRetentionPolicy = (database, stale, synced) => ({
type: 'SYNC_RETENTION_POLICY',
payload: {
database,
stale,
synced,
},
})
export const editUser = (user, updates) => ({ export const editUser = (user, updates) => ({
type: 'EDIT_USER', type: 'EDIT_USER',
payload: { payload: {
@ -224,19 +234,8 @@ export const loadPermissionsAsync = (url) => async (dispatch) => {
} }
export const loadDBsAndRPsAsync = (url) => async (dispatch) => { export const loadDBsAndRPsAsync = (url) => async (dispatch) => {
const {data: dbs} = await showDatabases(url) const {data: {databases}} = await getDbsAndRpsAJAX(url)
const {databases} = parseShowDatabases(dbs) dispatch(loadDatabases(databases))
const {data: {results}} = await showRetentionPolicies(url, databases)
const retentionPolicies = results.map(parseShowRetentionPolicies)
const rps = retentionPolicies.map(rp => rp.retentionPolicies)
const dbsAndRps = databases.map((name, i) => ({
name,
links: {self: uuid.v4()},
retentionPolicies: rps[i].map(rp => ({...rp, links: {self: uuid.v4()}})),
}))
dispatch(loadDatabases(dbsAndRps))
} }
export const createUserAsync = (url, user) => async (dispatch) => { export const createUserAsync = (url, user) => async (dispatch) => {
@ -265,10 +264,9 @@ export const createRoleAsync = (url, role) => async (dispatch) => {
export const createDatabaseAsync = (url, database) => async (dispatch) => { export const createDatabaseAsync = (url, database) => async (dispatch) => {
try { try {
// TODO: implement once server is up const {data} = await createDatabaseAJAX(url, database)
// const {data} = await createDatabaseAJAX(url, database) dispatch(syncDatabase(database, data))
dispatch(publishNotification('success', 'Database created successfully')) dispatch(publishNotification('success', 'Database created successfully'))
// dispatch(syncDatabase(database, {...data, id: uuid.v4()}))
} catch (error) { } catch (error) {
// undo optimistic update // undo optimistic update
dispatch(publishNotification('error', `Failed to create database: ${error.data.message}`)) dispatch(publishNotification('error', `Failed to create database: ${error.data.message}`))
@ -276,26 +274,24 @@ export const createDatabaseAsync = (url, database) => async (dispatch) => {
} }
} }
export const createRetentionPolicyAsync = (url, retentionPolicy) => async (dispatch) => { export const createRetentionPolicyAsync = (database, retentionPolicy) => async (dispatch) => {
try { try {
// TODO: implement once server is up const {data} = await createRetentionPolicyAJAX(database.links.retentionPolicies, retentionPolicy)
// const {data} = await createRetentionPolicyAJAX(url, retentionPolicy)
dispatch(publishNotification('success', 'Retention policy created successfully')) dispatch(publishNotification('success', 'Retention policy created successfully'))
// dispatch(syncRetentionPolicy(retentionPolicy, {...data, id: uuid.v4()})) dispatch(syncRetentionPolicy(database, retentionPolicy, data))
} catch (error) { } catch (error) {
// undo optimistic update // undo optimistic update
dispatch(publishNotification('error', `Failed to create retention policy: ${error.data.message}`)) dispatch(publishNotification('error', `Failed to create retention policy: ${error.data.message}`))
setTimeout(() => dispatch(removeRetentionPolicy(retentionPolicy)), ADMIN_NOTIFICATION_DELAY) setTimeout(() => dispatch(removeRetentionPolicy(database, retentionPolicy)), ADMIN_NOTIFICATION_DELAY)
} }
} }
export const updateRetentionPolicyAsync = (database, retentionPolicy, updates) => async (dispatch) => { export const updateRetentionPolicyAsync = (database, retentionPolicy, updates) => async (dispatch) => {
try { try {
// TODO: implement once server is up
dispatch(editRetentionPolicy(database, retentionPolicy, updates)) dispatch(editRetentionPolicy(database, retentionPolicy, updates))
// const {data} = await createRetentionPolicyAJAX(url, retentionPolicy) const {data} = await updateRetentionPolicyAJAX(retentionPolicy.links.self, updates)
dispatch(publishNotification('success', 'Retention policy updated successfully')) dispatch(publishNotification('success', 'Retention policy updated successfully'))
// dispatch(syncRetentionPolicy(retentionPolicy, {...data, id: uuid.v4()})) dispatch(syncRetentionPolicy(database, data))
} catch (error) { } catch (error) {
dispatch(publishNotification('error', `Failed to update retention policy: ${error.data.message}`)) dispatch(publishNotification('error', `Failed to update retention policy: ${error.data.message}`))
} }
@ -326,16 +322,24 @@ export const deleteUserAsync = (user, addFlashMessage) => (dispatch) => {
deleteUserAJAX(user.links.self, addFlashMessage, user.name) deleteUserAJAX(user.links.self, addFlashMessage, user.name)
} }
export const deleteDatabaseAsync = (url, database) => (dispatch) => { export const deleteDatabaseAsync = (database) => async (dispatch) => {
dispatch(removeDatabase(database)) dispatch(removeDatabase(database))
dispatch(publishNotification('success', 'Database deleted')) dispatch(publishNotification('success', 'Database deleted'))
try {
await deleteDatabaseAJAX(database.links.self)
} catch (error) {
dispatch(publishNotification('error', `Failed to delete database: ${error.data.message}`))
}
}
// TODO: implement once server is up export const deleteRetentionPolicyAsync = (database, retentionPolicy) => async (dispatch) => {
// try { dispatch(removeRetentionPolicy(database, retentionPolicy))
// await deleteDatabaseAJAX(url, database.name) dispatch(publishNotification('success', `Retention policy ${retentionPolicy.name} deleted`))
// } catch (error) { try {
// dispatch(publishNotification('error', `Failed to delete database: ${error.data.message}`)) await deleteRetentionPolicyAJAX(retentionPolicy.links.self)
// } } catch (error) {
dispatch(publishNotification('error', `Failed to delete retentionPolicy: ${error.data.message}`))
}
} }
export const updateRoleUsersAsync = (role, users) => async (dispatch) => { export const updateRoleUsersAsync = (role, users) => async (dispatch) => {

View File

@ -36,6 +36,18 @@ export const getPermissions = async (url) => {
} }
} }
export const getDbsAndRps = async (url) => {
try {
return await AJAX({
method: 'GET',
url,
})
} catch (error) {
console.error(error)
throw error
}
}
export const createUser = async (url, user) => { export const createUser = async (url, user) => {
try { try {
return await AJAX({ return await AJAX({
@ -84,6 +96,17 @@ export const createRetentionPolicy = async (url, retentionPolicy) => {
} }
} }
export const deleteRetentionPolicy = async (url) => {
try {
return await AJAX({
method: 'DELETE',
url,
})
} catch (error) {
throw error
}
}
export const deleteRole = async (url, addFlashMessage, rolename) => { export const deleteRole = async (url, addFlashMessage, rolename) => {
try { try {
const response = await AJAX({ const response = await AJAX({
@ -124,11 +147,11 @@ export const deleteUser = async (url, addFlashMessage, username) => {
} }
} }
export const deleteDatabase = async (url, name) => { export const deleteDatabase = async (url) => {
try { try {
return await AJAX({ return await AJAX({
method: 'DELETE', method: 'DELETE',
url: `${url}/${name}`, url,
}) })
} catch (error) { } catch (error) {
console.error(error) console.error(error)
@ -167,3 +190,18 @@ export const updateUser = async (url, roles, permissions) => {
throw error throw error
} }
} }
export const updateRetentionPolicy = async (url, retentionPolicy) => {
try {
return await AJAX({
method: 'PUT',
url,
data: {
retentionPolicy,
},
})
} catch (error) {
console.error(error)
throw error
}
}

View File

@ -18,6 +18,7 @@ const DatabaseManager = ({
onCreateRetentionPolicy, onCreateRetentionPolicy,
onUpdateRetentionPolicy, onUpdateRetentionPolicy,
onRemoveRetentionPolicy, onRemoveRetentionPolicy,
onDeleteRetentionPolicy,
}) => { }) => {
return ( return (
<div className="panel panel-info"> <div className="panel panel-info">
@ -45,6 +46,7 @@ const DatabaseManager = ({
onCreateRetentionPolicy={onCreateRetentionPolicy} onCreateRetentionPolicy={onCreateRetentionPolicy}
onUpdateRetentionPolicy={onUpdateRetentionPolicy} onUpdateRetentionPolicy={onUpdateRetentionPolicy}
onRemoveRetentionPolicy={onRemoveRetentionPolicy} onRemoveRetentionPolicy={onRemoveRetentionPolicy}
onDeleteRetentionPolicy={onDeleteRetentionPolicy}
/> />
) )
} }
@ -78,6 +80,7 @@ DatabaseManager.propTypes = {
onCreateRetentionPolicy: func, onCreateRetentionPolicy: func,
onUpdateRetentionPolicy: func, onUpdateRetentionPolicy: func,
onRemoveRetentionPolicy: func, onRemoveRetentionPolicy: func,
onDeleteRetentionPolicy: func,
} }
export default DatabaseManager export default DatabaseManager

View File

@ -8,6 +8,7 @@ class DatabaseRow extends Component {
super(props) super(props)
this.state = { this.state = {
isEditing: false, isEditing: false,
isDeleting: false,
} }
this.handleKeyDown = ::this.handleKeyDown this.handleKeyDown = ::this.handleKeyDown
this.handleClickOutside = ::this.handleClickOutside this.handleClickOutside = ::this.handleClickOutside
@ -16,6 +17,8 @@ class DatabaseRow extends Component {
this.handleCreate = ::this.handleCreate this.handleCreate = ::this.handleCreate
this.handleUpdate = ::this.handleUpdate this.handleUpdate = ::this.handleUpdate
this.getInputValues = ::this.getInputValues this.getInputValues = ::this.getInputValues
this.handleStartDelete = ::this.handleStartDelete
this.handleEndDelete = ::this.handleEndDelete
} }
componentWillMount() { componentWillMount() {
@ -30,12 +33,14 @@ class DatabaseRow extends Component {
retentionPolicy: {name, duration, replication, isDefault, isNew}, retentionPolicy: {name, duration, replication, isDefault, isNew},
retentionPolicy, retentionPolicy,
database, database,
onDelete,
isRFDisplayed, isRFDisplayed,
} = this.props } = this.props
const {isEditing, isDeleting} = this.state
const formattedDuration = formatInfiniteDuration(duration) const formattedDuration = formatInfiniteDuration(duration)
if (this.state.isEditing) { if (isEditing) {
return ( return (
<tr> <tr>
<td> <td>
@ -72,7 +77,7 @@ class DatabaseRow extends Component {
name="name" name="name"
type="number" type="number"
min="1" min="1"
defaultValue={replication} defaultValue={replication || 1}
placeholder="how many nodes do you have" placeholder="how many nodes do you have"
onKeyDown={(e) => this.handleKeyDown(e, database)} onKeyDown={(e) => this.handleKeyDown(e, database)}
ref={(r) => this.replication = r} ref={(r) => this.replication = r}
@ -95,9 +100,13 @@ class DatabaseRow extends Component {
<td onClick={this.handleStartEdit}>{formattedDuration}</td> <td onClick={this.handleStartEdit}>{formattedDuration}</td>
{isRFDisplayed ? <td onClick={this.handleStartEdit}>{replication}</td> : null} {isRFDisplayed ? <td onClick={this.handleStartEdit}>{replication}</td> : null}
<td className="text-right"> <td className="text-right">
<button className="btn btn-xs btn-danger admin-table--delete"> {
{`Delete ${name}`} isDeleting ?
</button> <YesNoButtons onConfirm={() => onDelete(database, retentionPolicy)} onCancel={this.handleEndDelete} /> :
<button className="btn btn-xs btn-danger admin-table--delete" onClick={this.handleStartDelete}>
{`Delete ${name}`}
</button>
}
</td> </td>
</tr> </tr>
) )
@ -105,6 +114,7 @@ class DatabaseRow extends Component {
handleClickOutside() { handleClickOutside() {
this.handleEndEdit() this.handleEndEdit()
this.handleEndDelete()
} }
handleStartEdit() { handleStartEdit() {
@ -115,14 +125,22 @@ class DatabaseRow extends Component {
this.setState({isEditing: false}) this.setState({isEditing: false})
} }
handleStartDelete() {
this.setState({isDeleting: true})
}
handleEndDelete() {
this.setState({isDeleting: false})
}
handleCreate() { handleCreate() {
const {database, onCreate} = this.props const {database, retentionPolicy, onCreate} = this.props
const validInputs = this.getInputValues() const validInputs = this.getInputValues()
if (!validInputs) { if (!validInputs) {
return return
} }
onCreate(database, validInputs) onCreate(database, {...retentionPolicy, ...validInputs})
this.handleEndEdit() this.handleEndEdit()
} }
@ -205,6 +223,7 @@ DatabaseRow.propTypes = {
onRemove: func, onRemove: func,
onCreate: func, onCreate: func,
onUpdate: func, onUpdate: func,
onDelete: func,
notify: func, notify: func,
isRFDisplayed: bool, isRFDisplayed: bool,
} }

View File

@ -22,6 +22,7 @@ const DatabaseTable = ({
onCreateRetentionPolicy, onCreateRetentionPolicy,
onUpdateRetentionPolicy, onUpdateRetentionPolicy,
onRemoveRetentionPolicy, onRemoveRetentionPolicy,
onDeleteRetentionPolicy,
}) => { }) => {
return ( return (
<div className="db-manager"> <div className="db-manager">
@ -57,6 +58,7 @@ const DatabaseTable = ({
onCreate={onCreateRetentionPolicy} onCreate={onCreateRetentionPolicy}
onUpdate={onUpdateRetentionPolicy} onUpdate={onUpdateRetentionPolicy}
onRemove={onRemoveRetentionPolicy} onRemove={onRemoveRetentionPolicy}
onDelete={onDeleteRetentionPolicy}
isRFDisplayed={isRFDisplayed} isRFDisplayed={isRFDisplayed}
/> />
) )
@ -84,6 +86,7 @@ DatabaseTable.propTypes = {
onCreateRetentionPolicy: func, onCreateRetentionPolicy: func,
onUpdateRetentionPolicy: func, onUpdateRetentionPolicy: func,
onRemoveRetentionPolicy: func, onRemoveRetentionPolicy: func,
onDeleteRetentionPolicy: func,
} }
const DatabaseTableHeader = ({ const DatabaseTableHeader = ({

View File

@ -15,9 +15,9 @@ class DatabaseManagerPage extends Component {
} }
componentDidMount() { componentDidMount() {
const {source: {links: {proxy}}, actions} = this.props const {source: {links: {databases}}, actions} = this.props
actions.loadDBsAndRPsAsync(proxy) actions.loadDBsAndRPsAsync(databases)
} }
render() { render() {
@ -39,23 +39,24 @@ class DatabaseManagerPage extends Component {
onCreateRetentionPolicy={actions.createRetentionPolicyAsync} onCreateRetentionPolicy={actions.createRetentionPolicyAsync}
onUpdateRetentionPolicy={actions.updateRetentionPolicyAsync} onUpdateRetentionPolicy={actions.updateRetentionPolicyAsync}
onRemoveRetentionPolicy={actions.removeRetentionPolicy} onRemoveRetentionPolicy={actions.removeRetentionPolicy}
onDeleteRetentionPolicy={actions.deleteRetentionPolicyAsync}
/> />
) )
} }
handleCreateDatabase(database) { handleCreateDatabase(database) {
const {actions, notify} = this.props const {actions, notify, source} = this.props
if (!database.name) { if (!database.name) {
return notify('error', 'Database name cannot be blank') return notify('error', 'Database name cannot be blank')
} }
actions.createDatabaseAsync(database) actions.createDatabaseAsync(source.links.databases, database)
} }
handleKeyDownDatabase(e, database) { handleKeyDownDatabase(e, database) {
const {key} = e const {key} = e
const {actions, notify} = this.props const {actions, notify, source} = this.props
if (key === 'Escape') { if (key === 'Escape') {
actions.removeDatabase(database) actions.removeDatabase(database)
@ -66,13 +67,13 @@ class DatabaseManagerPage extends Component {
return notify('error', 'Database name cannot be blank') return notify('error', 'Database name cannot be blank')
} }
actions.createDatabaseAsync(database) actions.createDatabaseAsync(source.links.databases, database)
} }
} }
handleDatabaseDeleteConfirm(database, e) { handleDatabaseDeleteConfirm(database, e) {
const {key, target: {value}} = e const {key, target: {value}} = e
const {actions, source, notify} = this.props const {actions, notify} = this.props
if (key === 'Escape') { if (key === 'Escape') {
return actions.removeDatabaseDeleteCode(database) return actions.removeDatabaseDeleteCode(database)
@ -83,7 +84,7 @@ class DatabaseManagerPage extends Component {
return notify('error', `Please type DELETE ${database.name} to confirm`) return notify('error', `Please type DELETE ${database.name} to confirm`)
} }
return actions.deleteDatabaseAsync(source, database) return actions.deleteDatabaseAsync(database)
} }
actions.editDatabase(database, {deleteCode: value}) actions.editDatabase(database, {deleteCode: value})
@ -125,6 +126,7 @@ DatabaseManagerPage.propTypes = {
startDeleteDatabase: func, startDeleteDatabase: func,
removeDatabaseDeleteCode: func, removeDatabaseDeleteCode: func,
removeRetentionPolicy: func, removeRetentionPolicy: func,
deleteRetentionPolicyAsync: func,
}), }),
notify: func, notify: func,
} }

View File

@ -99,6 +99,19 @@ export default function admin(state = initialState, action) {
const newState = { const newState = {
databases: state.databases.map(db => db.links.self === stale.links.self ? {...synced} : db), databases: state.databases.map(db => db.links.self === stale.links.self ? {...synced} : db),
} }
return {...state, ...newState}
}
case 'SYNC_RETENTION_POLICY': {
const {database, stale, synced} = action.payload
const newState = {
databases: state.databases.map(db => db.links.self === database.links.self ? {
...db,
retentionPolicies: db.retentionPolicies.map(rp => rp.links.self === stale.links.self ? {...synced} : rp),
} : db),
}
return {...state, ...newState} return {...state, ...newState}
} }