From c3520806c05b5b1f46c0f01eb54a89ebebeed9c6 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 13 Mar 2017 18:59:37 -0700 Subject: [PATCH 01/95] WIP introduce database manager --- ui/src/admin/components/AdminTabs.js | 7 +- ui/src/admin/components/DBManagementTable.js | 9 +++ ui/src/admin/components/DatabaseManager.js | 81 +++++++++++++++++++ .../admin/containers/DatabaseManagerPage.js | 53 ++++++++++++ ui/src/shared/apis/metaQuery.js | 27 +++---- ui/src/shared/parsing/showDatabases.js | 14 ++-- 6 files changed, 170 insertions(+), 21 deletions(-) create mode 100644 ui/src/admin/components/DBManagementTable.js create mode 100644 ui/src/admin/components/DatabaseManager.js create mode 100644 ui/src/admin/containers/DatabaseManagerPage.js diff --git a/ui/src/admin/components/AdminTabs.js b/ui/src/admin/components/AdminTabs.js index da9e1f0d2..7c7e898dd 100644 --- a/ui/src/admin/components/AdminTabs.js +++ b/ui/src/admin/components/AdminTabs.js @@ -1,8 +1,9 @@ import React, {PropTypes} from 'react' -import {Tab, Tabs, TabPanel, TabPanels, TabList} from 'src/shared/components/Tabs'; +import {Tab, Tabs, TabPanel, TabPanels, TabList} from 'src/shared/components/Tabs' import UsersTable from 'src/admin/components/UsersTable' import RolesTable from 'src/admin/components/RolesTable' import QueriesPage from 'src/admin/containers/QueriesPage' +import DatabaseManagerPage from 'src/admin/containers/DatabaseManagerPage' const AdminTabs = ({ users, @@ -29,6 +30,10 @@ const AdminTabs = ({ onUpdateUserPermissions, }) => { let tabs = [ + { + type: 'DB Management', + component: (), + }, { type: 'Users', component: ( diff --git a/ui/src/admin/components/DBManagementTable.js b/ui/src/admin/components/DBManagementTable.js new file mode 100644 index 000000000..55b80bd07 --- /dev/null +++ b/ui/src/admin/components/DBManagementTable.js @@ -0,0 +1,9 @@ +import React from 'react' + +const DatabaseManager = ({databases, retentionPolicies}) => { + return ( +
HI
+ ) +} + +export default DatabaseManager diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js new file mode 100644 index 000000000..7ef944a5a --- /dev/null +++ b/ui/src/admin/components/DatabaseManager.js @@ -0,0 +1,81 @@ +import React, {PropTypes} from 'react' + +const DatabaseManager = ({databases, retentionPolicies}) => { + return ( +
+ {databases.length} Databases +
+ { + databases.map((db, i) => + + ) + } +
+
+ ) +} + +const DatabaseTable = ({database, retentionPolicies}) => { + return ( +
+

{database}

+ + + + + + + + + + + { + retentionPolicies.map((rp, i) => { + return ( + + ) + }) + } + +
Retention PolicyDurationReplication Factor
+
+ ) +} + +const DatabaseRow = ({retentionPolicy}) => { + return ( + + {retentionPolicy.name} + + ) +} + +const { + arrayOf, + shape, + string, +} = PropTypes + +DatabaseManager.propTypes = { + databases: arrayOf(string), + retentionPolicies: arrayOf(arrayOf(shape)), +} + +DatabaseRow.propTypes = { + retentionPolicy: shape(), +} + +DatabaseTable.propTypes = { + database: string, + retentionPolicies: arrayOf(shape()), +} + +export default DatabaseManager + diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js new file mode 100644 index 000000000..f27c4004b --- /dev/null +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -0,0 +1,53 @@ +import React, {PropTypes, Component} from 'react' +import {showDatabases, showRetentionPolicies} from 'src/shared/apis/metaQuery' +import parseShowDatabases from 'src/shared/parsing/showDatabases' +import parseShowRetentionPolicies from 'src/shared/parsing/showRetentionPolicies' +import DatabaseManager from 'src/admin/components/DatabaseManager' + +class DatabaseManagerPage extends Component { + constructor(props) { + super(props) + this.state = { + databases: [], + retentionPolicies: [], + } + } + + async componentDidMount() { + const {source: {links: {proxy}}} = this.props + + const {data: dbs} = await showDatabases(proxy) + const {databases} = parseShowDatabases(dbs) + + const {data: {results}} = await showRetentionPolicies(proxy, databases) + const retentionPolicies = results.map(parseShowRetentionPolicies) + + this.setState({databases, retentionPolicies}) + } + + render() { + const {databases, retentionPolicies} = this.state + const rps = retentionPolicies.map((rp) => rp.retentionPolicies) + + if (!databases.length || !retentionPolicies.length) { + return null + } + + return + } +} + +const { + shape, + string, +} = PropTypes + +DatabaseManagerPage.propTypes = { + source: shape({ + links: shape({ + proxy: string, + }), + }), +} + +export default DatabaseManagerPage diff --git a/ui/src/shared/apis/metaQuery.js b/ui/src/shared/apis/metaQuery.js index 076ddca2e..646ae16ec 100644 --- a/ui/src/shared/apis/metaQuery.js +++ b/ui/src/shared/apis/metaQuery.js @@ -1,10 +1,20 @@ import AJAX from 'utils/ajax'; import {buildInfluxUrl, proxy} from 'utils/queryUrlGenerator'; -export function showDatabases(source) { - const query = `SHOW DATABASES`; +export const showDatabases = async (source) => { + const query = `SHOW DATABASES` + return await proxy({source, query}) +} - return proxy({source, query}); +export const showRetentionPolicies = async (source, databases) => { + let query + if (Array.isArray(databases)) { + query = databases.map((db) => `SHOW RETENTION POLICIES ON "${db}"`).join(';') + } else { + query = `SHOW RETENTION POLICIES ON "${databases}"` + } + + return await proxy({source, query}) } export function showQueries(source, db) { @@ -38,17 +48,6 @@ export function showTagValues({source, database, retentionPolicy, measurement, t return proxy({source, db: database, rp: retentionPolicy, query}); } -export function showRetentionPolicies(source, databases) { - let query; - if (Array.isArray(databases)) { - query = databases.map((db) => `SHOW RETENTION POLICIES ON "${db}"`).join(';'); - } else { - query = `SHOW RETENTION POLICIES ON "${databases}"`; - } - - return proxy({source, query}); -} - export function showShards() { return AJAX({ url: `/api/int/v1/show-shards`, diff --git a/ui/src/shared/parsing/showDatabases.js b/ui/src/shared/parsing/showDatabases.js index 259f337f1..4a4fb5b84 100644 --- a/ui/src/shared/parsing/showDatabases.js +++ b/ui/src/shared/parsing/showDatabases.js @@ -1,21 +1,23 @@ -export default function parseShowDatabases(response) { +const parseShowDatabases = (response) => { const results = response.results[0]; if (results.error) { - return {errors: [results.error], databases: []}; + return {errors: [results.error], databases: []} } - const series = results.series[0]; + const series = results.series[0] if (!series.values) { - return {errors: [], databases: []}; + return {errors: [], databases: []} } const databases = series.values.map((s) => { - return s[0]; + return s[0] }); if (!databases.length) { alert('No databases were found.'); // eslint-disable-line no-alert } - return {errors: [], databases}; + return {errors: [], databases} } + +export default parseShowDatabases From 47cdc368ddf564b6840baf1fa4dcb01e2104243f Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 14 Mar 2017 11:51:25 -0700 Subject: [PATCH 02/95] Display db and rp info --- ui/src/admin/components/DatabaseManager.js | 90 +++++++++++++++------- ui/src/style/pages/admin.scss | 31 +++++++- ui/src/style/theme/theme-dark.scss | 2 +- ui/src/utils/formatting.js | 6 +- 4 files changed, 96 insertions(+), 33 deletions(-) diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index 7ef944a5a..6bee1a7f9 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -1,9 +1,13 @@ import React, {PropTypes} from 'react' +import {formatRPDuration} from 'utils/formatting' const DatabaseManager = ({databases, retentionPolicies}) => { return (
- {databases.length} Databases +
+

{databases.length === 1 ? '1 Database' : `${databases.length} Databases`}

+
Create Database
+
{ databases.map((db, i) => @@ -21,44 +25,71 @@ const DatabaseManager = ({databases, retentionPolicies}) => { const DatabaseTable = ({database, retentionPolicies}) => { return ( -
-

{database}

- - - - - - - - - - - { - retentionPolicies.map((rp, i) => { - return ( - - ) - }) - } - -
Retention PolicyDurationReplication Factor
+
+
+

{database}

+
+ + +
+
+
+ + + + + + + + + + + { + retentionPolicies.map(({name, duration, replication, isDefault}) => { + return ( + + ) + }) + } + +
Retention PolicyDurationReplication Factor
+
) } -const DatabaseRow = ({retentionPolicy}) => { +const DatabaseRow = ({name, duration, replication, isDefault}) => { return ( - {retentionPolicy.name} + + {name} + {isDefault ? default : null} + + {formatRPDuration(duration)} + {replication} + + + ) } const { arrayOf, + bool, + number, shape, string, } = PropTypes @@ -69,7 +100,10 @@ DatabaseManager.propTypes = { } DatabaseRow.propTypes = { - retentionPolicy: shape(), + name: string, + duration: string, + replication: number, + isDefault: bool, } DatabaseTable.propTypes = { diff --git a/ui/src/style/pages/admin.scss b/ui/src/style/pages/admin.scss index a3a87812b..25e777fe7 100644 --- a/ui/src/style/pages/admin.scss +++ b/ui/src/style/pages/admin.scss @@ -119,4 +119,33 @@ &:first-child {margin-left: 0;} &:last-child {margin-right: 0;} } -} \ No newline at end of file +} + +.db-manager-header { + height: 32px; + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 11px; + + h4 { + margin: 0px; + color: $g15-platinum; + + } + + & .btn {display: none;} + &:hover .btn {display: inline-block;} +} +.db-manager { + margin-top: 18px; +} +.db-manager:first-child { + margin-top: 0; + } + +.db-manager-table { + background-color: $g4-onyx; + padding: 9px 11px; + border-radius: $radius-small; + } diff --git a/ui/src/style/theme/theme-dark.scss b/ui/src/style/theme/theme-dark.scss index 0a45f8067..beef5ea10 100644 --- a/ui/src/style/theme/theme-dark.scss +++ b/ui/src/style/theme/theme-dark.scss @@ -52,7 +52,7 @@ background-color: transparent; } .panel-body { - padding: 30px; + padding: 0px 30px 30px 30px; } .panel-heading { padding: 0 30px; diff --git a/ui/src/utils/formatting.js b/ui/src/utils/formatting.js index 7d28f22b9..25411e7be 100644 --- a/ui/src/utils/formatting.js +++ b/ui/src/utils/formatting.js @@ -1,4 +1,4 @@ -export function formatBytes(bytes) { +export const formatBytes = (bytes) => { if (bytes === 0) { return '0 Bytes'; } @@ -15,8 +15,8 @@ export function formatBytes(bytes) { return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`; } -export function formatRPDuration(duration) { - if (duration === '0') { +export const formatRPDuration = (duration) => { + if (duration === '0' || duration === '0s') { return 'infinite'; } From 615f94d0f904867e2d8cc69cb2cd3ef92117a03d Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 14 Mar 2017 13:53:37 -0700 Subject: [PATCH 03/95] Move dbs and rps to redux store --- ui/src/admin/actions/index.js | 34 ++++++++++++- .../admin/containers/DatabaseManagerPage.js | 49 ++++++++++--------- ui/src/admin/reducers/admin.js | 10 ++++ 3 files changed, 68 insertions(+), 25 deletions(-) diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index a662ca8ac..ea9da749d 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -10,7 +10,14 @@ import { updateUser as updateUserAJAX, } from 'src/admin/apis' -import {killQuery as killQueryProxy} from 'shared/apis/metaQuery' +import { + killQuery as killQueryProxy, + showDatabases, + showRetentionPolicies, +} 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 {ADMIN_NOTIFICATION_DELAY} from 'src/admin/constants' @@ -35,6 +42,20 @@ export const loadPermissions = ({permissions}) => ({ }, }) +export const loadDatabases = (databases) => ({ + type: 'LOAD_DATABASES', + payload: { + databases, + }, +}) + +export const loadRetentionPolicies = (retentionPolicies) => ({ + type: 'LOAD_RETENTION_POLICIES', + payload: { + retentionPolicies, + }, +}) + export const addUser = () => ({ type: 'ADD_USER', }) @@ -140,6 +161,17 @@ export const loadPermissionsAsync = (url) => async (dispatch) => { dispatch(loadPermissions(data)) } +export const loadDBsAndRPsAsync = (url) => async (dispatch) => { + const {data: dbs} = await showDatabases(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) + dispatch(loadRetentionPolicies(rps)) +} + export const createUserAsync = (url, user) => async (dispatch) => { try { const {data} = await createUserAJAX(url, user) diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index f27c4004b..a1e838455 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -1,43 +1,32 @@ import React, {PropTypes, Component} from 'react' -import {showDatabases, showRetentionPolicies} from 'src/shared/apis/metaQuery' -import parseShowDatabases from 'src/shared/parsing/showDatabases' -import parseShowRetentionPolicies from 'src/shared/parsing/showRetentionPolicies' +import {connect} from 'react-redux' +import {bindActionCreators} from 'redux' + +import {loadDBsAndRPsAsync} from 'src/admin/actions' import DatabaseManager from 'src/admin/components/DatabaseManager' class DatabaseManagerPage extends Component { constructor(props) { super(props) - this.state = { - databases: [], - retentionPolicies: [], - } } - async componentDidMount() { - const {source: {links: {proxy}}} = this.props + componentDidMount() { + const {source: {links: {proxy}}, loadDBsAndRPs} = this.props - const {data: dbs} = await showDatabases(proxy) - const {databases} = parseShowDatabases(dbs) - - const {data: {results}} = await showRetentionPolicies(proxy, databases) - const retentionPolicies = results.map(parseShowRetentionPolicies) - - this.setState({databases, retentionPolicies}) + loadDBsAndRPs(proxy) } render() { - const {databases, retentionPolicies} = this.state - const rps = retentionPolicies.map((rp) => rp.retentionPolicies) + const {databases, retentionPolicies} = this.props - if (!databases.length || !retentionPolicies.length) { - return null - } - - return + return } } const { + array, + arrayOf, + func, shape, string, } = PropTypes @@ -48,6 +37,18 @@ DatabaseManagerPage.propTypes = { proxy: string, }), }), + databases: arrayOf(string), + retentionPolicies: array, + loadDBsAndRPs: func, } -export default DatabaseManagerPage +const mapStateToProps = ({admin: {databases, retentionPolicies}}) => ({ + databases, + retentionPolicies, +}) + +const mapDispatchToProps = (dispatch) => ({ + loadDBsAndRPs: bindActionCreators(loadDBsAndRPsAsync, dispatch), +}) + +export default connect(mapStateToProps, mapDispatchToProps)(DatabaseManagerPage) diff --git a/ui/src/admin/reducers/admin.js b/ui/src/admin/reducers/admin.js index 189a18a59..ee997947d 100644 --- a/ui/src/admin/reducers/admin.js +++ b/ui/src/admin/reducers/admin.js @@ -22,6 +22,8 @@ const initialState = { permissions: [], queries: [], queryIDToKill: null, + databases: [], + retentionPolicies: [], } export default function admin(state = initialState, action) { @@ -38,6 +40,14 @@ export default function admin(state = initialState, action) { return {...state, ...action.payload} } + case 'LOAD_DATABASES': { + return {...state, ...action.payload} + } + + case 'LOAD_RETENTION_POLICIES': { + return {...state, ...action.payload} + } + case 'ADD_USER': { const newUser = {...newDefaultUser, isEditing: true} return { From be2860af7d82d788d556fd5ddb60e8c358e29df4 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Tue, 14 Mar 2017 14:01:28 -0700 Subject: [PATCH 04/95] db rp documentation WIP --- server/swagger.json | 209 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 208 insertions(+), 1 deletion(-) diff --git a/server/swagger.json b/server/swagger.json index 4b67b131c..d5730e84c 100644 --- a/server/swagger.json +++ b/server/swagger.json @@ -769,6 +769,174 @@ } } }, + "/sources/{id}/dbs/": { + "get": { + "summary": "Retrieve all databases for a source", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + } + ], + "responses": { + "200": { + "description": "Listing of all databases for a source", + "schema": { + "$ref": "#/definitions/Databases" + } + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "post": { + "summary": "Create new database for a source", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + } + ], + "responses": { + "201": { + "description": "Database successfully created.", + "schema": { + "$ref": "#/definitions/Database" + } + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/dbs/{db_id}": { + "get": { + + }, + "patch": { + + }, + "delete": { + + } + }, + "/sources/{id}/dbs/{db_id}/rps": { + "get": { + "summary": "Retrieve all retention policies for a database", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "db_id", + "in": "path", + "type": "string", + "description": "ID of the database", + "required": true + } + ], + "responses": { + "200": { + "description": "Listing of all retention policies for a database", + "schema": { + "$ref": "#/definitions/RetentionPolicies" + } + }, + "404": { + "description": "Specified retention policy does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "post": { + "summary": "Create new retention policy for a database", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "db_id", + "in": "path", + "type": "string", + "description": "ID of the database", + "required": true + } + ], + "responses": { + "201": { + "description": "Retention Policy successfully created.", + "schema": { + "$ref": "#/definitions/RetentionPolicy" + } + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/sources/{id}/dbs/{db_id}/rps/{rp_id}": { + "get": { + + }, + "patch": { + + }, + "delete": { + + } + }, "/sources/{id}/kapacitors": { "get": { "tags": [ @@ -1912,6 +2080,45 @@ } }, "definitions": { + "Databases": { + "type": "object", + "required": [ + "databases" + ], + "properties": { + "databases": { + "type": "array", + "items": { + "$ref": "#/definitions/Database" + } + } + } + }, + "Database": { + "type": "object", + "required": [ + "name" + ], + "example": { + + }, + "properties": { + "name": { + "type": "string", + "description": "The identifying name of the database", + }, + "duration": { + "type": "", + "description": "the duration of the default retention policy" + }, + "replication": { + + }, + "shardDuration": { + + } + } + }, "Kapacitors": { "type": "object", "required": [ @@ -3313,4 +3520,4 @@ } } } -} \ No newline at end of file +} From 460f23c0c0362841a800e3a62e45b763915b7a2a Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Tue, 14 Mar 2017 15:31:57 -0700 Subject: [PATCH 05/95] first whack at db rp server endpoints --- server/swagger.json | 136 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 124 insertions(+), 12 deletions(-) diff --git a/server/swagger.json b/server/swagger.json index d5730e84c..b1dc3e59c 100644 --- a/server/swagger.json +++ b/server/swagger.json @@ -811,6 +811,15 @@ "type": "string", "description": "ID of the data source", "required": true + }, + { + "name": "database", + "in": "body", + "description": "Configuration options for a database", + "schema": { + "$ref": "#/definitions/Database" + }, + "required": true } ], "responses": { @@ -836,14 +845,41 @@ } }, "/sources/{id}/dbs/{db_id}": { - "get": { - - }, - "patch": { - - }, "delete": { - + "summary": "Delete database for a source", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "db_id", + "in": "path", + "type": "string", + "description": "ID of the database", + "required": true + } + ], + "responses": { + "204": { + "description": "Database has been deleted", + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } } }, "/sources/{id}/dbs/{db_id}/rps": { @@ -902,6 +938,15 @@ "type": "string", "description": "ID of the database", "required": true + }, + { + "name": "rp", + "in": "body", + "description": "Configuration options for the retention policy", + "schema": { + "$ref": "#/definitions/RetentionPolicy" + }, + "required": true } ], "responses": { @@ -927,14 +972,81 @@ } }, "/sources/{id}/dbs/{db_id}/rps/{rp_id}": { - "get": { - - }, "patch": { - + "summary": "Alter retention policy for a database", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "db_id", + "in": "path", + "type": "string", + "description": "ID of the database", + "required": true + }, + { + "name": "rp_id", + "in": "path", + "description": "ID of the retention policy", + "required": true + } + { + "name": "rp", + "in": "body", + "description": "Configuration options for the retention policy", + "schema": { + "$ref": "#/definitions/RetentionPolicy" + }, + "required": true + } + ] }, "delete": { - + "summary": "Delete retention policy for a database", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "string", + "description": "ID of the data source", + "required": true + }, + { + "name": "db_id", + "in": "path", + "type": "string", + "description": "ID of the database", + "required": true + }, + { + "name": "rp_id", + "in": "path", + "description": "ID of the retention policy", + "required": true + } + ], + "responses": { + "204": { + "description": "Retention Policy has been deleted", + }, + "404": { + "description": "Data source id does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } } }, "/sources/{id}/kapacitors": { From 8c11ba392edd4563d9f43ded50635d4b59e95360 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 14 Mar 2017 15:45:04 -0700 Subject: [PATCH 06/95] Introduce addDatabase to store --- ui/src/admin/actions/index.js | 6 ++- ui/src/admin/components/DatabaseManager.js | 18 ++++---- .../admin/containers/DatabaseManagerPage.js | 46 +++++++++++++++---- ui/src/admin/reducers/admin.js | 30 ++++++++++++ 4 files changed, 79 insertions(+), 21 deletions(-) diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index ea9da749d..1dd6e3b61 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -64,6 +64,10 @@ export const addRole = () => ({ type: 'ADD_ROLE', }) +export const addDatabase = () => ({ + type: 'ADD_DATABASE', +}) + export const syncUser = (staleUser, syncedUser) => ({ type: 'SYNC_USER', payload: { @@ -164,7 +168,7 @@ export const loadPermissionsAsync = (url) => async (dispatch) => { export const loadDBsAndRPsAsync = (url) => async (dispatch) => { const {data: dbs} = await showDatabases(url) const {databases} = parseShowDatabases(dbs) - dispatch(loadDatabases(databases)) + dispatch(loadDatabases(databases.map(name => ({name})))) const {data: {results}} = await showRetentionPolicies(url, databases) const retentionPolicies = results.map(parseShowRetentionPolicies) diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index 6bee1a7f9..ada859bfe 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -1,19 +1,20 @@ import React, {PropTypes} from 'react' import {formatRPDuration} from 'utils/formatting' +import DatabaseTable from 'src/admin/components/DatabaseTable' -const DatabaseManager = ({databases, retentionPolicies}) => { +const DatabaseManager = ({databases, retentionPolicies, addDatabase}) => { return (

{databases.length === 1 ? '1 Database' : `${databases.length} Databases`}

-
Create Database
+
Create Database
{ databases.map((db, i) => ) @@ -89,14 +90,16 @@ const DatabaseRow = ({name, duration, replication, isDefault}) => { const { arrayOf, bool, + func, number, shape, string, } = PropTypes DatabaseManager.propTypes = { - databases: arrayOf(string), + databases: arrayOf(shape()), retentionPolicies: arrayOf(arrayOf(shape)), + addDatabase: func, } DatabaseRow.propTypes = { @@ -106,10 +109,5 @@ DatabaseRow.propTypes = { isDefault: bool, } -DatabaseTable.propTypes = { - database: string, - retentionPolicies: arrayOf(shape()), -} - export default DatabaseManager diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index a1e838455..db3d333ce 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -2,7 +2,7 @@ import React, {PropTypes, Component} from 'react' import {connect} from 'react-redux' import {bindActionCreators} from 'redux' -import {loadDBsAndRPsAsync} from 'src/admin/actions' +import * as adminActionCreators from 'src/admin/actions' import DatabaseManager from 'src/admin/components/DatabaseManager' class DatabaseManagerPage extends Component { @@ -11,22 +11,37 @@ class DatabaseManagerPage extends Component { } componentDidMount() { - const {source: {links: {proxy}}, loadDBsAndRPs} = this.props + const {source: {links: {proxy}}, actions} = this.props - loadDBsAndRPs(proxy) + actions.loadDBsAndRPsAsync(proxy) + } + + handleCreateDatabase() { + // this.props.createDatabase(database) + } + + handleAddDatabase() { + this.props.actions.addDatabase() } render() { - const {databases, retentionPolicies} = this.props + const {databases, retentionPolicies, actions} = this.props - return + return ( + + ) } } const { - array, arrayOf, + bool, func, + number, shape, string, } = PropTypes @@ -37,9 +52,20 @@ DatabaseManagerPage.propTypes = { proxy: string, }), }), - databases: arrayOf(string), - retentionPolicies: array, - loadDBsAndRPs: func, + databases: arrayOf(shape({ + name: string, + isEditing: bool, + })), + retentionPolicies: arrayOf(arrayOf(shape({ + name: string, + duration: string, + replication: number, + isDefault: bool, + }))), + actions: shape({ + loadDBsAndRPsAsync: func, + addDatabase: func, + }), } const mapStateToProps = ({admin: {databases, retentionPolicies}}) => ({ @@ -48,7 +74,7 @@ const mapStateToProps = ({admin: {databases, retentionPolicies}}) => ({ }) const mapDispatchToProps = (dispatch) => ({ - loadDBsAndRPs: bindActionCreators(loadDBsAndRPsAsync, dispatch), + actions: bindActionCreators(adminActionCreators, dispatch), }) export default connect(mapStateToProps, mapDispatchToProps)(DatabaseManagerPage) diff --git a/ui/src/admin/reducers/admin.js b/ui/src/admin/reducers/admin.js index ee997947d..698474169 100644 --- a/ui/src/admin/reducers/admin.js +++ b/ui/src/admin/reducers/admin.js @@ -16,6 +16,19 @@ const newDefaultRole = { isNew: true, } +const newDefaultDatabase = { + name: '', + isNew: true, +} + +const newDefaultRP = { + name: 'autogen', + duration: '0', + replication: 2, + isDefault: true, + isNew: true, +} + const initialState = { users: null, roles: [], @@ -70,6 +83,23 @@ export default function admin(state = initialState, action) { } } + case 'ADD_DATABASE': { + const newDatabase = {...newDefaultDatabase, isEditing: true} + const newRetentionPolicies = [{...newDefaultRP}] + + return { + ...state, + databases: [ + newDatabase, + ...state.databases, + ], + retentionPolicies: [ + newRetentionPolicies, + ...state.retentionPolicies, + ], + } + } + case 'SYNC_USER': { const {staleUser, syncedUser} = action.payload const newState = { From 5dfb3d3a3ec70d28af788ba8061e97be1aed28f9 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 14 Mar 2017 15:45:39 -0700 Subject: [PATCH 07/95] Move local components to independent files --- ui/src/admin/components/DatabaseManager.js | 45 ---------------- ui/src/admin/components/DatabaseRow.js | 33 ++++++++++++ ui/src/admin/components/DatabaseTable.js | 60 ++++++++++++++++++++++ 3 files changed, 93 insertions(+), 45 deletions(-) create mode 100644 ui/src/admin/components/DatabaseRow.js create mode 100644 ui/src/admin/components/DatabaseTable.js diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index ada859bfe..d0e964b4c 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -24,51 +24,6 @@ const DatabaseManager = ({databases, retentionPolicies, addDatabase}) => { ) } -const DatabaseTable = ({database, retentionPolicies}) => { - return ( -
-
-

{database}

-
- - -
-
-
- - - - - - - - - - - { - retentionPolicies.map(({name, duration, replication, isDefault}) => { - return ( - - ) - }) - } - -
Retention PolicyDurationReplication Factor
-
-
- ) -} - const DatabaseRow = ({name, duration, replication, isDefault}) => { return ( diff --git a/ui/src/admin/components/DatabaseRow.js b/ui/src/admin/components/DatabaseRow.js new file mode 100644 index 000000000..8521d5c76 --- /dev/null +++ b/ui/src/admin/components/DatabaseRow.js @@ -0,0 +1,33 @@ +import React, {PropTypes} from 'react' +import {formatRPDuration} from 'utils/formatting' + +export const DatabaseRow = ({name, duration, replication, isDefault}) => { + return ( + + + {name} + {isDefault ? default : null} + + {formatRPDuration(duration)} + {replication} + + + + + ) +} + +const { + bool, + number, + string, +} = PropTypes + +DatabaseRow.propTypes = { + name: string, + duration: string, + replication: number, + isDefault: bool, +} diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js new file mode 100644 index 000000000..8fcea3cd1 --- /dev/null +++ b/ui/src/admin/components/DatabaseTable.js @@ -0,0 +1,60 @@ +import React, {PropTypes} from 'react' +import {DatabaseRow} from 'src/admin/components/DatabaseRow' + +const DatabaseTable = ({database, retentionPolicies}) => { + return ( +
+
+

{database}

+
+ + +
+
+
+ + + + + + + + + + + { + retentionPolicies.map(({name, duration, replication, isDefault}) => { + return ( + + ) + }) + } + +
Retention PolicyDurationReplication Factor
+
+
+ ) +} + +const { + arrayOf, + shape, + string, +} = PropTypes + +DatabaseTable.propTypes = { + database: string, + retentionPolicies: arrayOf(shape()), +} + +export default DatabaseTable From 141c93bc5f5f7c0e395be767603550c4fb52cb0a Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Tue, 14 Mar 2017 16:05:13 -0700 Subject: [PATCH 08/95] db rp server endpoint objects --- server/swagger.json | 61 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/server/swagger.json b/server/swagger.json index b1dc3e59c..9d2a3c6f4 100644 --- a/server/swagger.json +++ b/server/swagger.json @@ -2212,7 +2212,10 @@ "name" ], "example": { - + "name": "NOAA_water_database", + "duration": "3d", + "replication": 3, + "shardDuration": "3h" }, "properties": { "name": { @@ -2220,14 +2223,17 @@ "description": "The identifying name of the database", }, "duration": { - "type": "", + "type": "string", "description": "the duration of the default retention policy" }, "replication": { - + "type": "integer", + "format": "int32", + "description": "how many copies of the data are stored in the cluster" }, "shardDuration": { - + "type": "string", + "description": "the interval spanned by each shard group" } } }, @@ -2469,6 +2475,53 @@ } } }, + "RetentionPolicies": { + "type": "object", + "required": [ + "retentionPolicies" + ], + "properties": { + "retentionPolicies": { + "type": "array", + "items": { + "$ref": "#/definitions/RetentionPolicy" + } + } + } + }, + "RetentionPolicy": { + "type": "object", + "required": [ + "name", + "duration", + "replication" + ], + "example": { + }, + "properties": { + "name": { + "type": "string", + "description": "The identifying name of the retention policy", + }, + "duration": { + "type": "string", + "description": "the duration of the retention policy" + }, + "replication": { + "type": "integer", + "format": "int32", + "description": "how many copies of the data are stored in the cluster" + }, + "shardDuration": { + "type": "string", + "description": "the interval spanned by each shard group" + }, + "default": { + "type": "boolean", + "description": "Indicates whether this retention policy should be the default" + } + } + }, "Rule": { "type": "object", "example": { From 310f7dc7c57eb701e03bfbc3656125db3fce0214 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Tue, 14 Mar 2017 16:44:44 -0700 Subject: [PATCH 09/95] fix swagger errors, add tags --- server/swagger.json | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/server/swagger.json b/server/swagger.json index 9d2a3c6f4..0baf6630a 100644 --- a/server/swagger.json +++ b/server/swagger.json @@ -771,6 +771,9 @@ }, "/sources/{id}/dbs/": { "get": { + "tags": [ + "databases" + ], "summary": "Retrieve all databases for a source", "parameters": [ { @@ -803,6 +806,9 @@ } }, "post": { + "tags": [ + "databases" + ], "summary": "Create new database for a source", "parameters": [ { @@ -846,6 +852,9 @@ }, "/sources/{id}/dbs/{db_id}": { "delete": { + "tags": [ + "databases" + ], "summary": "Delete database for a source", "parameters": [ { @@ -884,6 +893,9 @@ }, "/sources/{id}/dbs/{db_id}/rps": { "get": { + "tags": [ + "retention policies" + ], "summary": "Retrieve all retention policies for a database", "parameters": [ { @@ -923,6 +935,9 @@ } }, "post": { + "tags": [ + "retention policies" + ], "summary": "Create new retention policy for a database", "parameters": [ { @@ -973,6 +988,9 @@ }, "/sources/{id}/dbs/{db_id}/rps/{rp_id}": { "patch": { + "tags": [ + "retention policies" + ], "summary": "Alter retention policy for a database", "parameters": [ { @@ -992,9 +1010,10 @@ { "name": "rp_id", "in": "path", + "type": "string", "description": "ID of the retention policy", "required": true - } + }, { "name": "rp", "in": "body", @@ -1007,6 +1026,9 @@ ] }, "delete": { + "tags": [ + "retention policies" + ], "summary": "Delete retention policy for a database", "parameters": [ { @@ -1026,6 +1048,7 @@ { "name": "rp_id", "in": "path", + "type": "string", "description": "ID of the retention policy", "required": true } From b9e101c83144a93385fda63e1193ffccc802087c Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Tue, 14 Mar 2017 16:53:41 -0700 Subject: [PATCH 10/95] add example for retention policy --- server/swagger.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/swagger.json b/server/swagger.json index 0baf6630a..ae2598680 100644 --- a/server/swagger.json +++ b/server/swagger.json @@ -2520,6 +2520,11 @@ "replication" ], "example": { + "name": "weekly", + "duration": "7d", + "replication": 1, + "shardDuration": "7d", + "default": true }, "properties": { "name": { From f2f75a8963056058932405732656fccbf0674409 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 14 Mar 2017 17:56:30 -0700 Subject: [PATCH 11/95] Introduce add and edit database to state --- ui/src/admin/actions/index.js | 15 +++- ui/src/admin/components/DatabaseManager.js | 42 +++-------- ui/src/admin/components/DatabaseTable.js | 74 +++++++++++++++---- .../admin/containers/DatabaseManagerPage.js | 6 ++ ui/src/admin/reducers/admin.js | 13 +++- 5 files changed, 102 insertions(+), 48 deletions(-) diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index 1dd6e3b61..13e0e1f70 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -1,3 +1,5 @@ +import uuid from 'node-uuid'; + import { getUsers as getUsersAJAX, getRoles as getRolesAJAX, @@ -66,6 +68,9 @@ export const addRole = () => ({ export const addDatabase = () => ({ type: 'ADD_DATABASE', + payload: { + id: uuid.v4(), + }, }) export const syncUser = (staleUser, syncedUser) => ({ @@ -100,6 +105,14 @@ export const editRole = (role, updates) => ({ }, }) +export const editDatabase = (name, database) => ({ + type: 'EDIT_DATABASE', + payload: { + name, + database, + }, +}) + export const killQuery = (queryID) => ({ type: 'KILL_QUERY', payload: { @@ -168,7 +181,7 @@ export const loadPermissionsAsync = (url) => async (dispatch) => { export const loadDBsAndRPsAsync = (url) => async (dispatch) => { const {data: dbs} = await showDatabases(url) const {databases} = parseShowDatabases(dbs) - dispatch(loadDatabases(databases.map(name => ({name})))) + dispatch(loadDatabases(databases.map(name => ({name, id: uuid.v4()})))) const {data: {results}} = await showRetentionPolicies(url, databases) const retentionPolicies = results.map(parseShowRetentionPolicies) diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index d0e964b4c..95e727e94 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -1,8 +1,12 @@ import React, {PropTypes} from 'react' -import {formatRPDuration} from 'utils/formatting' import DatabaseTable from 'src/admin/components/DatabaseTable' -const DatabaseManager = ({databases, retentionPolicies, addDatabase}) => { +const DatabaseManager = ({ + databases, + retentionPolicies, + addDatabase, + onEditDatabase, +}) => { return (
@@ -13,9 +17,10 @@ const DatabaseManager = ({databases, retentionPolicies, addDatabase}) => { { databases.map((db, i) => ) } @@ -24,44 +29,17 @@ const DatabaseManager = ({databases, retentionPolicies, addDatabase}) => { ) } -const DatabaseRow = ({name, duration, replication, isDefault}) => { - return ( - - - {name} - {isDefault ? default : null} - - {formatRPDuration(duration)} - {replication} - - - - - ) -} - const { arrayOf, - bool, func, - number, shape, - string, } = PropTypes DatabaseManager.propTypes = { databases: arrayOf(shape()), retentionPolicies: arrayOf(arrayOf(shape)), addDatabase: func, -} - -DatabaseRow.propTypes = { - name: string, - duration: string, - replication: number, - isDefault: bool, + onEditDatabase: func, } export default DatabaseManager diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index 8fcea3cd1..6128a925b 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -1,20 +1,10 @@ import React, {PropTypes} from 'react' import {DatabaseRow} from 'src/admin/components/DatabaseRow' -const DatabaseTable = ({database, retentionPolicies}) => { +const DatabaseTable = ({database, retentionPolicies, onEditDatabase}) => { return (
-
-

{database}

-
- - -
-
+
@@ -46,15 +36,71 @@ const DatabaseTable = ({database, retentionPolicies}) => { ) } +const DatabaseTableHeader = ({database, onEdit}) => { + if (database.isEditing) { + return + } + + return
+} + +const Header = ({database}) => ( +
+

{database.name}

+
+ + +
+
+) + +const EditHeader = ({database, onEdit, onKeyPress}) => ( +
+

+
+ onEdit(e.target.value, database)} + onKeyPress={(e) => onKeyPress(e, database)} + autoFocus={true} + /> +
+

+
+) + const { arrayOf, + func, shape, - string, } = PropTypes DatabaseTable.propTypes = { - database: string, + onEditDatabase: func, + database: shape(), retentionPolicies: arrayOf(shape()), } +Header.propTypes = { + database: shape(), +} + +EditHeader.propTypes = { + onEdit: func, + database: shape(), +} + +DatabaseTableHeader.propTypes = { + onEdit: func, + database: shape(), +} + export default DatabaseTable diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index db3d333ce..ff821f2b8 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -8,6 +8,7 @@ import DatabaseManager from 'src/admin/components/DatabaseManager' class DatabaseManagerPage extends Component { constructor(props) { super(props) + this.handleEditDatabase = ::this.handleEditDatabase } componentDidMount() { @@ -20,6 +21,10 @@ class DatabaseManagerPage extends Component { // this.props.createDatabase(database) } + handleEditDatabase(updates, database) { + this.props.actions.editDatabase(updates, database) + } + handleAddDatabase() { this.props.actions.addDatabase() } @@ -32,6 +37,7 @@ class DatabaseManagerPage extends Component { addDatabase={actions.addDatabase} databases={databases} retentionPolicies={retentionPolicies} + onEditDatabase={this.handleEditDatabase} /> ) } diff --git a/ui/src/admin/reducers/admin.js b/ui/src/admin/reducers/admin.js index 698474169..6c1b54f2e 100644 --- a/ui/src/admin/reducers/admin.js +++ b/ui/src/admin/reducers/admin.js @@ -8,6 +8,7 @@ const newDefaultUser = { links: {self: ''}, isNew: true, } + const newDefaultRole = { name: '', permissions: [], @@ -84,7 +85,8 @@ export default function admin(state = initialState, action) { } case 'ADD_DATABASE': { - const newDatabase = {...newDefaultDatabase, isEditing: true} + const {id} = action.payload + const newDatabase = {...newDefaultDatabase, id, isEditing: true} const newRetentionPolicies = [{...newDefaultRP}] return { @@ -132,6 +134,15 @@ export default function admin(state = initialState, action) { return {...state, ...newState} } + case 'EDIT_DATABASE': { + const {database, name} = action.payload + const newState = { + databases: state.databases.map(db => db.id === database.id ? {...db, name} : db), + } + + return {...state, ...newState} + } + case 'DELETE_USER': { const {user} = action.payload const newState = { From e114681ce6c6e4db12b8da668df4c42a94fc1786 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 15 Mar 2017 09:52:40 -0700 Subject: [PATCH 12/95] Introduce skeleton for confirming and canceling db creation --- ui/src/admin/actions/index.js | 30 ++++++++++++ ui/src/admin/apis/index.js | 12 +++++ ui/src/admin/components/DatabaseManager.js | 9 ++++ ui/src/admin/components/DatabaseTable.js | 46 ++++++++++++++++--- .../admin/containers/DatabaseManagerPage.js | 23 +++++++--- ui/src/admin/reducers/admin.js | 17 +++++++ 6 files changed, 124 insertions(+), 13 deletions(-) diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index 13e0e1f70..dcdce7ff4 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -6,6 +6,7 @@ import { getPermissions as getPermissionsAJAX, createUser as createUserAJAX, createRole as createRoleAJAX, + createDatabase as createDatabaseAJAX, deleteUser as deleteUserAJAX, deleteRole as deleteRoleAJAX, updateRole as updateRoleAJAX, @@ -89,6 +90,14 @@ export const syncRole = (staleRole, syncedRole) => ({ }, }) +export const syncDatabase = (stale, synced) => ({ + type: 'SYNC_DATABASE', + payload: { + stale, + synced, + }, +}) + export const editUser = (user, updates) => ({ type: 'EDIT_USER', payload: { @@ -134,6 +143,7 @@ export const loadQueries = (queries) => ({ }, }) +// TODO: change to 'removeUser' export const deleteUser = (user) => ({ type: 'DELETE_USER', payload: { @@ -141,6 +151,7 @@ export const deleteUser = (user) => ({ }, }) +// TODO: change to 'removeRole' export const deleteRole = (role) => ({ type: 'DELETE_ROLE', payload: { @@ -148,6 +159,13 @@ export const deleteRole = (role) => ({ }, }) +export const removeDatabase = (database) => ({ + type: 'REMOVE_DATABASE', + payload: { + database, + }, +}) + export const filterUsers = (text) => ({ type: 'FILTER_USERS', payload: { @@ -213,6 +231,18 @@ export const createRoleAsync = (url, role) => async (dispatch) => { } } +export const createDatabaseAsync = (url, database) => async (dispatch) => { + try { + // const {data} = await createDatabaseAJAX(url, database) + dispatch(publishNotification('success', 'Database created successfully')) + // dispatch(syncDatabase(database, {...data, id: uuid.v4()})) + } catch (error) { + // undo optimistic update + dispatch(publishNotification('error', `Failed to create database: ${error.data.message}`)) + setTimeout(() => dispatch(removeDatabase(database)), ADMIN_NOTIFICATION_DELAY) + } +} + export const killQueryAsync = (source, queryID) => (dispatch) => { // optimistic update dispatch(killQuery(queryID)) diff --git a/ui/src/admin/apis/index.js b/ui/src/admin/apis/index.js index 6c7d747ae..7917beee7 100644 --- a/ui/src/admin/apis/index.js +++ b/ui/src/admin/apis/index.js @@ -60,6 +60,18 @@ export const createRole = async (url, role) => { } } +export const createDatabase = async (url, database) => { + try { + return await AJAX({ + method: 'POST', + url, + data: database, + }) + } catch (error) { + throw error + } +} + export const deleteRole = async (url, addFlashMessage, rolename) => { try { const response = await AJAX({ diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index 95e727e94..43d7eb80f 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -6,6 +6,9 @@ const DatabaseManager = ({ retentionPolicies, addDatabase, onEditDatabase, + onKeyDownDatabase, + onCancelDatabase, + onConfirmDatabase, }) => { return (
@@ -21,6 +24,9 @@ const DatabaseManager = ({ database={db} retentionPolicies={retentionPolicies[i] || []} onEditDatabase={onEditDatabase} + onKeyDownDatabase={onKeyDownDatabase} + onCancelDatabase={onCancelDatabase} + onConfirmDatabase={onConfirmDatabase} /> ) } @@ -40,6 +46,9 @@ DatabaseManager.propTypes = { retentionPolicies: arrayOf(arrayOf(shape)), addDatabase: func, onEditDatabase: func, + onKeyDownDatabase: func, + onCancelDatabase: func, + onConfirmDatabase: func, } export default DatabaseManager diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index 6128a925b..afd31afb6 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -1,10 +1,24 @@ import React, {PropTypes} from 'react' import {DatabaseRow} from 'src/admin/components/DatabaseRow' +import ConfirmButtons from 'src/admin/components/ConfirmButtons' -const DatabaseTable = ({database, retentionPolicies, onEditDatabase}) => { +const DatabaseTable = ({ + database, + retentionPolicies, + onEditDatabase, + onKeyDownDatabase, + onCancelDatabase, + onConfirmDatabase, +}) => { return (
- +
@@ -36,9 +50,17 @@ const DatabaseTable = ({database, retentionPolicies, onEditDatabase}) => { ) } -const DatabaseTableHeader = ({database, onEdit}) => { +const DatabaseTableHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel}) => { if (database.isEditing) { - return + return ( + + ) } return
@@ -58,7 +80,7 @@ const Header = ({database}) => ( ) -const EditHeader = ({database, onEdit, onKeyPress}) => ( +const EditHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel}) => (

@@ -69,11 +91,12 @@ const EditHeader = ({database, onEdit, onKeyPress}) => ( value={database.name} placeholder="database name" onChange={(e) => onEdit(e.target.value, database)} - onKeyPress={(e) => onKeyPress(e, database)} + onKeyDown={(e) => onKeyDown(e, database)} autoFocus={true} />

+
) @@ -87,6 +110,9 @@ DatabaseTable.propTypes = { onEditDatabase: func, database: shape(), retentionPolicies: arrayOf(shape()), + onKeyDownDatabase: func, + onCancelDatabase: func, + onConfirmDatabase: func, } Header.propTypes = { @@ -94,13 +120,19 @@ Header.propTypes = { } EditHeader.propTypes = { - onEdit: func, database: shape(), + onEdit: func, + onKeyDown: func, + onCancel: func, + onConfirm: func, } DatabaseTableHeader.propTypes = { onEdit: func, database: shape(), + onKeyDown: func, + onCancel: func, + onConfirm: func, } export default DatabaseTable diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index ff821f2b8..7d90eda75 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -9,6 +9,7 @@ class DatabaseManagerPage extends Component { constructor(props) { super(props) this.handleEditDatabase = ::this.handleEditDatabase + this.handleKeyDownDatabase = ::this.handleKeyDownDatabase } componentDidMount() { @@ -17,16 +18,21 @@ class DatabaseManagerPage extends Component { actions.loadDBsAndRPsAsync(proxy) } - handleCreateDatabase() { - // this.props.createDatabase(database) - } - handleEditDatabase(updates, database) { this.props.actions.editDatabase(updates, database) } - handleAddDatabase() { - this.props.actions.addDatabase() + handleKeyDownDatabase(e, database) { + const {key} = e + const {actions} = this.props + + if (key === 'Escape') { + actions.removeDatabase(database) + } + + if (key === 'Enter') { + actions.createDatabaseAsync(database) + } } render() { @@ -38,6 +44,9 @@ class DatabaseManagerPage extends Component { databases={databases} retentionPolicies={retentionPolicies} onEditDatabase={this.handleEditDatabase} + onKeyDownDatabase={this.handleKeyDownDatabase} + onCancelDatabase={actions.removeDatabase} + onConfirmDatabase={actions.createDatabaseAsync} /> ) } @@ -70,7 +79,9 @@ DatabaseManagerPage.propTypes = { }))), actions: shape({ loadDBsAndRPsAsync: func, + createDatabaseAsync: func, addDatabase: func, + removeDatabase: func, }), } diff --git a/ui/src/admin/reducers/admin.js b/ui/src/admin/reducers/admin.js index 6c1b54f2e..a4a45015b 100644 --- a/ui/src/admin/reducers/admin.js +++ b/ui/src/admin/reducers/admin.js @@ -118,6 +118,14 @@ export default function admin(state = initialState, action) { return {...state, ...newState} } + case 'SYNC_DATABASE': { + const {stale, synced} = action.payload + const newState = { + databases: state.databases.map(db => db.id === stale.id ? {...synced} : db), + } + return {...state, ...newState} + } + case 'EDIT_USER': { const {user, updates} = action.payload const newState = { @@ -161,6 +169,15 @@ export default function admin(state = initialState, action) { return {...state, ...newState} } + case 'REMOVE_DATABASE': { + const {database} = action.payload + const newState = { + databases: state.databases.filter(db => db.id !== database.id), + } + + return {...state, ...newState} + } + case 'LOAD_QUERIES': { return {...state, ...action.payload} } From 9d63d2ac134ca7d97df653f325f3473cb6910be2 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 15 Mar 2017 10:00:23 -0700 Subject: [PATCH 13/95] Remove unnecessary handler --- ui/src/admin/containers/DatabaseManagerPage.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index 7d90eda75..0335365f5 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -8,7 +8,6 @@ import DatabaseManager from 'src/admin/components/DatabaseManager' class DatabaseManagerPage extends Component { constructor(props) { super(props) - this.handleEditDatabase = ::this.handleEditDatabase this.handleKeyDownDatabase = ::this.handleKeyDownDatabase } @@ -18,10 +17,6 @@ class DatabaseManagerPage extends Component { actions.loadDBsAndRPsAsync(proxy) } - handleEditDatabase(updates, database) { - this.props.actions.editDatabase(updates, database) - } - handleKeyDownDatabase(e, database) { const {key} = e const {actions} = this.props @@ -43,8 +38,8 @@ class DatabaseManagerPage extends Component { addDatabase={actions.addDatabase} databases={databases} retentionPolicies={retentionPolicies} - onEditDatabase={this.handleEditDatabase} onKeyDownDatabase={this.handleKeyDownDatabase} + onEditDatabase={actions.editDatabase} onCancelDatabase={actions.removeDatabase} onConfirmDatabase={actions.createDatabaseAsync} /> From 94e4ea3d4bfa7f1c95670f5599649c6c8a8f11f8 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 15 Mar 2017 13:57:28 -0700 Subject: [PATCH 14/95] Introduce delete confirmation for database --- ui/src/admin/actions/index.js | 36 +++++ ui/src/admin/apis/index.js | 12 ++ ui/src/admin/components/DatabaseManager.js | 6 + ui/src/admin/components/DatabaseTable.js | 128 +++++++++++++----- .../admin/containers/DatabaseManagerPage.js | 23 +++- ui/src/admin/reducers/admin.js | 29 ++++ ui/src/style/pages/admin.scss | 17 +++ 7 files changed, 215 insertions(+), 36 deletions(-) diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index dcdce7ff4..f369fea38 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -7,6 +7,7 @@ import { createUser as createUserAJAX, createRole as createRoleAJAX, createDatabase as createDatabaseAJAX, + deleteDatabase as deleteDatabaseAJAX, deleteUser as deleteUserAJAX, deleteRole as deleteRoleAJAX, updateRole as updateRoleAJAX, @@ -180,6 +181,28 @@ export const filterRoles = (text) => ({ }, }) +export const startDeleteDatabase = (database) => ({ + type: 'START_DELETE_DATABASE', + payload: { + database, + }, +}) + +export const updateDatabaseDeleteCode = (database, deleteCode) => ({ + type: 'UPDATE_DATABASE_DELETE_CODE', + payload: { + database, + deleteCode, + }, +}) + +export const removeDatabaseDeleteCode = (database) => ({ + type: 'REMOVE_DATABASE_DELETE_CODE', + payload: { + database, + }, +}) + // async actions export const loadUsersAsync = (url) => async (dispatch) => { const {data} = await getUsersAJAX(url) @@ -233,6 +256,7 @@ export const createRoleAsync = (url, role) => async (dispatch) => { export const createDatabaseAsync = (url, database) => async (dispatch) => { try { + // TODO: implement once server is up // const {data} = await createDatabaseAJAX(url, database) dispatch(publishNotification('success', 'Database created successfully')) // dispatch(syncDatabase(database, {...data, id: uuid.v4()})) @@ -268,6 +292,18 @@ export const deleteUserAsync = (user, addFlashMessage) => (dispatch) => { deleteUserAJAX(user.links.self, addFlashMessage, user.name) } +export const deleteDatabaseAsync = (url, database) => (dispatch) => { + dispatch(removeDatabase(database)) + dispatch(publishNotification('success', 'Database deleted')) + + // TODO: implement once server is up + // try { + // await deleteDatabaseAJAX(url, database.name) + // } catch (error) { + // dispatch(publishNotification('error', `Failed to delete database: ${error.data.message}`)) + // } +} + export const updateRoleUsersAsync = (role, users) => async (dispatch) => { try { const {data} = await updateRoleAJAX(role.links.self, users, role.permissions) diff --git a/ui/src/admin/apis/index.js b/ui/src/admin/apis/index.js index 7917beee7..01e0146bb 100644 --- a/ui/src/admin/apis/index.js +++ b/ui/src/admin/apis/index.js @@ -112,6 +112,18 @@ export const deleteUser = async (url, addFlashMessage, username) => { } } +export const deleteDatabase = async (url, name) => { + try { + return await AJAX({ + method: 'DELETE', + url: `${url}/${name}`, + }) + } catch (error) { + console.error(error) + throw error + } +} + export const updateRole = async (url, users, permissions) => { try { return await AJAX({ diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index 43d7eb80f..88f80f142 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -9,6 +9,8 @@ const DatabaseManager = ({ onKeyDownDatabase, onCancelDatabase, onConfirmDatabase, + onStartDeleteDatabase, + onDatabaseDeleteConfirm, }) => { return (
@@ -27,6 +29,8 @@ const DatabaseManager = ({ onKeyDownDatabase={onKeyDownDatabase} onCancelDatabase={onCancelDatabase} onConfirmDatabase={onConfirmDatabase} + onStartDeleteDatabase={onStartDeleteDatabase} + onDatabaseDeleteConfirm={onDatabaseDeleteConfirm} /> ) } @@ -49,6 +53,8 @@ DatabaseManager.propTypes = { onKeyDownDatabase: func, onCancelDatabase: func, onConfirmDatabase: func, + onStartDeleteDatabase: func, + onDatabaseDeleteConfirm: func, } export default DatabaseManager diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index afd31afb6..ad22ccff0 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -2,6 +2,12 @@ import React, {PropTypes} from 'react' import {DatabaseRow} from 'src/admin/components/DatabaseRow' import ConfirmButtons from 'src/admin/components/ConfirmButtons' +const { + arrayOf, + func, + shape, +} = PropTypes + const DatabaseTable = ({ database, retentionPolicies, @@ -9,6 +15,8 @@ const DatabaseTable = ({ onKeyDownDatabase, onCancelDatabase, onConfirmDatabase, + onStartDeleteDatabase, + onDatabaseDeleteConfirm, }) => { return (
@@ -18,6 +26,8 @@ const DatabaseTable = ({ onKeyDown={onKeyDownDatabase} onCancel={onCancelDatabase} onConfirm={onConfirmDatabase} + onStartDelete={onStartDeleteDatabase} + onDatabaseDeleteConfirm={onDatabaseDeleteConfirm} />
@@ -50,7 +60,26 @@ const DatabaseTable = ({ ) } -const DatabaseTableHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel}) => { +DatabaseTable.propTypes = { + onEditDatabase: func, + database: shape(), + retentionPolicies: arrayOf(shape()), + onKeyDownDatabase: func, + onCancelDatabase: func, + onConfirmDatabase: func, + onStartDeleteDatabase: func, + onDatabaseDeleteConfirm: func, +} + +const DatabaseTableHeader = ({ + database, + onEdit, + onKeyDown, + onConfirm, + onCancel, + onStartDelete, + onDatabaseDeleteConfirm, +}) => { if (database.isEditing) { return ( + return ( +
+ ) } -const Header = ({database}) => ( -
-

{database.name}

+DatabaseTableHeader.propTypes = { + onEdit: func, + database: shape(), + onKeyDown: func, + onCancel: func, + onConfirm: func, + onStartDelete: func, + onDatabaseDeleteConfirm: func, +} + +const Header = ({ + database, + onStartDelete, + onDatabaseDeleteConfirm, +}) => { + const confirmStyle = { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + } + + const buttons = (
-
-
-) + ) + + const deleteConfirm = ( +
+
+ onDatabaseDeleteConfirm(database, e)} + onKeyDown={(e) => onDatabaseDeleteConfirm(database, e)} + autoFocus={true} + /> +
+ {}} onCancel={() => {}} /> +
+ ) + + return ( +
+

{database.name}

+ {database.hasOwnProperty('deleteCode') ? deleteConfirm : buttons} +
+ ) +} + +Header.propTypes = { + database: shape(), + onStartDelete: func, + onDatabaseDeleteConfirm: func, +} const EditHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel}) => (
@@ -100,25 +185,6 @@ const EditHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel}) => (
) -const { - arrayOf, - func, - shape, -} = PropTypes - -DatabaseTable.propTypes = { - onEditDatabase: func, - database: shape(), - retentionPolicies: arrayOf(shape()), - onKeyDownDatabase: func, - onCancelDatabase: func, - onConfirmDatabase: func, -} - -Header.propTypes = { - database: shape(), -} - EditHeader.propTypes = { database: shape(), onEdit: func, @@ -127,12 +193,4 @@ EditHeader.propTypes = { onConfirm: func, } -DatabaseTableHeader.propTypes = { - onEdit: func, - database: shape(), - onKeyDown: func, - onCancel: func, - onConfirm: func, -} - export default DatabaseTable diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index 0335365f5..c174027f4 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -9,6 +9,7 @@ class DatabaseManagerPage extends Component { constructor(props) { super(props) this.handleKeyDownDatabase = ::this.handleKeyDownDatabase + this.handleDatabaseDeleteConfirm = ::this.handleDatabaseDeleteConfirm } componentDidMount() { @@ -30,18 +31,35 @@ class DatabaseManagerPage extends Component { } } + handleDatabaseDeleteConfirm(database, e) { + const {key, target: {value}} = e + const {actions, source} = this.props + + if (key === 'Escape') { + return actions.removeDatabaseDeleteCode(database) + } + + if (key === 'Enter' && database.deleteCode === 'DELETE') { + return actions.deleteDatabaseAsync(source, database) + } + + actions.updateDatabaseDeleteCode(database, value) + } + render() { const {databases, retentionPolicies, actions} = this.props return ( ) } @@ -77,6 +95,9 @@ DatabaseManagerPage.propTypes = { createDatabaseAsync: func, addDatabase: func, removeDatabase: func, + startDeleteDatabase: func, + updateDatabaseDeleteCode: func, + removeDatabaseDeleteCode: func, }), } diff --git a/ui/src/admin/reducers/admin.js b/ui/src/admin/reducers/admin.js index a4a45015b..a389fd53f 100644 --- a/ui/src/admin/reducers/admin.js +++ b/ui/src/admin/reducers/admin.js @@ -178,6 +178,35 @@ export default function admin(state = initialState, action) { return {...state, ...newState} } + case 'START_DELETE_DATABASE': { + const {database} = action.payload + const newState = { + databases: state.databases.map(db => db.id === database.id ? {...db, deleteCode: ''} : db), + } + + return {...state, ...newState} + } + + case 'UPDATE_DATABASE_DELETE_CODE': { + const {database, deleteCode} = action.payload + const newState = { + databases: state.databases.map(db => db.id === database.id ? {...db, deleteCode} : db), + } + + return {...state, ...newState} + } + + case 'REMOVE_DATABASE_DELETE_CODE': { + const {database} = action.payload + delete database.deleteCode + + const newState = { + databases: state.databases.map(db => db.id === database.id ? {...database} : db), + } + + return {...state, ...newState} + } + case 'LOAD_QUERIES': { return {...state, ...action.payload} } diff --git a/ui/src/style/pages/admin.scss b/ui/src/style/pages/admin.scss index 25e777fe7..d7975b00c 100644 --- a/ui/src/style/pages/admin.scss +++ b/ui/src/style/pages/admin.scss @@ -121,6 +121,23 @@ } } +.admin-table--delete-cell { + margin: 0 !important; + display: flex !important; + justify-content: space-between; + + > input { + height: 30px; + padding: 0 9px; + flex-grow: 1; + margin: 0 2px; + min-width: 110px; + + &:first-child {margin-left: 0;} + &:last-child {margin-right: 0;} + } +} + .db-manager-header { height: 32px; display: flex; From 885e40fb6e49912111ee29d94a7a8e0a98f5bdfd Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 15 Mar 2017 16:21:38 -0700 Subject: [PATCH 15/95] Introduce add retention policy to state --- ui/src/admin/actions/index.js | 15 +++++-- ui/src/admin/components/DatabaseManager.js | 8 ++-- ui/src/admin/components/DatabaseTable.js | 17 +++++--- .../admin/containers/DatabaseManagerPage.js | 37 +++++++++--------- ui/src/admin/reducers/admin.js | 39 ++++++++++++------- 5 files changed, 70 insertions(+), 46 deletions(-) diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index f369fea38..44dabac50 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -75,6 +75,14 @@ export const addDatabase = () => ({ }, }) +export const addRetentionPolicy = (database) => ({ + type: 'ADD_RETENTION_POLICY', + payload: { + id: uuid.v4(), + database, + }, +}) + export const syncUser = (staleUser, syncedUser) => ({ type: 'SYNC_USER', payload: { @@ -222,12 +230,13 @@ export const loadPermissionsAsync = (url) => async (dispatch) => { export const loadDBsAndRPsAsync = (url) => async (dispatch) => { const {data: dbs} = await showDatabases(url) const {databases} = parseShowDatabases(dbs) - dispatch(loadDatabases(databases.map(name => ({name, id: uuid.v4()})))) const {data: {results}} = await showRetentionPolicies(url, databases) const retentionPolicies = results.map(parseShowRetentionPolicies) - const rps = retentionPolicies.map((rp) => rp.retentionPolicies) - dispatch(loadRetentionPolicies(rps)) + const rps = retentionPolicies.map(rp => rp.retentionPolicies) + const dbsAndRps = databases.map((name, i) => ({name, id: uuid.v4(), retentionPolicies: rps[i]})) + + dispatch(loadDatabases(dbsAndRps)) } export const createUserAsync = (url, user) => async (dispatch) => { diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index 88f80f142..582a40987 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -3,7 +3,6 @@ import DatabaseTable from 'src/admin/components/DatabaseTable' const DatabaseManager = ({ databases, - retentionPolicies, addDatabase, onEditDatabase, onKeyDownDatabase, @@ -11,6 +10,7 @@ const DatabaseManager = ({ onConfirmDatabase, onStartDeleteDatabase, onDatabaseDeleteConfirm, + onAddRetentionPolicy, }) => { return (
@@ -20,17 +20,17 @@ const DatabaseManager = ({
{ - databases.map((db, i) => + databases.map(db => ) } @@ -47,7 +47,6 @@ const { DatabaseManager.propTypes = { databases: arrayOf(shape()), - retentionPolicies: arrayOf(arrayOf(shape)), addDatabase: func, onEditDatabase: func, onKeyDownDatabase: func, @@ -55,6 +54,7 @@ DatabaseManager.propTypes = { onConfirmDatabase: func, onStartDeleteDatabase: func, onDatabaseDeleteConfirm: func, + onAddRetentionPolicy: func, } export default DatabaseManager diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index ad22ccff0..060f0a70b 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -3,20 +3,19 @@ import {DatabaseRow} from 'src/admin/components/DatabaseRow' import ConfirmButtons from 'src/admin/components/ConfirmButtons' const { - arrayOf, func, shape, } = PropTypes const DatabaseTable = ({ database, - retentionPolicies, onEditDatabase, onKeyDownDatabase, onCancelDatabase, onConfirmDatabase, onStartDeleteDatabase, onDatabaseDeleteConfirm, + onAddRetentionPolicy, }) => { return (
@@ -28,6 +27,7 @@ const DatabaseTable = ({ onConfirm={onConfirmDatabase} onStartDelete={onStartDeleteDatabase} onDatabaseDeleteConfirm={onDatabaseDeleteConfirm} + onAddRetentionPolicy={onAddRetentionPolicy} />
@@ -41,7 +41,7 @@ const DatabaseTable = ({ { - retentionPolicies.map(({name, duration, replication, isDefault}) => { + database.retentionPolicies.map(({name, duration, replication, isDefault}) => { return ( { if (database.isEditing) { return ( @@ -97,6 +98,7 @@ const DatabaseTableHeader = ({ database={database} onStartDelete={onStartDelete} onDatabaseDeleteConfirm={onDatabaseDeleteConfirm} + onAddRetentionPolicy={onAddRetentionPolicy} /> ) } @@ -109,12 +111,14 @@ DatabaseTableHeader.propTypes = { onConfirm: func, onStartDelete: func, onDatabaseDeleteConfirm: func, + onAddRetentionPolicy: func, } const Header = ({ database, onStartDelete, onDatabaseDeleteConfirm, + onAddRetentionPolicy, }) => { const confirmStyle = { display: 'flex', @@ -127,8 +131,8 @@ const Header = ({ - ) @@ -163,6 +167,7 @@ Header.propTypes = { database: shape(), onStartDelete: func, onDatabaseDeleteConfirm: func, + onAddRetentionPolicy: func, } const EditHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel}) => ( diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index c174027f4..5c4f1514b 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -18,6 +18,24 @@ class DatabaseManagerPage extends Component { actions.loadDBsAndRPsAsync(proxy) } + render() { + const {databases, actions} = this.props + + return ( + + ) + } + handleKeyDownDatabase(e, database) { const {key} = e const {actions} = this.props @@ -45,24 +63,6 @@ class DatabaseManagerPage extends Component { actions.updateDatabaseDeleteCode(database, value) } - - render() { - const {databases, retentionPolicies, actions} = this.props - - return ( - - ) - } } const { @@ -91,6 +91,7 @@ DatabaseManagerPage.propTypes = { isDefault: bool, }))), actions: shape({ + addRetentionPolicy: func, loadDBsAndRPsAsync: func, createDatabaseAsync: func, addDatabase: func, diff --git a/ui/src/admin/reducers/admin.js b/ui/src/admin/reducers/admin.js index a389fd53f..f1c92ee8c 100644 --- a/ui/src/admin/reducers/admin.js +++ b/ui/src/admin/reducers/admin.js @@ -17,11 +17,6 @@ const newDefaultRole = { isNew: true, } -const newDefaultDatabase = { - name: '', - isNew: true, -} - const newDefaultRP = { name: 'autogen', duration: '0', @@ -30,6 +25,19 @@ const newDefaultRP = { isNew: true, } +const newEmptyRP = { + name: '', + duration: '0', + replication: 0, + isNew: true, +} + +const newDefaultDatabase = { + name: '', + isNew: true, + retentionPolicies: [newDefaultRP], +} + const initialState = { users: null, roles: [], @@ -37,7 +45,6 @@ const initialState = { queries: [], queryIDToKill: null, databases: [], - retentionPolicies: [], } export default function admin(state = initialState, action) { @@ -58,10 +65,6 @@ export default function admin(state = initialState, action) { return {...state, ...action.payload} } - case 'LOAD_RETENTION_POLICIES': { - return {...state, ...action.payload} - } - case 'ADD_USER': { const newUser = {...newDefaultUser, isEditing: true} return { @@ -87,7 +90,6 @@ export default function admin(state = initialState, action) { case 'ADD_DATABASE': { const {id} = action.payload const newDatabase = {...newDefaultDatabase, id, isEditing: true} - const newRetentionPolicies = [{...newDefaultRP}] return { ...state, @@ -95,13 +97,20 @@ export default function admin(state = initialState, action) { newDatabase, ...state.databases, ], - retentionPolicies: [ - newRetentionPolicies, - ...state.retentionPolicies, - ], } } + case 'ADD_RETENTION_POLICY': { + const {database} = action.payload + const databases = state.databases.map(db => + db.id === database.id ? + {...database, retentionPolicies: [newEmptyRP, ...database.retentionPolicies]} + : db + ) + + return {...state, databases} + } + case 'SYNC_USER': { const {staleUser, syncedUser} = action.payload const newState = { From 7eebd623ea7cfac7ec3887df877c534d7ffed33a Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 15 Mar 2017 16:25:18 -0700 Subject: [PATCH 16/95] Remove cruft component --- ui/src/admin/components/DBManagementTable.js | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 ui/src/admin/components/DBManagementTable.js diff --git a/ui/src/admin/components/DBManagementTable.js b/ui/src/admin/components/DBManagementTable.js deleted file mode 100644 index 55b80bd07..000000000 --- a/ui/src/admin/components/DBManagementTable.js +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react' - -const DatabaseManager = ({databases, retentionPolicies}) => { - return ( -
HI
- ) -} - -export default DatabaseManager From 4a3f2e1981841cc1631afa63b9b69230e229fd91 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 17 Mar 2017 11:51:42 -0700 Subject: [PATCH 17/95] Introduce editing rp state --- ui/src/admin/actions/index.js | 19 +++-- ui/src/admin/components/DatabaseManager.js | 3 + ui/src/admin/components/DatabaseRow.js | 74 +++++++++++++++++-- ui/src/admin/components/DatabaseTable.js | 13 ++-- .../admin/containers/DatabaseManagerPage.js | 2 + ui/src/admin/reducers/admin.js | 22 +++++- 6 files changed, 110 insertions(+), 23 deletions(-) diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index 44dabac50..f69176a5f 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -53,13 +53,6 @@ export const loadDatabases = (databases) => ({ }, }) -export const loadRetentionPolicies = (retentionPolicies) => ({ - type: 'LOAD_RETENTION_POLICIES', - payload: { - retentionPolicies, - }, -}) - export const addUser = () => ({ type: 'ADD_USER', }) @@ -211,6 +204,14 @@ export const removeDatabaseDeleteCode = (database) => ({ }, }) +export const editRetentionPolicy = (database, retentionPolicy) => ({ + type: 'EDIT_RETENTION_POLICY', + payload: { + database, + retentionPolicy, + }, +}) + // async actions export const loadUsersAsync = (url) => async (dispatch) => { const {data} = await getUsersAJAX(url) @@ -234,7 +235,9 @@ export const loadDBsAndRPsAsync = (url) => async (dispatch) => { 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, id: uuid.v4(), retentionPolicies: rps[i]})) + const dbsAndRps = databases.map((name, i) => ( + {name, id: uuid.v4(), retentionPolicies: rps[i].map(rp => ({...rp, id: uuid.v4()}))} + )) dispatch(loadDatabases(dbsAndRps)) } diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index 582a40987..f8d069e50 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -11,6 +11,7 @@ const DatabaseManager = ({ onStartDeleteDatabase, onDatabaseDeleteConfirm, onAddRetentionPolicy, + onEditRetentionPolicy, }) => { return (
@@ -31,6 +32,7 @@ const DatabaseManager = ({ onStartDeleteDatabase={onStartDeleteDatabase} onDatabaseDeleteConfirm={onDatabaseDeleteConfirm} onAddRetentionPolicy={onAddRetentionPolicy} + onEditRetentionPolicy={onEditRetentionPolicy} /> ) } @@ -55,6 +57,7 @@ DatabaseManager.propTypes = { onStartDeleteDatabase: func, onDatabaseDeleteConfirm: func, onAddRetentionPolicy: func, + onEditRetentionPolicy: func, } export default DatabaseManager diff --git a/ui/src/admin/components/DatabaseRow.js b/ui/src/admin/components/DatabaseRow.js index 8521d5c76..461873918 100644 --- a/ui/src/admin/components/DatabaseRow.js +++ b/ui/src/admin/components/DatabaseRow.js @@ -1,7 +1,64 @@ import React, {PropTypes} from 'react' import {formatRPDuration} from 'utils/formatting' +import ConfirmButtons from 'src/admin/components/ConfirmButtons' + +export const DatabaseRow = ({ + retentionPolicy, + retentionPolicy: {name, duration, replication, isEditing, isDefault}, + database, + onEdit, +}) => { + if (isEditing) { + return ( +
+ + + + + + ) + } -export const DatabaseRow = ({name, duration, replication, isDefault}) => { return ( { - database.retentionPolicies.map(({name, duration, replication, isDefault}) => { + database.retentionPolicies.map(rp => { return ( ) }) @@ -69,6 +69,7 @@ DatabaseTable.propTypes = { onStartDeleteDatabase: func, onDatabaseDeleteConfirm: func, onAddRetentionPolicy: func, + onEditRetentionPolicy: func, } const DatabaseTableHeader = ({ diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index 5c4f1514b..3dbc1eb7b 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -32,6 +32,7 @@ class DatabaseManagerPage extends Component { onConfirmDatabase={actions.createDatabaseAsync} onStartDeleteDatabase={actions.startDeleteDatabase} onAddRetentionPolicy={actions.addRetentionPolicy} + onEditRetentionPolicy={actions.editRetentionPolicy} /> ) } @@ -99,6 +100,7 @@ DatabaseManagerPage.propTypes = { startDeleteDatabase: func, updateDatabaseDeleteCode: func, removeDatabaseDeleteCode: func, + editRetentionPolicy: func, }), } diff --git a/ui/src/admin/reducers/admin.js b/ui/src/admin/reducers/admin.js index f1c92ee8c..4d4ee6e27 100644 --- a/ui/src/admin/reducers/admin.js +++ b/ui/src/admin/reducers/admin.js @@ -27,9 +27,10 @@ const newDefaultRP = { const newEmptyRP = { name: '', - duration: '0', - replication: 0, + duration: '2w', + replication: 2, isNew: true, + isEditing: true, } const newDefaultDatabase = { @@ -101,10 +102,10 @@ export default function admin(state = initialState, action) { } case 'ADD_RETENTION_POLICY': { - const {database} = action.payload + const {database, id} = action.payload const databases = state.databases.map(db => db.id === database.id ? - {...database, retentionPolicies: [newEmptyRP, ...database.retentionPolicies]} + {...database, retentionPolicies: [{...newEmptyRP, id}, ...database.retentionPolicies]} : db ) @@ -160,6 +161,19 @@ export default function admin(state = initialState, action) { return {...state, ...newState} } + case 'EDIT_RETENTION_POLICY': { + const {database, retentionPolicy} = action.payload + + const newState = { + databases: state.databases.map(db => db.id === database.id ? { + ...db, + retentionPolicies: db.retentionPolicies.map(rp => rp.id === retentionPolicy.id ? {...rp, ...retentionPolicy} : rp), + } : db), + } + + return {...state, ...newState} + } + case 'DELETE_USER': { const {user} = action.payload const newState = { From ac918a4306bb50858e1297646f164a9ac32e107b Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 17 Mar 2017 13:43:27 -0700 Subject: [PATCH 18/95] Introduce remove rp from state --- ui/src/admin/actions/index.js | 8 +++++++ ui/src/admin/components/DatabaseManager.js | 6 ++++++ ui/src/admin/components/DatabaseRow.js | 7 +++++-- ui/src/admin/components/DatabaseTable.js | 6 ++++++ .../admin/containers/DatabaseManagerPage.js | 21 +++++++++++++++++++ ui/src/admin/reducers/admin.js | 13 ++++++++++++ 6 files changed, 59 insertions(+), 2 deletions(-) diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index f69176a5f..30bd254f1 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -168,6 +168,14 @@ export const removeDatabase = (database) => ({ }, }) +export const removeRetentionPolicy = (database, retentionPolicy) => ({ + type: 'REMOVE_RETENTION_POLICY', + payload: { + database, + retentionPolicy, + }, +}) + export const filterUsers = (text) => ({ type: 'FILTER_USERS', payload: { diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index f8d069e50..ab6f7f4da 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -12,6 +12,8 @@ const DatabaseManager = ({ onDatabaseDeleteConfirm, onAddRetentionPolicy, onEditRetentionPolicy, + onKeyDownRetentionPolicy, + onCancelRetentionPolicy, }) => { return (
@@ -33,6 +35,8 @@ const DatabaseManager = ({ onDatabaseDeleteConfirm={onDatabaseDeleteConfirm} onAddRetentionPolicy={onAddRetentionPolicy} onEditRetentionPolicy={onEditRetentionPolicy} + onKeyDownRetentionPolicy={onKeyDownRetentionPolicy} + onCancelRetentionPolicy={onCancelRetentionPolicy} /> ) } @@ -58,6 +62,8 @@ DatabaseManager.propTypes = { onDatabaseDeleteConfirm: func, onAddRetentionPolicy: func, onEditRetentionPolicy: func, + onKeyDownRetentionPolicy: func, + onCancelRetentionPolicy: func, } export default DatabaseManager diff --git a/ui/src/admin/components/DatabaseRow.js b/ui/src/admin/components/DatabaseRow.js index 461873918..1867c8d98 100644 --- a/ui/src/admin/components/DatabaseRow.js +++ b/ui/src/admin/components/DatabaseRow.js @@ -7,6 +7,8 @@ export const DatabaseRow = ({ retentionPolicy: {name, duration, replication, isEditing, isDefault}, database, onEdit, + onKeyDown, + onCancel, }) => { if (isEditing) { return ( @@ -20,7 +22,7 @@ export const DatabaseRow = ({ value={name} placeholder="retention policy name" onChange={(e) => onEdit(database, {...retentionPolicy, name: e.target.value})} - onKeyDown={() => {}} + onKeyDown={(e) => onKeyDown(e, database, retentionPolicy)} autoFocus={true} />
@@ -53,7 +55,7 @@ export const DatabaseRow = ({
) @@ -94,4 +96,5 @@ DatabaseRow.propTypes = { }), database: shape(), onEdit: func, + onKeyDown: func, } diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index 360391e50..95bca5149 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -17,6 +17,8 @@ const DatabaseTable = ({ onDatabaseDeleteConfirm, onAddRetentionPolicy, onEditRetentionPolicy, + onKeyDownRetentionPolicy, + onCancelRetentionPolicy, }) => { return (
@@ -49,6 +51,8 @@ const DatabaseTable = ({ database={database} retentionPolicy={rp} onEdit={onEditRetentionPolicy} + onKeyDown={onKeyDownRetentionPolicy} + onCancel={onCancelRetentionPolicy} /> ) }) @@ -70,6 +74,8 @@ DatabaseTable.propTypes = { onDatabaseDeleteConfirm: func, onAddRetentionPolicy: func, onEditRetentionPolicy: func, + onKeyDownRetentionPolicy: func, + onCancelRetentionPolicy: func, } const DatabaseTableHeader = ({ diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index 3dbc1eb7b..9871f446e 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -10,6 +10,8 @@ class DatabaseManagerPage extends Component { super(props) this.handleKeyDownDatabase = ::this.handleKeyDownDatabase this.handleDatabaseDeleteConfirm = ::this.handleDatabaseDeleteConfirm + this.handleKeyDownRetentionPolicy = ::this.handleKeyDownRetentionPolicy + this.handleCancelRetentionPolicy = ::this.handleCancelRetentionPolicy } componentDidMount() { @@ -26,6 +28,7 @@ class DatabaseManagerPage extends Component { databases={databases} onKeyDownDatabase={this.handleKeyDownDatabase} onDatabaseDeleteConfirm={this.handleDatabaseDeleteConfirm} + onKeyDownRetentionPolicy={this.handleKeyDownRetentionPolicy} addDatabase={actions.addDatabase} onEditDatabase={actions.editDatabase} onCancelDatabase={actions.removeDatabase} @@ -33,10 +36,28 @@ class DatabaseManagerPage extends Component { onStartDeleteDatabase={actions.startDeleteDatabase} onAddRetentionPolicy={actions.addRetentionPolicy} onEditRetentionPolicy={actions.editRetentionPolicy} + onCancelRetentionPolicy={this.handleCancelRetentionPolicy} /> ) } + handleCancelRetentionPolicy({database, retentionPolicy}) { + this.props.actions.removeRetentionPolicy(database, retentionPolicy) + } + + handleKeyDownRetentionPolicy(e, db, rp) { + const {key} = e + const {actions} = this.props + + if (key === 'Escape') { + return actions.removeRetentionPolicy(db, rp) + } + + if (key === 'Enter') { + // actions.createRetentionPolicyAsync(db, rp) + } + } + handleKeyDownDatabase(e, database) { const {key} = e const {actions} = this.props diff --git a/ui/src/admin/reducers/admin.js b/ui/src/admin/reducers/admin.js index 4d4ee6e27..effea41b0 100644 --- a/ui/src/admin/reducers/admin.js +++ b/ui/src/admin/reducers/admin.js @@ -201,6 +201,19 @@ export default function admin(state = initialState, action) { return {...state, ...newState} } + case 'REMOVE_RETENTION_POLICY': { + const {database, retentionPolicy} = action.payload + const newState = { + databases: state.databases.map(db => db.id === database.id ? { + ...db, + retentionPolicies: db.retentionPolicies.filter(rp => rp.id !== retentionPolicy.id), + } + : db), + } + + return {...state, ...newState} + } + case 'START_DELETE_DATABASE': { const {database} = action.payload const newState = { From 9e1c4c29ef31a8f4dd13709f6168dffb535d471f Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 17 Mar 2017 14:56:06 -0700 Subject: [PATCH 19/95] Add skeleton for retention policy creation --- ui/src/admin/actions/index.js | 15 +++++++++++++-- ui/src/admin/apis/index.js | 12 ++++++++++++ ui/src/admin/components/DatabaseRow.js | 9 +++++---- ui/src/admin/containers/DatabaseManagerPage.js | 7 +++++-- ui/src/admin/reducers/admin.js | 5 +++-- 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index 30bd254f1..85aa08fc0 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -6,8 +6,6 @@ import { getPermissions as getPermissionsAJAX, createUser as createUserAJAX, createRole as createRoleAJAX, - createDatabase as createDatabaseAJAX, - deleteDatabase as deleteDatabaseAJAX, deleteUser as deleteUserAJAX, deleteRole as deleteRoleAJAX, updateRole as updateRoleAJAX, @@ -287,6 +285,19 @@ export const createDatabaseAsync = (url, database) => async (dispatch) => { } } +export const createRetentionPolicyAsync = (url, retentionPolicy) => async (dispatch) => { + try { + // TODO: implement once server is up + // const {data} = await createRetentionPolicyAJAX(url, retentionPolicy) + dispatch(publishNotification('success', 'Retention policy created successfully')) + // dispatch(syncRetentionPolicy(retentionPolicy, {...data, id: uuid.v4()})) + } catch (error) { + // undo optimistic update + dispatch(publishNotification('error', `Failed to create retention policy: ${error.data.message}`)) + setTimeout(() => dispatch(removeRetentionPolicy(retentionPolicy)), ADMIN_NOTIFICATION_DELAY) + } +} + export const killQueryAsync = (source, queryID) => (dispatch) => { // optimistic update dispatch(killQuery(queryID)) diff --git a/ui/src/admin/apis/index.js b/ui/src/admin/apis/index.js index 01e0146bb..0d90536a0 100644 --- a/ui/src/admin/apis/index.js +++ b/ui/src/admin/apis/index.js @@ -72,6 +72,18 @@ export const createDatabase = async (url, database) => { } } +export const createRetentionPolicy = async (url, retentionPolicy) => { + try { + return await AJAX({ + method: 'POST', + url, + data: retentionPolicy, + }) + } catch (error) { + throw error + } +} + export const deleteRole = async (url, addFlashMessage, rolename) => { try { const response = await AJAX({ diff --git a/ui/src/admin/components/DatabaseRow.js b/ui/src/admin/components/DatabaseRow.js index 1867c8d98..d93ca7036 100644 --- a/ui/src/admin/components/DatabaseRow.js +++ b/ui/src/admin/components/DatabaseRow.js @@ -20,7 +20,7 @@ export const DatabaseRow = ({ name="name" type="text" value={name} - placeholder="retention policy name" + placeholder="give it a name" onChange={(e) => onEdit(database, {...retentionPolicy, name: e.target.value})} onKeyDown={(e) => onKeyDown(e, database, retentionPolicy)} autoFocus={true} @@ -34,7 +34,7 @@ export const DatabaseRow = ({ name="name" type="text" value={duration} - placeholder="duration" + placeholder="how long should data last" onChange={(e) => onEdit(database, {...retentionPolicy, duration: e.target.value})} onKeyDown={() => {}} /> @@ -47,8 +47,8 @@ export const DatabaseRow = ({ name="name" type="number" min="1" - value={replication} - placeholder="replication factor" + value={replication || ''} + placeholder="how many nodes do you have" onChange={(e) => onEdit(database, {...retentionPolicy, replication: +e.target.value})} onKeyDown={() => {}} /> @@ -97,4 +97,5 @@ DatabaseRow.propTypes = { database: shape(), onEdit: func, onKeyDown: func, + onCancel: func, } diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index 9871f446e..252a83c17 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -12,6 +12,7 @@ class DatabaseManagerPage extends Component { this.handleDatabaseDeleteConfirm = ::this.handleDatabaseDeleteConfirm this.handleKeyDownRetentionPolicy = ::this.handleKeyDownRetentionPolicy this.handleCancelRetentionPolicy = ::this.handleCancelRetentionPolicy + this.handleSaveRetentionPolicy = ::this.handleSaveRetentionPolicy } componentDidMount() { @@ -50,11 +51,12 @@ class DatabaseManagerPage extends Component { const {actions} = this.props if (key === 'Escape') { - return actions.removeRetentionPolicy(db, rp) + actions.removeRetentionPolicy(db, rp) } if (key === 'Enter') { - // actions.createRetentionPolicyAsync(db, rp) + // TODO: validate input + actions.createRetentionPolicyAsync(db, rp) } } @@ -67,6 +69,7 @@ class DatabaseManagerPage extends Component { } if (key === 'Enter') { + // TODO: validate input actions.createDatabaseAsync(database) } } diff --git a/ui/src/admin/reducers/admin.js b/ui/src/admin/reducers/admin.js index effea41b0..a8ca7b50a 100644 --- a/ui/src/admin/reducers/admin.js +++ b/ui/src/admin/reducers/admin.js @@ -18,6 +18,7 @@ const newDefaultRole = { } const newDefaultRP = { + id: '', name: 'autogen', duration: '0', replication: 2, @@ -27,8 +28,8 @@ const newDefaultRP = { const newEmptyRP = { name: '', - duration: '2w', - replication: 2, + duration: '', + replication: 0, isNew: true, isEditing: true, } From 5859beb6031a83aedb59d9551d5221abb5a378ea Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Mon, 20 Mar 2017 11:48:25 -0700 Subject: [PATCH 20/95] add links to database swagger object --- server/swagger.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/server/swagger.json b/server/swagger.json index ae2598680..42c397635 100644 --- a/server/swagger.json +++ b/server/swagger.json @@ -2238,7 +2238,10 @@ "name": "NOAA_water_database", "duration": "3d", "replication": 3, - "shardDuration": "3h" + "shardDuration": "3h", + "links": { + "self": "/chronograf/v1/sources/1/dbs/NOAA_water_database" + } }, "properties": { "name": { @@ -2257,6 +2260,16 @@ "shardDuration": { "type": "string", "description": "the interval spanned by each shard group" + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "string", + "description": "Self link mapping to this resource", + "format": "url" + } + } } } }, From 626b313d98609749a6991d7bb64290ad72ea318f Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Mon, 20 Mar 2017 11:52:49 -0700 Subject: [PATCH 21/95] add links to retentionpolicy swagger --- server/swagger.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/server/swagger.json b/server/swagger.json index 42c397635..725e56203 100644 --- a/server/swagger.json +++ b/server/swagger.json @@ -2537,7 +2537,10 @@ "duration": "7d", "replication": 1, "shardDuration": "7d", - "default": true + "default": true, + "links": { + "self": "/chronograf/v1/ousrces/1/dbs/NOAA_water_database/rps/liquid" + } }, "properties": { "name": { @@ -2560,6 +2563,16 @@ "default": { "type": "boolean", "description": "Indicates whether this retention policy should be the default" + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "string", + "description": "Self link mapping to this resource", + "format": "url" + } + } } } }, From 71a3fa47f76a9654e9123fc2b943d1a79e1737b3 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 20 Mar 2017 11:53:00 -0700 Subject: [PATCH 22/95] Handle confirm button creation of RP --- ui/src/admin/components/DatabaseManager.js | 3 +++ ui/src/admin/components/DatabaseRow.js | 8 +++++--- ui/src/admin/components/DatabaseTable.js | 3 +++ ui/src/admin/containers/DatabaseManagerPage.js | 8 +++++++- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index ab6f7f4da..b93d3da29 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -14,6 +14,7 @@ const DatabaseManager = ({ onEditRetentionPolicy, onKeyDownRetentionPolicy, onCancelRetentionPolicy, + onCreateRetentionPolicy, }) => { return (
@@ -37,6 +38,7 @@ const DatabaseManager = ({ onEditRetentionPolicy={onEditRetentionPolicy} onKeyDownRetentionPolicy={onKeyDownRetentionPolicy} onCancelRetentionPolicy={onCancelRetentionPolicy} + onCreateRetentionPolicy={onCreateRetentionPolicy} /> ) } @@ -64,6 +66,7 @@ DatabaseManager.propTypes = { onEditRetentionPolicy: func, onKeyDownRetentionPolicy: func, onCancelRetentionPolicy: func, + onCreateRetentionPolicy: func, } export default DatabaseManager diff --git a/ui/src/admin/components/DatabaseRow.js b/ui/src/admin/components/DatabaseRow.js index d93ca7036..50d044293 100644 --- a/ui/src/admin/components/DatabaseRow.js +++ b/ui/src/admin/components/DatabaseRow.js @@ -9,6 +9,7 @@ export const DatabaseRow = ({ onEdit, onKeyDown, onCancel, + onConfirm, }) => { if (isEditing) { return ( @@ -36,7 +37,7 @@ export const DatabaseRow = ({ value={duration} placeholder="how long should data last" onChange={(e) => onEdit(database, {...retentionPolicy, duration: e.target.value})} - onKeyDown={() => {}} + onKeyDown={(e) => onKeyDown(e, database, retentionPolicy)} />
@@ -50,12 +51,12 @@ export const DatabaseRow = ({ value={replication || ''} placeholder="how many nodes do you have" onChange={(e) => onEdit(database, {...retentionPolicy, replication: +e.target.value})} - onKeyDown={() => {}} + onKeyDown={(e) => onKeyDown(e, database, retentionPolicy)} />
) @@ -98,4 +99,5 @@ DatabaseRow.propTypes = { onEdit: func, onKeyDown: func, onCancel: func, + onConfirm: func, } diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index 95bca5149..ec23ecdb2 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -19,6 +19,7 @@ const DatabaseTable = ({ onEditRetentionPolicy, onKeyDownRetentionPolicy, onCancelRetentionPolicy, + onCreateRetentionPolicy, }) => { return (
@@ -53,6 +54,7 @@ const DatabaseTable = ({ onEdit={onEditRetentionPolicy} onKeyDown={onKeyDownRetentionPolicy} onCancel={onCancelRetentionPolicy} + onConfirm={onCreateRetentionPolicy} /> ) }) @@ -76,6 +78,7 @@ DatabaseTable.propTypes = { onEditRetentionPolicy: func, onKeyDownRetentionPolicy: func, onCancelRetentionPolicy: func, + onCreateRetentionPolicy: func, } const DatabaseTableHeader = ({ diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index 252a83c17..eee6e1c03 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -12,7 +12,7 @@ class DatabaseManagerPage extends Component { this.handleDatabaseDeleteConfirm = ::this.handleDatabaseDeleteConfirm this.handleKeyDownRetentionPolicy = ::this.handleKeyDownRetentionPolicy this.handleCancelRetentionPolicy = ::this.handleCancelRetentionPolicy - this.handleSaveRetentionPolicy = ::this.handleSaveRetentionPolicy + this.handleCreateRetentionPolicy = ::this.handleCreateRetentionPolicy } componentDidMount() { @@ -38,6 +38,7 @@ class DatabaseManagerPage extends Component { onAddRetentionPolicy={actions.addRetentionPolicy} onEditRetentionPolicy={actions.editRetentionPolicy} onCancelRetentionPolicy={this.handleCancelRetentionPolicy} + onCreateRetentionPolicy={this.handleCreateRetentionPolicy} /> ) } @@ -46,6 +47,10 @@ class DatabaseManagerPage extends Component { this.props.actions.removeRetentionPolicy(database, retentionPolicy) } + handleCreateRetentionPolicy({database, retentionPolicy}) { + this.props.actions.createRetentionPolicyAsync(database, retentionPolicy) + } + handleKeyDownRetentionPolicy(e, db, rp) { const {key} = e const {actions} = this.props @@ -119,6 +124,7 @@ DatabaseManagerPage.propTypes = { addRetentionPolicy: func, loadDBsAndRPsAsync: func, createDatabaseAsync: func, + createRetentionPolicyAsync: func, addDatabase: func, removeDatabase: func, startDeleteDatabase: func, From 4ac8d72902d7b3477f20fc714059039cf776b21c Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Mon, 20 Mar 2017 11:58:09 -0700 Subject: [PATCH 23/95] link to retention policies in db response --- server/swagger.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/server/swagger.json b/server/swagger.json index 725e56203..97e09c58f 100644 --- a/server/swagger.json +++ b/server/swagger.json @@ -2240,7 +2240,8 @@ "replication": 3, "shardDuration": "3h", "links": { - "self": "/chronograf/v1/sources/1/dbs/NOAA_water_database" + "self": "/chronograf/v1/sources/1/dbs/NOAA_water_database", + "rps": "/chronograf/v1/sources/1/dbs/NOAA_water_database/rps" } }, "properties": { @@ -2268,6 +2269,11 @@ "type": "string", "description": "Self link mapping to this resource", "format": "url" + }, + "rps": { + "type": "string", + "description": "Link to retention policies for this database", + "format": "url" } } } From b443d857ca71285efce1bffe35cf8789be97e9c6 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Mon, 20 Mar 2017 14:23:29 -0700 Subject: [PATCH 24/95] add db and rp routes to mux.go --- server/mux.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/server/mux.go b/server/mux.go index 1738ba255..e549ca410 100644 --- a/server/mux.go +++ b/server/mux.go @@ -131,6 +131,19 @@ func NewMux(opts MuxOpts, service Service) http.Handler { router.PUT("/chronograf/v1/dashboards/:id", service.ReplaceDashboard) router.PATCH("/chronograf/v1/dashboards/:id", service.UpdateDashboard) + // Databases + router.GET("/chronograf/v1/sources/:id/dbs", service.Databases) + router.POST("/chronograf/v1/sources/:id/dbs", service.NewDatabase) + + router.DELETE("/chronograf/v1/sources/:id/dbs/:did", service.DropDatabase) + + // Retention Policies + router.GET("/chronograf/v1/sources/:id/dbs/:did/rps", service.RetentionPolicies) + router.POST("/chronograf/v1/sources/:id/dbs/:did/rps", service.NewRetentionPolicy) + + router.PATCH("/chronograf/v1/sources/:id/dbs/:did/rps/:rpid", service.UpdateRetentionPolicy) + router.DELETE("/chronograf/v1/sources/:id/dbs/:did/rps/:rpid", service.DropRetentionPolicy) + var authRoutes AuthRoutes var out http.Handler From a64c3f7bcf4c7aa4dde75e61b4d1fb44a2450166 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 20 Mar 2017 14:26:03 -0700 Subject: [PATCH 25/95] Introduce cancel edit retention policy --- ui/src/admin/actions/index.js | 8 +++++++ ui/src/admin/components/DatabaseManager.js | 3 +++ ui/src/admin/components/DatabaseRow.js | 12 ++++++----- ui/src/admin/components/DatabaseTable.js | 3 +++ .../admin/containers/DatabaseManagerPage.js | 21 +++++++++++++++++-- ui/src/admin/reducers/admin.js | 16 +++++++++++++- 6 files changed, 55 insertions(+), 8 deletions(-) diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index 85aa08fc0..192458414 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -218,6 +218,14 @@ export const editRetentionPolicy = (database, retentionPolicy) => ({ }, }) +export const stopEditRetentionPolicy = (database, retentionPolicy) => ({ + type: 'STOP_EDIT_RETENTION_POLICY', + payload: { + database, + retentionPolicy, + }, +}) + // async actions export const loadUsersAsync = (url) => async (dispatch) => { const {data} = await getUsersAJAX(url) diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index b93d3da29..b2415a38a 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -12,6 +12,7 @@ const DatabaseManager = ({ onDatabaseDeleteConfirm, onAddRetentionPolicy, onEditRetentionPolicy, + onStopEditRetentionPolicy, onKeyDownRetentionPolicy, onCancelRetentionPolicy, onCreateRetentionPolicy, @@ -36,6 +37,7 @@ const DatabaseManager = ({ onDatabaseDeleteConfirm={onDatabaseDeleteConfirm} onAddRetentionPolicy={onAddRetentionPolicy} onEditRetentionPolicy={onEditRetentionPolicy} + onStopEditRetentionPolicy={onStopEditRetentionPolicy} onKeyDownRetentionPolicy={onKeyDownRetentionPolicy} onCancelRetentionPolicy={onCancelRetentionPolicy} onCreateRetentionPolicy={onCreateRetentionPolicy} @@ -64,6 +66,7 @@ DatabaseManager.propTypes = { onDatabaseDeleteConfirm: func, onAddRetentionPolicy: func, onEditRetentionPolicy: func, + onStopEditRetentionPolicy: func, onKeyDownRetentionPolicy: func, onCancelRetentionPolicy: func, onCreateRetentionPolicy: func, diff --git a/ui/src/admin/components/DatabaseRow.js b/ui/src/admin/components/DatabaseRow.js index 50d044293..5919a68e2 100644 --- a/ui/src/admin/components/DatabaseRow.js +++ b/ui/src/admin/components/DatabaseRow.js @@ -4,9 +4,10 @@ import ConfirmButtons from 'src/admin/components/ConfirmButtons' export const DatabaseRow = ({ retentionPolicy, - retentionPolicy: {name, duration, replication, isEditing, isDefault}, + retentionPolicy: {name, duration, replication, isEditing, isDefault, isNew}, database, onEdit, + onStopEdit, onKeyDown, onCancel, onConfirm, @@ -56,7 +57,7 @@ export const DatabaseRow = ({
) @@ -64,12 +65,12 @@ export const DatabaseRow = ({ return ( - - - + + + + + + + + ) + } + return ( - - - + + ) } - - return ( - - - - - - - ) } const { From b9054266f2a11ce0f299838e170891e57be633ca Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Mar 2017 12:55:48 -0700 Subject: [PATCH 31/95] Change DatabaseRow to stateful to handle editing --- ui/src/admin/actions/index.js | 20 ++-- ui/src/admin/components/DatabaseManager.js | 6 +- ui/src/admin/components/DatabaseRow.js | 108 ++++++++++++------ ui/src/admin/components/DatabaseTable.js | 17 +-- .../admin/containers/DatabaseManagerPage.js | 48 +------- ui/src/admin/reducers/admin.js | 17 +-- ui/src/shared/components/YesNoButtons.js | 23 ++++ 7 files changed, 121 insertions(+), 118 deletions(-) create mode 100644 ui/src/shared/components/YesNoButtons.js diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index 192458414..6377c7215 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -218,14 +218,6 @@ export const editRetentionPolicy = (database, retentionPolicy) => ({ }, }) -export const stopEditRetentionPolicy = (database, retentionPolicy) => ({ - type: 'STOP_EDIT_RETENTION_POLICY', - payload: { - database, - retentionPolicy, - }, -}) - // async actions export const loadUsersAsync = (url) => async (dispatch) => { const {data} = await getUsersAJAX(url) @@ -306,6 +298,18 @@ export const createRetentionPolicyAsync = (url, retentionPolicy) => async (dispa } } +export const updateRetentionPolicyAsync = (database, retentionPolicy) => async (dispatch) => { + try { + // TODO: implement once server is up + dispatch(editRetentionPolicy(database, retentionPolicy)) + // const {data} = await createRetentionPolicyAJAX(url, retentionPolicy) + dispatch(publishNotification('success', 'Retention policy updated successfully')) + // dispatch(syncRetentionPolicy(retentionPolicy, {...data, id: uuid.v4()})) + } catch (error) { + setTimeout(() => dispatch(removeRetentionPolicy(retentionPolicy)), ADMIN_NOTIFICATION_DELAY) + } +} + export const killQueryAsync = (source, queryID) => (dispatch) => { // optimistic update dispatch(killQuery(queryID)) diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index b2415a38a..983fe0130 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -13,9 +13,9 @@ const DatabaseManager = ({ onAddRetentionPolicy, onEditRetentionPolicy, onStopEditRetentionPolicy, - onKeyDownRetentionPolicy, onCancelRetentionPolicy, onCreateRetentionPolicy, + onUpdateRetentionPolicy, }) => { return (
@@ -38,9 +38,9 @@ const DatabaseManager = ({ onAddRetentionPolicy={onAddRetentionPolicy} onEditRetentionPolicy={onEditRetentionPolicy} onStopEditRetentionPolicy={onStopEditRetentionPolicy} - onKeyDownRetentionPolicy={onKeyDownRetentionPolicy} onCancelRetentionPolicy={onCancelRetentionPolicy} onCreateRetentionPolicy={onCreateRetentionPolicy} + onUpdateRetentionPolicy={onUpdateRetentionPolicy} /> ) } @@ -67,9 +67,9 @@ DatabaseManager.propTypes = { onAddRetentionPolicy: func, onEditRetentionPolicy: func, onStopEditRetentionPolicy: func, - onKeyDownRetentionPolicy: func, onCancelRetentionPolicy: func, onCreateRetentionPolicy: func, + onUpdateRetentionPolicy: func, } export default DatabaseManager diff --git a/ui/src/admin/components/DatabaseRow.js b/ui/src/admin/components/DatabaseRow.js index e2662d694..6611a70da 100644 --- a/ui/src/admin/components/DatabaseRow.js +++ b/ui/src/admin/components/DatabaseRow.js @@ -1,49 +1,86 @@ import React, {PropTypes, Component} from 'react' import {formatRPDuration} from 'utils/formatting' -import ConfirmButtons from 'src/admin/components/ConfirmButtons' +import YesNoButtons from 'src/shared/components/YesNoButtons' import onClickOutside from 'react-onclickoutside' class DatabaseRow extends Component { constructor(props) { super(props) + this.state = { + isEditing: false, + } this.handleKeyDown = ::this.handleKeyDown + this.handleClickOutside = ::this.handleClickOutside + this.handleStartEdit = ::this.handleStartEdit + this.handleEndEdit = ::this.handleEndEdit + this.handleCreate = ::this.handleCreate + this.handleUpdate = ::this.handleUpdate + this._getInputValues = ::this._getInputValues } - handleKeyDown(e, db) { + handleClickOutside() { + this.handleEndEdit() + } + + handleStartEdit() { + this.setState({isEditing: true}) + } + + handleEndEdit() { + this.setState({isEditing: false}) + } + + handleCreate() { + const {database, onCreate} = this.props + onCreate(database, this._getInputValues()) + this.handleEndEdit() + } + + handleUpdate() { + const {database, retentionPolicy, onUpdate} = this.props + onUpdate(database, {...retentionPolicy, ...this._getInputValues()}) + this.handleEndEdit() + } + + handleKeyDown(e) { const {key} = e + const {retentionPolicy, database, onCancel} = this.props - if (rp.isNew) { - if (key === 'Escape') { - // return actions.removeRetentionPolicy(db, rp) - } - - if (key === 'Enter') { - // return actions.createRetentionPolicyAsync(db, rp) - } - } if (key === 'Escape') { - // actions.stopEditRetentionPolicy(db, rp) + if (retentionPolicy.isNew) { + onCancel(database, retentionPolicy) + return + } + + this.handleEndEdit() } if (key === 'Enter') { - // actions.updateRetentionPolicy(db, rp) + if (retentionPolicy.isNew) { + this.handleCreate() + return + } + + this.handleUpdate() + } + } + + _getInputValues() { + return { + name: this.name.value.trim(), + duration: this.duration.value.trim(), + replication: +this.replication.value.trim(), } } render() { const { - retentionPolicy, - retentionPolicy: {name, duration, replication, isEditing, isDefault, isNew}, + retentionPolicy: {name, duration, replication, isDefault, isNew}, database, - onEdit, - onStopEdit, - onKeyDown, - onCancel, - onConfirm, } = this.props - if (isEditing) { + if (this.state.isEditing) { return (
@@ -65,9 +103,10 @@ class DatabaseRow extends Component { className="form-control" name="name" type="text" - value={duration} + defaultValue={duration} placeholder="how long should data last" - onKeyDown={(e) => onKeyDown(e, database)} + onKeyDown={(e) => this.handleKeyDown(e, database)} + ref={(r) => this.duration = r} /> @@ -78,14 +117,18 @@ class DatabaseRow extends Component { name="name" type="number" min="1" - value={replication || 1} + defaultValue={replication || 1} placeholder="how many nodes do you have" - onKeyDown={(e) => onKeyDown(e, database)} + onKeyDown={(e) => this.handleKeyDown(e, database)} + ref={(r) => this.replication = r} /> ) @@ -93,12 +136,12 @@ class DatabaseRow extends Component { return ( - - - + + + @@ -131,12 +139,12 @@ class DatabaseRow extends Component { handleKeyDown(e) { const {key} = e - const {retentionPolicy, database, onCancel} = this.props + const {retentionPolicy, database, onRemove} = this.props if (key === 'Escape') { if (retentionPolicy.isNew) { - onCancel(database, retentionPolicy) + onRemove(database, retentionPolicy) return } @@ -189,7 +197,7 @@ DatabaseRow.propTypes = { isEditing: bool, }), database: shape(), - onCancel: func, + onRemove: func, onCreate: func, onUpdate: func, notify: func, diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index afe677987..3610f15d7 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -19,6 +19,7 @@ const DatabaseTable = ({ onAddRetentionPolicy, onCreateRetentionPolicy, onUpdateRetentionPolicy, + onRemoveRetentionPolicy, }) => { return (
@@ -53,6 +54,7 @@ const DatabaseTable = ({ retentionPolicy={rp} onCreate={onCreateRetentionPolicy} onUpdate={onUpdateRetentionPolicy} + onRemove={onRemoveRetentionPolicy} /> ) }) @@ -77,6 +79,7 @@ DatabaseTable.propTypes = { onCancelRetentionPolicy: func, onCreateRetentionPolicy: func, onUpdateRetentionPolicy: func, + onRemoveRetentionPolicy: func, } const DatabaseTableHeader = ({ diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index cac0d0444..4fc9d10fe 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -35,9 +35,9 @@ class DatabaseManagerPage extends Component { onStartDeleteDatabase={actions.startDeleteDatabase} onAddRetentionPolicy={actions.addRetentionPolicy} onEditRetentionPolicy={actions.editRetentionPolicy} - onCancelRetentionPolicy={actions.removeRetentionPolicy} onCreateRetentionPolicy={actions.createRetentionPolicyAsync} onUpdateRetentionPolicy={actions.updateRetentionPolicyAsync} + onRemoveRetentionPolicy={actions.removeRetentionPolicy} /> ) } @@ -108,6 +108,7 @@ DatabaseManagerPage.propTypes = { updateDatabaseDeleteCode: func, removeDatabaseDeleteCode: func, editRetentionPolicy: func, + removeRetentionPolicy: func, }), notify: func, } From 8e1e70ecf1ffc9239190bc345123f250aa48194d Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Mar 2017 14:35:01 -0700 Subject: [PATCH 36/95] Remove default flag from default rp --- ui/src/admin/reducers/admin.js | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/src/admin/reducers/admin.js b/ui/src/admin/reducers/admin.js index 06bc7e4a2..6201d6e5a 100644 --- a/ui/src/admin/reducers/admin.js +++ b/ui/src/admin/reducers/admin.js @@ -23,7 +23,6 @@ const newDefaultRP = { duration: '0', replication: 2, isDefault: true, - isNew: true, } const newEmptyRP = { From d23ec6423fdd3ca81b07cba8c0d5cf3dc03a9de9 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Mar 2017 14:49:08 -0700 Subject: [PATCH 37/95] Use links instead of ids --- ui/src/admin/actions/index.js | 14 ++++----- ui/src/admin/components/DatabaseManager.js | 2 +- ui/src/admin/components/DatabaseTable.js | 2 +- ui/src/admin/reducers/admin.js | 33 +++++++++++----------- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index 6377c7215..0584ae5c1 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -61,15 +61,11 @@ export const addRole = () => ({ export const addDatabase = () => ({ type: 'ADD_DATABASE', - payload: { - id: uuid.v4(), - }, }) export const addRetentionPolicy = (database) => ({ type: 'ADD_RETENTION_POLICY', payload: { - id: uuid.v4(), database, }, }) @@ -241,9 +237,11 @@ export const loadDBsAndRPsAsync = (url) => async (dispatch) => { 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, id: uuid.v4(), retentionPolicies: rps[i].map(rp => ({...rp, id: uuid.v4()}))} - )) + const dbsAndRps = databases.map((name, i) => ({ + name, + links: {self: uuid.v4()}, + retentionPolicies: rps[i].map(rp => ({...rp, links: {self: uuid.v4()}})), + })) dispatch(loadDatabases(dbsAndRps)) } @@ -306,7 +304,7 @@ export const updateRetentionPolicyAsync = (database, retentionPolicy) => async ( dispatch(publishNotification('success', 'Retention policy updated successfully')) // dispatch(syncRetentionPolicy(retentionPolicy, {...data, id: uuid.v4()})) } catch (error) { - setTimeout(() => dispatch(removeRetentionPolicy(retentionPolicy)), ADMIN_NOTIFICATION_DELAY) + dispatch(publishNotification('error', `Failed to update retention policy: ${error.data.message}`)) } } diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index 34e10f6c9..22769291d 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -29,7 +29,7 @@ const DatabaseManager = ({ { databases.map(db => { return ( - db.id === database.id ? - {...database, retentionPolicies: [{...newEmptyRP, id}, ...database.retentionPolicies]} + db.links.self === database.links.self ? + {...database, retentionPolicies: [{...newEmptyRP}, ...database.retentionPolicies]} : db ) @@ -130,7 +131,7 @@ export default function admin(state = initialState, action) { case 'SYNC_DATABASE': { const {stale, synced} = action.payload const newState = { - databases: state.databases.map(db => db.id === stale.id ? {...synced} : db), + databases: state.databases.map(db => db.links.self === stale.links.self ? {...synced} : db), } return {...state, ...newState} } @@ -154,7 +155,7 @@ export default function admin(state = initialState, action) { case 'EDIT_DATABASE': { const {database, name} = action.payload const newState = { - databases: state.databases.map(db => db.id === database.id ? {...db, name} : db), + databases: state.databases.map(db => db.links.self === database.links.self ? {...db, name} : db), } return {...state, ...newState} @@ -164,9 +165,9 @@ export default function admin(state = initialState, action) { const {database, retentionPolicy} = action.payload const newState = { - databases: state.databases.map(db => db.id === database.id ? { + databases: state.databases.map(db => db.links.self === database.links.self ? { ...db, - retentionPolicies: db.retentionPolicies.map(rp => rp.id === retentionPolicy.id ? {...rp, ...retentionPolicy} : rp), + retentionPolicies: db.retentionPolicies.map(rp => rp.links.self === retentionPolicy.links.self ? {...rp, ...retentionPolicy} : rp), } : db), } @@ -194,7 +195,7 @@ export default function admin(state = initialState, action) { case 'REMOVE_DATABASE': { const {database} = action.payload const newState = { - databases: state.databases.filter(db => db.id !== database.id), + databases: state.databases.filter(db => db.links.self !== database.links.self), } return {...state, ...newState} @@ -203,9 +204,9 @@ export default function admin(state = initialState, action) { case 'REMOVE_RETENTION_POLICY': { const {database, retentionPolicy} = action.payload const newState = { - databases: state.databases.map(db => db.id === database.id ? { + databases: state.databases.map(db => db.links.self === database.links.self ? { ...db, - retentionPolicies: db.retentionPolicies.filter(rp => rp.id !== retentionPolicy.id), + retentionPolicies: db.retentionPolicies.filter(rp => rp.links.self !== retentionPolicy.links.self), } : db), } @@ -216,7 +217,7 @@ export default function admin(state = initialState, action) { case 'START_DELETE_DATABASE': { const {database} = action.payload const newState = { - databases: state.databases.map(db => db.id === database.id ? {...db, deleteCode: ''} : db), + databases: state.databases.map(db => db.links.self === database.links.self ? {...db, deleteCode: ''} : db), } return {...state, ...newState} @@ -225,7 +226,7 @@ export default function admin(state = initialState, action) { case 'UPDATE_DATABASE_DELETE_CODE': { const {database, deleteCode} = action.payload const newState = { - databases: state.databases.map(db => db.id === database.id ? {...db, deleteCode} : db), + databases: state.databases.map(db => db.links.self === database.links.self ? {...db, deleteCode} : db), } return {...state, ...newState} @@ -236,7 +237,7 @@ export default function admin(state = initialState, action) { delete database.deleteCode const newState = { - databases: state.databases.map(db => db.id === database.id ? {...database} : db), + databases: state.databases.map(db => db.links.self === database.links.self ? {...database} : db), } return {...state, ...newState} From 2984c139a235a206c25b5c959edca77d2a10fe97 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Mar 2017 15:32:41 -0700 Subject: [PATCH 38/95] =?UTF-8?q?Fix=20test=20to=20use=20=E2=88=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/spec/utils/formattingSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/spec/utils/formattingSpec.js b/ui/spec/utils/formattingSpec.js index 2f0a8040f..97f8d0ba5 100644 --- a/ui/spec/utils/formattingSpec.js +++ b/ui/spec/utils/formattingSpec.js @@ -25,7 +25,7 @@ describe('Formatting helpers', () => { it("returns 'infinite' for a retention policy with a value of '0'", () => { const actual = formatRPDuration('0') - expect(actual).to.equal('infinite'); + expect(actual).to.equal('∞'); }); it('correctly formats retention policy durations', () => { From 2d9c26a463f774705fd75fbe84b165cfb9635667 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Mar 2017 15:33:30 -0700 Subject: [PATCH 39/95] Pull out default objects into constants dir --- ui/spec/admin/reducers/adminSpec.js | 19 +++++----- ui/src/admin/constants/index.js | 41 ++++++++++++++++++++++ ui/src/admin/reducers/admin.js | 54 ++++++----------------------- 3 files changed, 60 insertions(+), 54 deletions(-) diff --git a/ui/spec/admin/reducers/adminSpec.js b/ui/spec/admin/reducers/adminSpec.js index 9c293b3dd..3879fde8f 100644 --- a/ui/spec/admin/reducers/adminSpec.js +++ b/ui/spec/admin/reducers/adminSpec.js @@ -15,6 +15,13 @@ import { filterUsers, } from 'src/admin/actions' +import { + NEW_DEFAULT_USER, + NEW_DEFAULT_ROLE, + NEW_DEFAULT_DATABASE, + NEW_EMPTY_RP, +} from 'src/admin/constants' + let state = undefined // Users @@ -58,14 +65,6 @@ const u2 = { links: {self: '/chronograf/v1/sources/1/users/zerocool'}, } const users = [u1, u2] -const newDefaultUser = { - name: '', - password: '', - roles: [], - permissions: [], - links: {self: ''}, - isNew: true, -} // Roles const r1 = { @@ -127,7 +126,7 @@ describe('Admin.Reducers', () => { const actual = reducer(state, addUser()) const expected = { users: [ - {...newDefaultUser, isEditing: true}, + {...NEW_DEFAULT_USER, isEditing: true}, u1, ], } @@ -171,7 +170,7 @@ describe('Admin.Reducers', () => { const actual = reducer(state, addRole()) const expected = { roles: [ - {...newDefaultRole, isEditing: true}, + {...NEW_DEFAULT_ROLE, isEditing: true}, r1, ], } diff --git a/ui/src/admin/constants/index.js b/ui/src/admin/constants/index.js index 6ed9a315d..db3b70554 100644 --- a/ui/src/admin/constants/index.js +++ b/ui/src/admin/constants/index.js @@ -9,3 +9,44 @@ export const TIMES = [ ]; export const ADMIN_NOTIFICATION_DELAY = 1500 // milliseconds + +export const NEW_DEFAULT_USER = { + name: '', + password: '', + roles: [], + permissions: [], + links: {self: ''}, + isNew: true, +} + +export const NEW_DEFAULT_ROLE = { + name: '', + permissions: [], + users: [], + links: {self: ''}, + isNew: true, +} + +export const NEW_DEFAULT_RP = { + name: 'autogen', + duration: '0', + replication: 2, + isDefault: true, + links: {self: ''}, +} + +export const NEW_EMPTY_RP = { + name: '', + duration: '', + replication: 0, + links: {self: ''}, + isNew: true, +} + +export const NEW_DEFAULT_DATABASE = { + name: '', + isNew: true, + retentionPolicies: [NEW_DEFAULT_RP], + links: {self: ''}, +} + diff --git a/ui/src/admin/reducers/admin.js b/ui/src/admin/reducers/admin.js index 001c53f09..f6f1df3b1 100644 --- a/ui/src/admin/reducers/admin.js +++ b/ui/src/admin/reducers/admin.js @@ -1,44 +1,10 @@ import reject from 'lodash/reject' - -const newDefaultUser = { - name: '', - password: '', - roles: [], - permissions: [], - links: {self: ''}, - isNew: true, -} - -const newDefaultRole = { - name: '', - permissions: [], - users: [], - links: {self: ''}, - isNew: true, -} - -const newDefaultRP = { - name: 'autogen', - duration: '0', - replication: 2, - isDefault: true, - links: {self: ''}, -} - -const newEmptyRP = { - name: '', - duration: '', - replication: 0, - links: {self: ''}, - isNew: true, -} - -const newDefaultDatabase = { - name: '', - isNew: true, - retentionPolicies: [newDefaultRP], - links: {self: ''}, -} +import { + NEW_DEFAULT_USER, + NEW_DEFAULT_ROLE, + NEW_DEFAULT_DATABASE, + NEW_EMPTY_RP, +} from 'src/admin/constants' const initialState = { users: null, @@ -68,7 +34,7 @@ export default function admin(state = initialState, action) { } case 'ADD_USER': { - const newUser = {...newDefaultUser, isEditing: true} + const newUser = {...NEW_DEFAULT_USER, isEditing: true} return { ...state, users: [ @@ -79,7 +45,7 @@ export default function admin(state = initialState, action) { } case 'ADD_ROLE': { - const newRole = {...newDefaultRole, isEditing: true} + const newRole = {...NEW_DEFAULT_ROLE, isEditing: true} return { ...state, roles: [ @@ -90,7 +56,7 @@ export default function admin(state = initialState, action) { } case 'ADD_DATABASE': { - const newDatabase = {...newDefaultDatabase, isEditing: true} + const newDatabase = {...NEW_DEFAULT_DATABASE, isEditing: true} return { ...state, @@ -105,7 +71,7 @@ export default function admin(state = initialState, action) { const {database} = action.payload const databases = state.databases.map(db => db.links.self === database.links.self ? - {...database, retentionPolicies: [{...newEmptyRP}, ...database.retentionPolicies]} + {...database, retentionPolicies: [{...NEW_EMPTY_RP}, ...database.retentionPolicies]} : db ) From 527e56bfa057c033ba1fbd2e1d87b890d8d208ba Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Mar 2017 15:34:13 -0700 Subject: [PATCH 40/95] Introduce add database test --- ui/spec/admin/reducers/adminSpec.js | 41 ++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/ui/spec/admin/reducers/adminSpec.js b/ui/spec/admin/reducers/adminSpec.js index 3879fde8f..9c7bd38e4 100644 --- a/ui/spec/admin/reducers/adminSpec.js +++ b/ui/spec/admin/reducers/adminSpec.js @@ -3,6 +3,7 @@ import reducer from 'src/admin/reducers/admin' import { addUser, addRole, + addDatabase, syncUser, syncRole, editUser, @@ -102,20 +103,46 @@ const r2 = { links: {self: '/chronograf/v1/sources/1/roles/l33tus3r'} } const roles = [r1, r2] -const newDefaultRole = { - name: '', - users: [], - permissions: [], - links: {self: ''}, - isNew: true, -} // Permissions const global = {scope: 'all', allowed: ['p1', 'p2']} const scoped = {scope: 'db1', allowed: ['p1', 'p3']} const permissions = [global, scoped] +// Databases && Retention Policies +const db1 = { + name: 'db1', + links: {self: '/chronograf/v1/sources/1/db/db1'}, + retentionPolicies: [], +} + +const db2 = { + name: 'db2', + links: {self: '/chronograf/v1/sources/1/db/db2'}, + retentionPolicies: [], +} + describe('Admin.Reducers', () => { + describe('Databases', () => { + it('can add a database', () => { + state = { + databases: [ + db1, + ] + } + + const actual = reducer(state, addDatabase()) + const expected = { + databases: [ + {...NEW_DEFAULT_DATABASE, isEditing: true}, + db1, + ], + } + + expect(actual.databases).to.deep.equal(expected.databases) + }) + }) + it('it can add a user', () => { state = { users: [ From 32a5fc6de7b88b22c0d2c912478bd0da72de5829 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Mar 2017 15:47:51 -0700 Subject: [PATCH 41/95] Add edit database test --- ui/spec/admin/reducers/adminSpec.js | 35 ++++++++++++++---------- ui/src/admin/actions/index.js | 2 +- ui/src/admin/components/DatabaseTable.js | 2 +- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/ui/spec/admin/reducers/adminSpec.js b/ui/spec/admin/reducers/adminSpec.js index 9c7bd38e4..8c6342d6f 100644 --- a/ui/spec/admin/reducers/adminSpec.js +++ b/ui/spec/admin/reducers/adminSpec.js @@ -8,6 +8,7 @@ import { syncRole, editUser, editRole, + editDatabase, loadRoles, loadPermissions, deleteRole, @@ -123,23 +124,29 @@ const db2 = { } describe('Admin.Reducers', () => { - describe('Databases', () => { + describe('Databases and Retention Policies', () => { + const state = { + databases: [ + db1, + ] + } + it('can add a database', () => { - state = { - databases: [ - db1, - ] - } - const actual = reducer(state, addDatabase()) - const expected = { - databases: [ - {...NEW_DEFAULT_DATABASE, isEditing: true}, - db1, - ], - } + const expected = [ + {...NEW_DEFAULT_DATABASE, isEditing: true}, + db1, + ] - expect(actual.databases).to.deep.equal(expected.databases) + expect(actual.databases).to.deep.equal(expected) + }) + + it('can edit a database', () => { + const name = 'dbOne' + const actual = reducer(state, editDatabase(db1, name)) + const expected = [{...db1, name}] + + expect(actual.databases).to.deep.equal(expected) }) }) diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index 0584ae5c1..f78deea08 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -110,7 +110,7 @@ export const editRole = (role, updates) => ({ }, }) -export const editDatabase = (name, database) => ({ +export const editDatabase = (database, name) => ({ type: 'EDIT_DATABASE', payload: { name, diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index ffc66b7f5..5a6554ef5 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -191,7 +191,7 @@ const EditHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel}) => ( type="text" value={database.name} placeholder="database name" - onChange={(e) => onEdit(e.target.value, database)} + onChange={(e) => onEdit(database, e.target.value)} onKeyDown={(e) => onKeyDown(e, database)} autoFocus={true} /> From a06e933467ccdb4eeda3ad5407a080e05c70b2b2 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Mar 2017 15:51:14 -0700 Subject: [PATCH 42/95] Introduce remove database test --- ui/spec/admin/reducers/adminSpec.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ui/spec/admin/reducers/adminSpec.js b/ui/spec/admin/reducers/adminSpec.js index 8c6342d6f..9b4acf8d5 100644 --- a/ui/spec/admin/reducers/adminSpec.js +++ b/ui/spec/admin/reducers/adminSpec.js @@ -13,6 +13,7 @@ import { loadPermissions, deleteRole, deleteUser, + removeDatabase, filterRoles, filterUsers, } from 'src/admin/actions' @@ -148,6 +149,13 @@ describe('Admin.Reducers', () => { expect(actual.databases).to.deep.equal(expected) }) + + it('can remove a database', () => { + const actual = reducer(state, removeDatabase(db1)) + const expected = [] + + expect(actual.databases).to.deep.equal(expected) + }) }) it('it can add a user', () => { From 38b381de8764107afb3e2fbfeb5e0759c33ae5d9 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Mar 2017 15:56:41 -0700 Subject: [PATCH 43/95] Add database delete code test --- ui/spec/admin/reducers/adminSpec.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ui/spec/admin/reducers/adminSpec.js b/ui/spec/admin/reducers/adminSpec.js index 9b4acf8d5..da2f8af34 100644 --- a/ui/spec/admin/reducers/adminSpec.js +++ b/ui/spec/admin/reducers/adminSpec.js @@ -16,6 +16,7 @@ import { removeDatabase, filterRoles, filterUsers, + startDeleteDatabase, } from 'src/admin/actions' import { @@ -154,6 +155,16 @@ describe('Admin.Reducers', () => { const actual = reducer(state, removeDatabase(db1)) const expected = [] + expect(actual.databases).to.deep.equal(expected) + }) + + it('can start delete database by adding a delete code', () => { + const actual = reducer(state, startDeleteDatabase(db1)) + const expected = [ + {...db1, deleteCode: ''} + ] + + expect(actual.databases).to.deep.equal(expected) }) }) From 4990aa39a4ad681023c198b3dcbf1e25ce10f92f Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Mar 2017 16:11:42 -0700 Subject: [PATCH 44/95] Remove redundant action --- ui/spec/admin/reducers/adminSpec.js | 6 +++--- ui/src/admin/actions/index.js | 12 ++---------- ui/src/admin/components/DatabaseTable.js | 2 +- ui/src/admin/containers/DatabaseManagerPage.js | 3 +-- ui/src/admin/reducers/admin.js | 13 ++----------- 5 files changed, 9 insertions(+), 27 deletions(-) diff --git a/ui/spec/admin/reducers/adminSpec.js b/ui/spec/admin/reducers/adminSpec.js index da2f8af34..d2608e2ab 100644 --- a/ui/spec/admin/reducers/adminSpec.js +++ b/ui/spec/admin/reducers/adminSpec.js @@ -144,9 +144,9 @@ describe('Admin.Reducers', () => { }) it('can edit a database', () => { - const name = 'dbOne' - const actual = reducer(state, editDatabase(db1, name)) - const expected = [{...db1, name}] + const updates = {name: 'dbOne'} + const actual = reducer(state, editDatabase(db1, updates)) + const expected = [{...db1, ...updates}] expect(actual.databases).to.deep.equal(expected) }) diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index f78deea08..4a923d737 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -110,11 +110,11 @@ export const editRole = (role, updates) => ({ }, }) -export const editDatabase = (database, name) => ({ +export const editDatabase = (database, updates) => ({ type: 'EDIT_DATABASE', payload: { - name, database, + updates, }, }) @@ -191,14 +191,6 @@ export const startDeleteDatabase = (database) => ({ }, }) -export const updateDatabaseDeleteCode = (database, deleteCode) => ({ - type: 'UPDATE_DATABASE_DELETE_CODE', - payload: { - database, - deleteCode, - }, -}) - export const removeDatabaseDeleteCode = (database) => ({ type: 'REMOVE_DATABASE_DELETE_CODE', payload: { diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index 5a6554ef5..759486e62 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -191,7 +191,7 @@ const EditHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel}) => ( type="text" value={database.name} placeholder="database name" - onChange={(e) => onEdit(database, e.target.value)} + onChange={(e) => onEdit(database, {name: e.target.value})} onKeyDown={(e) => onKeyDown(e, database)} autoFocus={true} /> diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index 4fc9d10fe..13e95de74 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -68,7 +68,7 @@ class DatabaseManagerPage extends Component { return actions.deleteDatabaseAsync(source, database) } - actions.updateDatabaseDeleteCode(database, value) + actions.editDatabase(database, {deleteCode: value}) } } @@ -105,7 +105,6 @@ DatabaseManagerPage.propTypes = { addDatabase: func, removeDatabase: func, startDeleteDatabase: func, - updateDatabaseDeleteCode: func, removeDatabaseDeleteCode: func, editRetentionPolicy: func, removeRetentionPolicy: func, diff --git a/ui/src/admin/reducers/admin.js b/ui/src/admin/reducers/admin.js index f6f1df3b1..1fe5fff5c 100644 --- a/ui/src/admin/reducers/admin.js +++ b/ui/src/admin/reducers/admin.js @@ -119,9 +119,9 @@ export default function admin(state = initialState, action) { } case 'EDIT_DATABASE': { - const {database, name} = action.payload + const {database, updates} = action.payload const newState = { - databases: state.databases.map(db => db.links.self === database.links.self ? {...db, name} : db), + databases: state.databases.map(db => db.links.self === database.links.self ? {...db, ...updates} : db), } return {...state, ...newState} @@ -189,15 +189,6 @@ export default function admin(state = initialState, action) { return {...state, ...newState} } - case 'UPDATE_DATABASE_DELETE_CODE': { - const {database, deleteCode} = action.payload - const newState = { - databases: state.databases.map(db => db.links.self === database.links.self ? {...db, deleteCode} : db), - } - - return {...state, ...newState} - } - case 'REMOVE_DATABASE_DELETE_CODE': { const {database} = action.payload delete database.deleteCode From e56f18154bfa59d9a056c2a073e21292a83836a6 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Mar 2017 16:27:30 -0700 Subject: [PATCH 45/95] Add remove database code test --- ui/spec/admin/reducers/adminSpec.js | 33 ++++++++++++------- ui/src/admin/actions/index.js | 4 +-- .../admin/containers/DatabaseManagerPage.js | 2 +- ui/src/admin/reducers/admin.js | 2 +- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/ui/spec/admin/reducers/adminSpec.js b/ui/spec/admin/reducers/adminSpec.js index d2608e2ab..963833940 100644 --- a/ui/spec/admin/reducers/adminSpec.js +++ b/ui/spec/admin/reducers/adminSpec.js @@ -16,7 +16,8 @@ import { removeDatabase, filterRoles, filterUsers, - startDeleteDatabase, + addDatabaseDeleteCode, + removeDatabaseDeleteCode, } from 'src/admin/actions' import { @@ -123,21 +124,19 @@ const db2 = { name: 'db2', links: {self: '/chronograf/v1/sources/1/db/db2'}, retentionPolicies: [], + deleteCode: 'DELETE', } describe('Admin.Reducers', () => { describe('Databases and Retention Policies', () => { - const state = { - databases: [ - db1, - ] - } + const state = {databases: [db1, db2]} it('can add a database', () => { const actual = reducer(state, addDatabase()) const expected = [ {...NEW_DEFAULT_DATABASE, isEditing: true}, db1, + db2, ] expect(actual.databases).to.deep.equal(expected) @@ -146,27 +145,39 @@ describe('Admin.Reducers', () => { it('can edit a database', () => { const updates = {name: 'dbOne'} const actual = reducer(state, editDatabase(db1, updates)) - const expected = [{...db1, ...updates}] + const expected = [{...db1, ...updates}, db2] expect(actual.databases).to.deep.equal(expected) }) it('can remove a database', () => { const actual = reducer(state, removeDatabase(db1)) - const expected = [] + const expected = [db2] expect(actual.databases).to.deep.equal(expected) }) - it('can start delete database by adding a delete code', () => { - const actual = reducer(state, startDeleteDatabase(db1)) + it('can add a database delete code', () => { + const actual = reducer(state, addDatabaseDeleteCode(db1)) const expected = [ - {...db1, deleteCode: ''} + {...db1, deleteCode: ''}, + db2, ] + expect(actual.databases).to.deep.equal(expected) + }) + + it('can remove the delete code', () => { + const actual = reducer(state, removeDatabaseDeleteCode(db2)) + delete db2.deleteCode + const expected = [ + db1, + db2, + ] expect(actual.databases).to.deep.equal(expected) }) + }) it('it can add a user', () => { diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index 4a923d737..873925ecc 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -184,8 +184,8 @@ export const filterRoles = (text) => ({ }, }) -export const startDeleteDatabase = (database) => ({ - type: 'START_DELETE_DATABASE', +export const addDatabaseDeleteCode = (database) => ({ + type: 'ADD_DATABASE_DELETE_CODE', payload: { database, }, diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index 13e95de74..02680ed1c 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -32,7 +32,7 @@ class DatabaseManagerPage extends Component { onEditDatabase={actions.editDatabase} onCancelDatabase={actions.removeDatabase} onConfirmDatabase={actions.createDatabaseAsync} - onStartDeleteDatabase={actions.startDeleteDatabase} + onStartDeleteDatabase={actions.addDatabaseDeleteCode} onAddRetentionPolicy={actions.addRetentionPolicy} onEditRetentionPolicy={actions.editRetentionPolicy} onCreateRetentionPolicy={actions.createRetentionPolicyAsync} diff --git a/ui/src/admin/reducers/admin.js b/ui/src/admin/reducers/admin.js index 1fe5fff5c..1dd1bb610 100644 --- a/ui/src/admin/reducers/admin.js +++ b/ui/src/admin/reducers/admin.js @@ -180,7 +180,7 @@ export default function admin(state = initialState, action) { return {...state, ...newState} } - case 'START_DELETE_DATABASE': { + case 'ADD_DATABASE_DELETE_CODE': { const {database} = action.payload const newState = { databases: state.databases.map(db => db.links.self === database.links.self ? {...db, deleteCode: ''} : db), From 585fef4f06471067ab3844788b33dba2641b98c7 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Mar 2017 16:53:40 -0700 Subject: [PATCH 46/95] Add remove and add rp tests --- ui/spec/admin/reducers/adminSpec.js | 35 +++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/ui/spec/admin/reducers/adminSpec.js b/ui/spec/admin/reducers/adminSpec.js index 963833940..ff15d7552 100644 --- a/ui/spec/admin/reducers/adminSpec.js +++ b/ui/spec/admin/reducers/adminSpec.js @@ -4,6 +4,7 @@ import { addUser, addRole, addDatabase, + addRetentionPolicy, syncUser, syncRole, editUser, @@ -14,6 +15,7 @@ import { deleteRole, deleteUser, removeDatabase, + removeRetentionPolicy, filterRoles, filterUsers, addDatabaseDeleteCode, @@ -114,10 +116,18 @@ const scoped = {scope: 'db1', allowed: ['p1', 'p3']} const permissions = [global, scoped] // Databases && Retention Policies +const rp1 = { + name: 'rp1', + duration: '0', + replication: 2, + isDefault: true, + links: {self: '/chronograf/v1/sources/1/db/db1/rp/rp1'}, +} + const db1 = { name: 'db1', links: {self: '/chronograf/v1/sources/1/db/db1'}, - retentionPolicies: [], + retentionPolicies: [rp1], } const db2 = { @@ -128,7 +138,7 @@ const db2 = { } describe('Admin.Reducers', () => { - describe('Databases and Retention Policies', () => { + describe('Databases', () => { const state = {databases: [db1, db2]} it('can add a database', () => { @@ -177,7 +187,28 @@ describe('Admin.Reducers', () => { expect(actual.databases).to.deep.equal(expected) }) + }) + describe('Retention Policies', () => { + const state = {databases: [db1]} + + it('can add a retention policy', () => { + const actual = reducer(state, addRetentionPolicy(db1)) + const expected = [ + {...db1, retentionPolicies: [NEW_EMPTY_RP, rp1]}, + ] + + expect(actual.databases).to.deep.equal(expected) + }) + + it('can remove a retention policy', () => { + const actual = reducer(state, removeRetentionPolicy(db1, rp1)) + const expected = [ + {...db1, retentionPolicies: []}, + ] + + expect(actual.databases).to.deep.equal(expected) + }) }) it('it can add a user', () => { From 5cf448e55984486a13e8a4e8964ccbfde9ba5840 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Mar 2017 17:08:02 -0700 Subject: [PATCH 47/95] Add edit rp tests and refactor edit rp action --- ui/spec/admin/reducers/adminSpec.js | 11 +++++++++++ ui/src/admin/actions/index.js | 7 ++++--- ui/src/admin/components/DatabaseManager.js | 2 -- ui/src/admin/components/DatabaseRow.js | 2 +- ui/src/admin/containers/DatabaseManagerPage.js | 2 -- ui/src/admin/reducers/admin.js | 4 ++-- 6 files changed, 18 insertions(+), 10 deletions(-) diff --git a/ui/spec/admin/reducers/adminSpec.js b/ui/spec/admin/reducers/adminSpec.js index ff15d7552..5d8dec75e 100644 --- a/ui/spec/admin/reducers/adminSpec.js +++ b/ui/spec/admin/reducers/adminSpec.js @@ -10,6 +10,7 @@ import { editUser, editRole, editDatabase, + editRetentionPolicy, loadRoles, loadPermissions, deleteRole, @@ -209,6 +210,16 @@ describe('Admin.Reducers', () => { expect(actual.databases).to.deep.equal(expected) }) + + it('can edit a retention policy', () => { + const updates = {name: 'rpOne', duration: '100y', replication: '42'} + const actual = reducer(state, editRetentionPolicy(db1, rp1, updates)) + const expected = [ + {...db1, retentionPolicies: [{...rp1, ...updates}]}, + ] + + expect(actual.databases).to.deep.equal(expected) + }) }) it('it can add a user', () => { diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index 873925ecc..7cfb9894e 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -198,11 +198,12 @@ export const removeDatabaseDeleteCode = (database) => ({ }, }) -export const editRetentionPolicy = (database, retentionPolicy) => ({ +export const editRetentionPolicy = (database, retentionPolicy, updates) => ({ type: 'EDIT_RETENTION_POLICY', payload: { database, retentionPolicy, + updates, }, }) @@ -288,10 +289,10 @@ export const createRetentionPolicyAsync = (url, retentionPolicy) => async (dispa } } -export const updateRetentionPolicyAsync = (database, retentionPolicy) => async (dispatch) => { +export const updateRetentionPolicyAsync = (database, retentionPolicy, updates) => async (dispatch) => { try { // TODO: implement once server is up - dispatch(editRetentionPolicy(database, retentionPolicy)) + dispatch(editRetentionPolicy(database, retentionPolicy, updates)) // const {data} = await createRetentionPolicyAJAX(url, retentionPolicy) dispatch(publishNotification('success', 'Retention policy updated successfully')) // dispatch(syncRetentionPolicy(retentionPolicy, {...data, id: uuid.v4()})) diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index 22769291d..5568de6a9 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -12,7 +12,6 @@ const DatabaseManager = ({ onStartDeleteDatabase, onDatabaseDeleteConfirm, onAddRetentionPolicy, - onEditRetentionPolicy, onStopEditRetentionPolicy, onCancelRetentionPolicy, onCreateRetentionPolicy, @@ -39,7 +38,6 @@ const DatabaseManager = ({ onStartDeleteDatabase={onStartDeleteDatabase} onDatabaseDeleteConfirm={onDatabaseDeleteConfirm} onAddRetentionPolicy={onAddRetentionPolicy} - onEditRetentionPolicy={onEditRetentionPolicy} onStopEditRetentionPolicy={onStopEditRetentionPolicy} onCancelRetentionPolicy={onCancelRetentionPolicy} onCreateRetentionPolicy={onCreateRetentionPolicy} diff --git a/ui/src/admin/components/DatabaseRow.js b/ui/src/admin/components/DatabaseRow.js index 519da0812..e18ee8592 100644 --- a/ui/src/admin/components/DatabaseRow.js +++ b/ui/src/admin/components/DatabaseRow.js @@ -133,7 +133,7 @@ class DatabaseRow extends Component { return } - onUpdate(database, {...retentionPolicy, ...validInputs}) + onUpdate(database, retentionPolicy, validInputs) this.handleEndEdit() } diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index 02680ed1c..d4ca446b0 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -34,7 +34,6 @@ class DatabaseManagerPage extends Component { onConfirmDatabase={actions.createDatabaseAsync} onStartDeleteDatabase={actions.addDatabaseDeleteCode} onAddRetentionPolicy={actions.addRetentionPolicy} - onEditRetentionPolicy={actions.editRetentionPolicy} onCreateRetentionPolicy={actions.createRetentionPolicyAsync} onUpdateRetentionPolicy={actions.updateRetentionPolicyAsync} onRemoveRetentionPolicy={actions.removeRetentionPolicy} @@ -106,7 +105,6 @@ DatabaseManagerPage.propTypes = { removeDatabase: func, startDeleteDatabase: func, removeDatabaseDeleteCode: func, - editRetentionPolicy: func, removeRetentionPolicy: func, }), notify: func, diff --git a/ui/src/admin/reducers/admin.js b/ui/src/admin/reducers/admin.js index 1dd1bb610..2f607add9 100644 --- a/ui/src/admin/reducers/admin.js +++ b/ui/src/admin/reducers/admin.js @@ -128,12 +128,12 @@ export default function admin(state = initialState, action) { } case 'EDIT_RETENTION_POLICY': { - const {database, retentionPolicy} = action.payload + const {database, retentionPolicy, updates} = action.payload const newState = { databases: state.databases.map(db => db.links.self === database.links.self ? { ...db, - retentionPolicies: db.retentionPolicies.map(rp => rp.links.self === retentionPolicy.links.self ? {...rp, ...retentionPolicy} : rp), + retentionPolicies: db.retentionPolicies.map(rp => rp.links.self === retentionPolicy.links.self ? {...rp, ...updates} : rp), } : db), } From 056abde56dda6dfb030d46e73796fb889ab0c797 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Wed, 22 Mar 2017 01:40:30 -0700 Subject: [PATCH 48/95] WIP --- chronograf.go | 14 +++++ enterprise/enterprise.go | 2 + enterprise/types.go | 11 ++++ influx/influx.go | 4 ++ influx/permissions.go | 18 +++++++ server/databases.go | 109 ++++++++++++++++++++------------------- server/mux.go | 20 +++---- 7 files changed, 116 insertions(+), 62 deletions(-) diff --git a/chronograf.go b/chronograf.go index fc624bea4..afd1cbc8d 100644 --- a/chronograf.go +++ b/chronograf.go @@ -59,6 +59,8 @@ type TimeSeries interface { Permissions(context.Context) Permissions // Roles represents the roles associated with this TimesSeriesDatabase Roles(context.Context) (RolesStore, error) + // Databases represents the named databases within an InfluxDB instance + Databases(context.Context) Databases } // Role is a restricted set of permissions assigned to a set of users. @@ -294,6 +296,18 @@ type UsersStore interface { Update(context.Context, *User) error } +// type Database struct { +// Name string `json:"name"` // a unique string identifier for the database +// Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) +// Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy) +// ShardDuration string `json:shardDuration,omitempty` // the shard duration (when creating a default retention policy) +// } +// +// type Databases interface { +// // All lists all databases +// AllDB(context.Context) ([]Database, error) +// } + // DashboardID is the dashboard ID type DashboardID int diff --git a/enterprise/enterprise.go b/enterprise/enterprise.go index 189e5a150..9e82d4695 100644 --- a/enterprise/enterprise.go +++ b/enterprise/enterprise.go @@ -34,6 +34,8 @@ type Ctrl interface { SetRoleUsers(ctx context.Context, name string, users []string) error AddRoleUsers(ctx context.Context, name string, users []string) error RemoveRoleUsers(ctx context.Context, name string, users []string) error + + Databases(ctx context.Context) (*Databases, error) } // Client is a device for retrieving time series data from an Influx Enterprise diff --git a/enterprise/types.go b/enterprise/types.go index d3c241ca2..6e0f161af 100644 --- a/enterprise/types.go +++ b/enterprise/types.go @@ -65,6 +65,17 @@ type RoleAction struct { Role *Role `json:"role"` } +type Database struct { + Name string `json:"name"` // a unique string identifier for the database + Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) + Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy) + ShardDuration string `json:shardDuration,omitempty` // the shard duration (when creating a default retention policy) +} + +type Databases struct { + Databases []Database `json:"databases,omitempty"` +} + // Error is JSON error message return by Influx Enterprise's meta API. type Error struct { Error string `json:"error"` diff --git a/influx/influx.go b/influx/influx.go index 02d8eebf1..857c158fb 100644 --- a/influx/influx.go +++ b/influx/influx.go @@ -180,6 +180,10 @@ func (c *Client) Users(ctx context.Context) chronograf.UsersStore { return c } +func (c *Client) Databases(ctx context.Context) chronograf.Databases { + return c +} + // Roles aren't support in OSS func (c *Client) Roles(ctx context.Context) (chronograf.RolesStore, error) { return nil, fmt.Errorf("Roles not support in open-source InfluxDB. Roles are support in Influx Enterprise") diff --git a/influx/permissions.go b/influx/permissions.go index 809aff953..07fe482c0 100644 --- a/influx/permissions.go +++ b/influx/permissions.go @@ -75,6 +75,24 @@ func (r *showResults) Users() []chronograf.User { return res } +// Databases converts SHOW DATABASES to chronograf Databases +func (r *showResults) Databases() []chronograf.Database { + res := []chronograf.Database{} + for _, u := range *r { + for _, s := range u.Series { + for _, v := range s.Values { + if name, ok := v[0].(string); !ok { + continue + } else { + d := chronograf.Database{Name: name} + res = append(res, d) + } + } + } + } + return res +} + // Permissions converts SHOW GRANTS to chronograf.Permissions func (r *showResults) Permissions() chronograf.Permissions { res := []chronograf.Permission{} diff --git a/server/databases.go b/server/databases.go index 0bb112bf4..7228ea15a 100644 --- a/server/databases.go +++ b/server/databases.go @@ -12,7 +12,7 @@ type dbLinks struct { RPs string `json:"rps"` // URL for retention policies for this database } -type database struct { +type dbResponse struct { Name string `json:"name"` // a unique string identifier for the database Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy) @@ -20,62 +20,67 @@ type database struct { Links dbLinks `json:links` // Links are URI locations related to the database } -type postInfluxResponze struct { - Results interface{} `json:"results"` // results from influx +type dbsResponse struct { + Databases []dbResponse `json:"databases"` } +// type influxResponse struct { +// Results interface{} `json:"results"` // results from influx +// } + +// func (h *Service) sourcesSeries(ctx context.Context, w http.ResponseWriter, r *http.Request) (int, chronograf.TimeSeries, error) { +// srcID, err := paramID("id", r) +// if err != nil { +// Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) +// return 0, nil, err +// } +// +// src, err := h.SourcesStore.Get(ctx, srcID) +// if err != nil { +// notFound(w, srcID, h.Logger) +// return 0, nil, err +// } +// +// ts, err := h.TimeSeries(src) +// if err != nil { +// msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err) +// Error(w, http.StatusBadRequest, msg, h.Logger) +// return 0, nil, err +// } +// +// if err = ts.Connect(ctx, &src); err != nil { +// msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err) +// Error(w, http.StatusBadRequest, msg, h.Logger) +// return 0, nil, err +// } +// return srcID, ts, nil +// } + // Databases queries the list of all databases for a source func (h *Service) Databases(w http.ResponseWriter, r *http.Request) { - id, err := paramID("id", r) - if err != nil { - Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) - return + ctx := r.Context() + srcID, ts, err := h.sourcesSeries(ctx, w, r) + if err != nil { + return + } + + store := ts.Databases(ctx) + databases, err := store.All(ctx) + if err != nil { + Error(w, http.StatusBadRequest, err.Error(), h.Logger) + return + } + + dbs := make([]dbResponse, len(databases)) + for i, d := range databases { + + } + + // res = append(res, database{Name: response}) + + res := dbsResponse{ + Databases: dbs, } - // res := []database{} - - // move this influxdb communication code somewhere else after it's working - // START - ctx := r.Context() - src, err := h.SourcesStore.Get(ctx, id) - if err != nil { - notFound(w, id, h.Logger) - return - } - - ts, err := h.TimeSeries(src) - if err != nil { - msg := fmt.Sprintf("Unable to connect to source %d: %v", id, err) - Error(w, http.StatusBadRequest, msg, h.Logger) - return - } - - if err = ts.Connect(ctx, &src); err != nil { - msg := fmt.Sprintf("Unable to connect to source %d: %v", id, err) - Error(w, http.StatusBadRequest, msg, h.Logger) - return - } - - req := chronograf.Query{Command: "SHOW DATABASES"} - - response, err := ts.Query(ctx, req) - if err != nil { - if err == chronograf.ErrUpstreamTimeout { - msg := "Timeout waiting for Influx response" - Error(w, http.StatusRequestTimeout, msg, h.Logger) - return - } - // TODO: Here I want to return the error code from influx. - Error(w, http.StatusBadRequest, err.Error(), h.Logger) - return - } - - res := postInfluxResponze{ - Results: response, - } - - //fmt.Printf("%+v\n", foo) - // END - encodeJSON(w, http.StatusOK, res, h.Logger) } diff --git a/server/mux.go b/server/mux.go index e549ca410..4a8f2b6d4 100644 --- a/server/mux.go +++ b/server/mux.go @@ -133,16 +133,16 @@ func NewMux(opts MuxOpts, service Service) http.Handler { // Databases router.GET("/chronograf/v1/sources/:id/dbs", service.Databases) - router.POST("/chronograf/v1/sources/:id/dbs", service.NewDatabase) - - router.DELETE("/chronograf/v1/sources/:id/dbs/:did", service.DropDatabase) - - // Retention Policies - router.GET("/chronograf/v1/sources/:id/dbs/:did/rps", service.RetentionPolicies) - router.POST("/chronograf/v1/sources/:id/dbs/:did/rps", service.NewRetentionPolicy) - - router.PATCH("/chronograf/v1/sources/:id/dbs/:did/rps/:rpid", service.UpdateRetentionPolicy) - router.DELETE("/chronograf/v1/sources/:id/dbs/:did/rps/:rpid", service.DropRetentionPolicy) + // router.POST("/chronograf/v1/sources/:id/dbs", service.NewDatabase) + // + // router.DELETE("/chronograf/v1/sources/:id/dbs/:did", service.DropDatabase) + // + // // Retention Policies + // router.GET("/chronograf/v1/sources/:id/dbs/:did/rps", service.RetentionPolicies) + // router.POST("/chronograf/v1/sources/:id/dbs/:did/rps", service.NewRetentionPolicy) + // + // router.PATCH("/chronograf/v1/sources/:id/dbs/:did/rps/:rpid", service.UpdateRetentionPolicy) + // router.DELETE("/chronograf/v1/sources/:id/dbs/:did/rps/:rpid", service.DropRetentionPolicy) var authRoutes AuthRoutes From aebbb3743fc41b30b9acd50c8367e63f26df40d4 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Wed, 22 Mar 2017 02:53:19 -0700 Subject: [PATCH 49/95] WIP ugh --- chronograf.go | 22 +++++++++++----------- enterprise/enterprise.go | 7 ++++++- enterprise/meta.go | 11 +++++++++++ 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/chronograf.go b/chronograf.go index afd1cbc8d..1edb44c2b 100644 --- a/chronograf.go +++ b/chronograf.go @@ -296,17 +296,17 @@ type UsersStore interface { Update(context.Context, *User) error } -// type Database struct { -// Name string `json:"name"` // a unique string identifier for the database -// Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) -// Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy) -// ShardDuration string `json:shardDuration,omitempty` // the shard duration (when creating a default retention policy) -// } -// -// type Databases interface { -// // All lists all databases -// AllDB(context.Context) ([]Database, error) -// } +type Database struct { + Name string `json:"name"` // a unique string identifier for the database + Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) + Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy) + ShardDuration string `json:shardDuration,omitempty` // the shard duration (when creating a default retention policy) +} + +type Databases interface { + // All lists all databases + AllDB(context.Context) ([]Database, error) +} // DashboardID is the dashboard ID type DashboardID int diff --git a/enterprise/enterprise.go b/enterprise/enterprise.go index 9e82d4695..0f9d737f4 100644 --- a/enterprise/enterprise.go +++ b/enterprise/enterprise.go @@ -35,7 +35,8 @@ type Ctrl interface { AddRoleUsers(ctx context.Context, name string, users []string) error RemoveRoleUsers(ctx context.Context, name string, users []string) error - Databases(ctx context.Context) (*Databases, error) + // TODO add error here? + Databases(ctx context.Context) chronograf.Databases } // Client is a device for retrieving time series data from an Influx Enterprise @@ -142,6 +143,10 @@ func (c *Client) Query(ctx context.Context, q chronograf.Query) (chronograf.Resp return c.nextDataNode().Query(ctx, q) } +// func (c *Client) Databases(context.Context) chronograf.Databases { +// return c.Databases +// } + // Users is the interface to the users within Influx Enterprise func (c *Client) Users(context.Context) chronograf.UsersStore { return c.UsersStore diff --git a/enterprise/meta.go b/enterprise/meta.go index cd08730b7..94330a4ac 100644 --- a/enterprise/meta.go +++ b/enterprise/meta.go @@ -46,6 +46,17 @@ func (m *MetaClient) ShowCluster(ctx context.Context) (*Cluster, error) { return out, nil } +func (m *MetaClient) Databases(ctx context.Context) chronograf.Databases { + res, _ := m.Do(ctx, "GET", "/databases", nil, nil) + + defer res.Body.Close() + dec := json.NewDecoder(res.Body) + dbs := []chronograf.Database{} + databases := chronograf.Databases{Databases: dbs} + // _ = dec.Decode(databases) + return databases +} + // Users gets all the users. If name is not nil it filters for a single user func (m *MetaClient) Users(ctx context.Context, name *string) (*Users, error) { params := map[string]string{} From cea9d9a8dc39835aba2622cdc5522f315dcc3732 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Wed, 22 Mar 2017 04:01:55 -0700 Subject: [PATCH 50/95] uuuugh --- chronograf.go | 2 -- enterprise/enterprise.go | 7 ------- enterprise/meta.go | 11 ----------- influx/databases.go | 35 +++++++++++++++++++++++++++++++++++ influx/influx.go | 4 ---- server/databases.go | 6 +++--- 6 files changed, 38 insertions(+), 27 deletions(-) create mode 100644 influx/databases.go diff --git a/chronograf.go b/chronograf.go index 1edb44c2b..c8d1c7513 100644 --- a/chronograf.go +++ b/chronograf.go @@ -59,8 +59,6 @@ type TimeSeries interface { Permissions(context.Context) Permissions // Roles represents the roles associated with this TimesSeriesDatabase Roles(context.Context) (RolesStore, error) - // Databases represents the named databases within an InfluxDB instance - Databases(context.Context) Databases } // Role is a restricted set of permissions assigned to a set of users. diff --git a/enterprise/enterprise.go b/enterprise/enterprise.go index 0f9d737f4..189e5a150 100644 --- a/enterprise/enterprise.go +++ b/enterprise/enterprise.go @@ -34,9 +34,6 @@ type Ctrl interface { SetRoleUsers(ctx context.Context, name string, users []string) error AddRoleUsers(ctx context.Context, name string, users []string) error RemoveRoleUsers(ctx context.Context, name string, users []string) error - - // TODO add error here? - Databases(ctx context.Context) chronograf.Databases } // Client is a device for retrieving time series data from an Influx Enterprise @@ -143,10 +140,6 @@ func (c *Client) Query(ctx context.Context, q chronograf.Query) (chronograf.Resp return c.nextDataNode().Query(ctx, q) } -// func (c *Client) Databases(context.Context) chronograf.Databases { -// return c.Databases -// } - // Users is the interface to the users within Influx Enterprise func (c *Client) Users(context.Context) chronograf.UsersStore { return c.UsersStore diff --git a/enterprise/meta.go b/enterprise/meta.go index 94330a4ac..cd08730b7 100644 --- a/enterprise/meta.go +++ b/enterprise/meta.go @@ -46,17 +46,6 @@ func (m *MetaClient) ShowCluster(ctx context.Context) (*Cluster, error) { return out, nil } -func (m *MetaClient) Databases(ctx context.Context) chronograf.Databases { - res, _ := m.Do(ctx, "GET", "/databases", nil, nil) - - defer res.Body.Close() - dec := json.NewDecoder(res.Body) - dbs := []chronograf.Database{} - databases := chronograf.Databases{Databases: dbs} - // _ = dec.Decode(databases) - return databases -} - // Users gets all the users. If name is not nil it filters for a single user func (m *MetaClient) Users(ctx context.Context, name *string) (*Users, error) { params := map[string]string{} diff --git a/influx/databases.go b/influx/databases.go new file mode 100644 index 000000000..1cbd6265a --- /dev/null +++ b/influx/databases.go @@ -0,0 +1,35 @@ +package influx + +import ( + "encoding/json" +) + +// this stuff should probably live elsewhere? +func (c *Client) AllDB(ctx context.Context) ([]chronograf.Database, error) { + databases, err := c.showDatabases(ctx) + if err != nil { + return nil, err + } + + return databases, nil +} + +func (c *Client) showDatabases(ctx context.Context) ([]chronograf.Database, error) { + res, err := c.Query(ctx, chronograf.Query{ + Command: `SHOW DATABASES`, + }) + if err != nil { + return nil, err + } + octets, err := res.MarshalJSON() + if err != nil { + return nil, err + } + + results := showResults{} + if err := json.Unmarshal(octets, &results); err != nil { + return nil, err + } + + return results.Databases(), nil +} diff --git a/influx/influx.go b/influx/influx.go index 857c158fb..02d8eebf1 100644 --- a/influx/influx.go +++ b/influx/influx.go @@ -180,10 +180,6 @@ func (c *Client) Users(ctx context.Context) chronograf.UsersStore { return c } -func (c *Client) Databases(ctx context.Context) chronograf.Databases { - return c -} - // Roles aren't support in OSS func (c *Client) Roles(ctx context.Context) (chronograf.RolesStore, error) { return nil, fmt.Errorf("Roles not support in open-source InfluxDB. Roles are support in Influx Enterprise") diff --git a/server/databases.go b/server/databases.go index 7228ea15a..f725a738e 100644 --- a/server/databases.go +++ b/server/databases.go @@ -1,8 +1,9 @@ package server import ( - "fmt" "net/http" + "context" + "encoding/json" "github.com/influxdata/chronograf" ) @@ -64,8 +65,7 @@ func (h *Service) Databases(w http.ResponseWriter, r *http.Request) { return } - store := ts.Databases(ctx) - databases, err := store.All(ctx) + databases, err := ts.AllDB(ctx) if err != nil { Error(w, http.StatusBadRequest, err.Error(), h.Logger) return From db54e5963fe077f972a643c828e80ff2af9163c2 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 22 Mar 2017 10:00:21 -0700 Subject: [PATCH 51/95] Add validation for database name input --- .../admin/containers/DatabaseManagerPage.js | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index d4ca446b0..68db3aeb6 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -11,6 +11,7 @@ class DatabaseManagerPage extends Component { super(props) this.handleKeyDownDatabase = ::this.handleKeyDownDatabase this.handleDatabaseDeleteConfirm = ::this.handleDatabaseDeleteConfirm + this.handleCreateDatabase = :: this.handleCreateDatabase } componentDidMount() { @@ -31,7 +32,7 @@ class DatabaseManagerPage extends Component { addDatabase={actions.addDatabase} onEditDatabase={actions.editDatabase} onCancelDatabase={actions.removeDatabase} - onConfirmDatabase={actions.createDatabaseAsync} + onConfirmDatabase={this.handleCreateDatabase} onStartDeleteDatabase={actions.addDatabaseDeleteCode} onAddRetentionPolicy={actions.addRetentionPolicy} onCreateRetentionPolicy={actions.createRetentionPolicyAsync} @@ -41,16 +42,29 @@ class DatabaseManagerPage extends Component { ) } + handleCreateDatabase(database) { + const {actions, notify} = this.props + + if (!database.name) { + return notify('error', 'Database name cannot be blank') + } + + actions.createDatabaseAsync(database) + } + handleKeyDownDatabase(e, database) { const {key} = e - const {actions} = this.props + const {actions, notify} = this.props if (key === 'Escape') { actions.removeDatabase(database) } if (key === 'Enter') { - // TODO: validate input + if (!database.name) { + return notify('error', 'Database name cannot be blank') + } + actions.createDatabaseAsync(database) } } From 19729da202fb7824e3e9a6adc17fcdfca1f9ee16 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Wed, 22 Mar 2017 11:29:38 -0700 Subject: [PATCH 52/95] cleanup --- influx/databases.go | 4 +++- server/databases.go | 38 -------------------------------------- 2 files changed, 3 insertions(+), 39 deletions(-) diff --git a/influx/databases.go b/influx/databases.go index 1cbd6265a..d33863249 100644 --- a/influx/databases.go +++ b/influx/databases.go @@ -2,9 +2,11 @@ package influx import ( "encoding/json" + "context" + + "github.com/influxdata/chronograf" ) -// this stuff should probably live elsewhere? func (c *Client) AllDB(ctx context.Context) ([]chronograf.Database, error) { databases, err := c.showDatabases(ctx) if err != nil { diff --git a/server/databases.go b/server/databases.go index f725a738e..9e60f198a 100644 --- a/server/databases.go +++ b/server/databases.go @@ -2,10 +2,6 @@ package server import ( "net/http" - "context" - "encoding/json" - - "github.com/influxdata/chronograf" ) type dbLinks struct { @@ -25,38 +21,6 @@ type dbsResponse struct { Databases []dbResponse `json:"databases"` } -// type influxResponse struct { -// Results interface{} `json:"results"` // results from influx -// } - -// func (h *Service) sourcesSeries(ctx context.Context, w http.ResponseWriter, r *http.Request) (int, chronograf.TimeSeries, error) { -// srcID, err := paramID("id", r) -// if err != nil { -// Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) -// return 0, nil, err -// } -// -// src, err := h.SourcesStore.Get(ctx, srcID) -// if err != nil { -// notFound(w, srcID, h.Logger) -// return 0, nil, err -// } -// -// ts, err := h.TimeSeries(src) -// if err != nil { -// msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err) -// Error(w, http.StatusBadRequest, msg, h.Logger) -// return 0, nil, err -// } -// -// if err = ts.Connect(ctx, &src); err != nil { -// msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err) -// Error(w, http.StatusBadRequest, msg, h.Logger) -// return 0, nil, err -// } -// return srcID, ts, nil -// } - // Databases queries the list of all databases for a source func (h *Service) Databases(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -76,8 +40,6 @@ func (h *Service) Databases(w http.ResponseWriter, r *http.Request) { } - // res = append(res, database{Name: response}) - res := dbsResponse{ Databases: dbs, } From 84d3da7edd190d50a5c07795dab6bce714142a8c Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 22 Mar 2017 12:29:48 -0700 Subject: [PATCH 53/95] Update delete code requirements --- ui/src/admin/components/DatabaseTable.js | 2 +- ui/src/admin/containers/DatabaseManagerPage.js | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index 759486e62..ddebd33a2 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -156,7 +156,7 @@ const Header = ({ name="name" type="text" value={database.deleteCode || ''} - placeholder="type DELETE to confirm" + placeholder={`DELETE ${database.name}`} onChange={(e) => onDatabaseDeleteConfirm(database, e)} onKeyDown={(e) => onDatabaseDeleteConfirm(database, e)} autoFocus={true} diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index 68db3aeb6..f49a56a66 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -71,13 +71,17 @@ class DatabaseManagerPage extends Component { handleDatabaseDeleteConfirm(database, e) { const {key, target: {value}} = e - const {actions, source} = this.props + const {actions, source, notify} = this.props if (key === 'Escape') { return actions.removeDatabaseDeleteCode(database) } - if (key === 'Enter' && database.deleteCode === 'DELETE') { + if (key === 'Enter') { + if (database.deleteCode !== `DELETE ${database.name}`) { + return notify('error', `Please type DELETE ${database.name} to confirm`) + } + return actions.deleteDatabaseAsync(source, database) } From 6a4c4122bbcf0cb3a0e56e725e3374c63d07bcf3 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Wed, 22 Mar 2017 13:27:36 -0700 Subject: [PATCH 54/95] it's working! --- chronograf.go | 1 + enterprise/types.go | 11 ----------- influx/influx.go | 1 + server/databases.go | 39 ++++++++++++++++++++++++++++----------- server/mux.go | 2 +- server/server.go | 2 ++ server/service.go | 1 + 7 files changed, 34 insertions(+), 23 deletions(-) diff --git a/chronograf.go b/chronograf.go index c8d1c7513..cbf128eac 100644 --- a/chronograf.go +++ b/chronograf.go @@ -304,6 +304,7 @@ type Database struct { type Databases interface { // All lists all databases AllDB(context.Context) ([]Database, error) + Connect(context.Context, *Source) error } // DashboardID is the dashboard ID diff --git a/enterprise/types.go b/enterprise/types.go index 6e0f161af..d3c241ca2 100644 --- a/enterprise/types.go +++ b/enterprise/types.go @@ -65,17 +65,6 @@ type RoleAction struct { Role *Role `json:"role"` } -type Database struct { - Name string `json:"name"` // a unique string identifier for the database - Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) - Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy) - ShardDuration string `json:shardDuration,omitempty` // the shard duration (when creating a default retention policy) -} - -type Databases struct { - Databases []Database `json:"databases,omitempty"` -} - // Error is JSON error message return by Influx Enterprise's meta API. type Error struct { Error string `json:"error"` diff --git a/influx/influx.go b/influx/influx.go index 02d8eebf1..db9d6f332 100644 --- a/influx/influx.go +++ b/influx/influx.go @@ -12,6 +12,7 @@ import ( ) var _ chronograf.TimeSeries = &Client{} +var _ chronograf.Databases = &Client{} // Shared transports for all clients to prevent leaking connections var ( diff --git a/server/databases.go b/server/databases.go index 9e60f198a..5714e5ca3 100644 --- a/server/databases.go +++ b/server/databases.go @@ -1,6 +1,7 @@ package server import ( + "fmt" "net/http" ) @@ -13,8 +14,8 @@ type dbResponse struct { Name string `json:"name"` // a unique string identifier for the database Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy) - ShardDuration string `json:shardDuration,omitempty` // the shard duration (when creating a default retention policy) - Links dbLinks `json:links` // Links are URI locations related to the database + ShardDuration string `json:"shardDuration,omitempty"` // the shard duration (when creating a default retention policy) + Links dbLinks `json:"links"` // Links are URI locations related to the database } type dbsResponse struct { @@ -22,23 +23,39 @@ type dbsResponse struct { } // Databases queries the list of all databases for a source -func (h *Service) Databases(w http.ResponseWriter, r *http.Request) { +func (h *Service) GetDatabases(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - srcID, ts, err := h.sourcesSeries(ctx, w, r) - if err != nil { - return - } - databases, err := ts.AllDB(ctx) + srcID, err := paramID("id", r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) + return + } + + src, err := h.SourcesStore.Get(ctx, srcID) + if err != nil { + notFound(w, srcID, h.Logger) + return + } + + db := h.Databases + + if err = db.Connect(ctx, &src); err != nil { + msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err) + Error(w, http.StatusBadRequest, msg, h.Logger) + return + } + + databases, err := db.AllDB(ctx) if err != nil { Error(w, http.StatusBadRequest, err.Error(), h.Logger) return } dbs := make([]dbResponse, len(databases)) - for i, d := range databases { - - } + // for i, d := range databases { + // + // } res := dbsResponse{ Databases: dbs, diff --git a/server/mux.go b/server/mux.go index 4a8f2b6d4..d50368b90 100644 --- a/server/mux.go +++ b/server/mux.go @@ -132,7 +132,7 @@ func NewMux(opts MuxOpts, service Service) http.Handler { router.PATCH("/chronograf/v1/dashboards/:id", service.UpdateDashboard) // Databases - router.GET("/chronograf/v1/sources/:id/dbs", service.Databases) + router.GET("/chronograf/v1/sources/:id/dbs", service.GetDatabases) // router.POST("/chronograf/v1/sources/:id/dbs", service.NewDatabase) // // router.DELETE("/chronograf/v1/sources/:id/dbs/:did", service.DropDatabase) diff --git a/server/server.go b/server/server.go index 3a494b9f8..2c0a883d1 100644 --- a/server/server.go +++ b/server/server.go @@ -21,6 +21,7 @@ import ( client "github.com/influxdata/usage-client/v1" flags "github.com/jessevdk/go-flags" "github.com/tylerb/graceful" + "github.com/influxdata/chronograf/influx" ) var ( @@ -292,6 +293,7 @@ func openService(boltPath, cannedPath string, logger chronograf.Logger, useAuth AlertRulesStore: db.AlertsStore, Logger: logger, UseAuth: useAuth, + Databases: &influx.Client{Logger: logger}, } } diff --git a/server/service.go b/server/service.go index 8dfa115a4..34ea80f65 100644 --- a/server/service.go +++ b/server/service.go @@ -20,6 +20,7 @@ type Service struct { TimeSeriesClient TimeSeriesClient Logger chronograf.Logger UseAuth bool + Databases chronograf.Databases } // TimeSeriesClient returns the correct client for a time series database. From 15b2744a5c6334bf7ff476ed41f984cece0beaf4 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Wed, 22 Mar 2017 13:46:30 -0700 Subject: [PATCH 55/95] write db info to dbresponse --- server/databases.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/server/databases.go b/server/databases.go index 5714e5ca3..1c51aa93f 100644 --- a/server/databases.go +++ b/server/databases.go @@ -53,9 +53,11 @@ func (h *Service) GetDatabases(w http.ResponseWriter, r *http.Request) { } dbs := make([]dbResponse, len(databases)) - // for i, d := range databases { - // - // } + for i, d := range databases { + dbs[i] = dbResponse{ + Name: d.Name, + } + } res := dbsResponse{ Databases: dbs, From 5528377de5283a000c82de9192d6fae7f30c4fdd Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 22 Mar 2017 17:15:45 -0700 Subject: [PATCH 56/95] Remove replication factor for OSS and simplify rf formatting --- ui/src/admin/components/DatabaseManager.js | 4 +++ ui/src/admin/components/DatabaseRow.js | 32 +++++++++++-------- ui/src/admin/components/DatabaseTable.js | 7 +++- .../admin/containers/DatabaseManagerPage.js | 3 +- ui/src/utils/formatting.js | 10 ++++++ 5 files changed, 41 insertions(+), 15 deletions(-) diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index 5568de6a9..b9053052c 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -4,6 +4,7 @@ import DatabaseTable from 'src/admin/components/DatabaseTable' const DatabaseManager = ({ databases, notify, + isRFDisplayed, addDatabase, onEditDatabase, onKeyDownDatabase, @@ -31,6 +32,7 @@ const DatabaseManager = ({ key={db.links.self} database={db} notify={notify} + isRFDisplayed={isRFDisplayed} onEditDatabase={onEditDatabase} onKeyDownDatabase={onKeyDownDatabase} onCancelDatabase={onCancelDatabase} @@ -53,6 +55,7 @@ const DatabaseManager = ({ const { arrayOf, + bool, func, shape, } = PropTypes @@ -61,6 +64,7 @@ DatabaseManager.propTypes = { databases: arrayOf(shape()), notify: func, addDatabase: func, + isRFDisplayed: bool, onEditDatabase: func, onKeyDownDatabase: func, onCancelDatabase: func, diff --git a/ui/src/admin/components/DatabaseRow.js b/ui/src/admin/components/DatabaseRow.js index e18ee8592..777e62414 100644 --- a/ui/src/admin/components/DatabaseRow.js +++ b/ui/src/admin/components/DatabaseRow.js @@ -1,5 +1,5 @@ import React, {PropTypes, Component} from 'react' -import {formatRPDuration} from 'utils/formatting' +import {formatInfiniteDuration} from 'utils/formatting' import YesNoButtons from 'src/shared/components/YesNoButtons' import onClickOutside from 'react-onclickoutside' @@ -30,8 +30,11 @@ class DatabaseRow extends Component { retentionPolicy: {name, duration, replication, isDefault, isNew}, retentionPolicy, database, + isRFDisplayed, } = this.props + const formattedDuration = formatInfiniteDuration(duration) + if (this.state.isEditing) { return (
@@ -55,27 +58,27 @@ class DatabaseRow extends Component { className="form-control" name="name" type="text" - defaultValue={duration} + defaultValue={formattedDuration} placeholder="how long should data last" onKeyDown={(e) => this.handleKeyDown(e, database)} ref={(r) => this.duration = r} /> - + - - + + + {isRFDisplayed ? : null} - + {isRFDisplayed ? : null} @@ -55,6 +57,7 @@ const DatabaseTable = ({ onCreate={onCreateRetentionPolicy} onUpdate={onUpdateRetentionPolicy} onRemove={onRemoveRetentionPolicy} + isRFDisplayed={isRFDisplayed} /> ) }) @@ -70,6 +73,7 @@ DatabaseTable.propTypes = { onEditDatabase: func, database: shape(), notify: func, + isRFDisplayed: bool, onKeyDownDatabase: func, onCancelDatabase: func, onConfirmDatabase: func, @@ -207,6 +211,7 @@ EditHeader.propTypes = { onKeyDown: func, onCancel: func, onConfirm: func, + isRFDisplayed: bool, } export default DatabaseTable diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index f49a56a66..1104368ed 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -21,12 +21,13 @@ class DatabaseManagerPage extends Component { } render() { - const {databases, actions, notify} = this.props + const {source, databases, actions, notify} = this.props return ( { return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`; } +// Using InfluxDB 1.2+ we should no longer need this formatter. +// Times can now be submitted using multiple units i.e. 1d2h3m export const formatRPDuration = (duration) => { if (duration === '0' || duration === '0s') { return '∞'; @@ -38,3 +40,11 @@ export const formatRPDuration = (duration) => { return adjustedTime; } + +export const formatInfiniteDuration = (duration) => { + if (duration === '0' || duration === '0s' || duration === 'INF') { + return '∞'; + } + + return duration +} From 523a0788f7dbd3ca7a0a3c780254876659c13392 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Wed, 22 Mar 2017 22:21:25 -0700 Subject: [PATCH 57/95] WIP --- chronograf.go | 1 + influx/databases.go | 14 ++++++++++++++ server/databases.go | 34 ++++++++++++++++++++++++++++++++++ server/mux.go | 2 +- 4 files changed, 50 insertions(+), 1 deletion(-) diff --git a/chronograf.go b/chronograf.go index cbf128eac..63cf80dde 100644 --- a/chronograf.go +++ b/chronograf.go @@ -305,6 +305,7 @@ type Databases interface { // All lists all databases AllDB(context.Context) ([]Database, error) Connect(context.Context, *Source) error + CreateDB(context.Context, db *chronograf.Database) (*chronograf.Database, error) } // DashboardID is the dashboard ID diff --git a/influx/databases.go b/influx/databases.go index d33863249..e3b29a194 100644 --- a/influx/databases.go +++ b/influx/databases.go @@ -3,6 +3,7 @@ package influx import ( "encoding/json" "context" + "fmt" "github.com/influxdata/chronograf" ) @@ -16,6 +17,19 @@ func (c *Client) AllDB(ctx context.Context) ([]chronograf.Database, error) { return databases, nil } +func (c *Client) CreateDB(ctx context.Context, db *chronograf.Database) (*chronograf.Database, error) { + _, err := c.Query(ctx, chronograf.Query{ + Command: fmt.Sprintf(`CREATE DATABASE "%s"`, db.Name), + }) + if err != nil { + return nil, err + } + + res := &chronograf.Database{Name: db.Name} + + return res, nil +} + func (c *Client) showDatabases(ctx context.Context) ([]chronograf.Database, error) { res, err := c.Query(ctx, chronograf.Query{ Command: `SHOW DATABASES`, diff --git a/server/databases.go b/server/databases.go index 1c51aa93f..3346acaf6 100644 --- a/server/databases.go +++ b/server/databases.go @@ -65,3 +65,37 @@ func (h *Service) GetDatabases(w http.ResponseWriter, r *http.Request) { encodeJSON(w, http.StatusOK, res, h.Logger) } + +func (h *Service) NewDatabase(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + srcID, err := paramID("id", r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) + return + } + + src, err := h.SourcesStore.Get(ctx, srcID) + if err != nil { + notFound(w, srcID, h.Logger) + return + } + + db := h.Databases + + if err = db.Connect(ctx, &src); err != nil { + msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err) + Error(w, http.StatusBadRequest, msg, h.Logger) + return + } + + // need to pass database object from POST + database, err := db.CreateDB(ctx, ) + if err != nil { + Error(w, http.StatusBadRequest, err.Error(), h.Logger) + return + } + + res := dbResponse{Name: database.Name} + encodeJSON(w, http.StatusOK, res, h.Logger) +} diff --git a/server/mux.go b/server/mux.go index d50368b90..bb788459c 100644 --- a/server/mux.go +++ b/server/mux.go @@ -133,7 +133,7 @@ func NewMux(opts MuxOpts, service Service) http.Handler { // Databases router.GET("/chronograf/v1/sources/:id/dbs", service.GetDatabases) - // router.POST("/chronograf/v1/sources/:id/dbs", service.NewDatabase) + router.POST("/chronograf/v1/sources/:id/dbs", service.NewDatabase) // // router.DELETE("/chronograf/v1/sources/:id/dbs/:did", service.DropDatabase) // From c21a378629db8763f57c23b30b5330c7c42ec8bf Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Wed, 22 Mar 2017 23:21:21 -0700 Subject: [PATCH 58/95] db creation seems to be working --- chronograf.go | 2 +- server/databases.go | 26 +++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/chronograf.go b/chronograf.go index 63cf80dde..b8e2e09b4 100644 --- a/chronograf.go +++ b/chronograf.go @@ -305,7 +305,7 @@ type Databases interface { // All lists all databases AllDB(context.Context) ([]Database, error) Connect(context.Context, *Source) error - CreateDB(context.Context, db *chronograf.Database) (*chronograf.Database, error) + CreateDB(context.Context, *Database) (*Database, error) } // DashboardID is the dashboard ID diff --git a/server/databases.go b/server/databases.go index 3346acaf6..af707de07 100644 --- a/server/databases.go +++ b/server/databases.go @@ -3,6 +3,9 @@ package server import ( "fmt" "net/http" + "encoding/json" + + "github.com/influxdata/chronograf" ) type dbLinks struct { @@ -89,13 +92,30 @@ func (h *Service) NewDatabase(w http.ResponseWriter, r *http.Request) { return } - // need to pass database object from POST - database, err := db.CreateDB(ctx, ) + postedDB := &chronograf.Database{} + if err := json.NewDecoder(r.Body).Decode(postedDB); err != nil { + invalidJSON(w, h.Logger) + return + } + + if err := ValidDatabaseRequest(postedDB); err != nil { + invalidData(w, err, h.Logger) + return + } + + database, err := db.CreateDB(ctx, postedDB) if err != nil { Error(w, http.StatusBadRequest, err.Error(), h.Logger) return } res := dbResponse{Name: database.Name} - encodeJSON(w, http.StatusOK, res, h.Logger) + encodeJSON(w, http.StatusCreated, res, h.Logger) +} + +func ValidDatabaseRequest(d *chronograf.Database) error { + if len(d.Name) == 0 { + return fmt.Errorf("name is required") + } + return nil } From 766ee9b517b1356d2ada8106218aabb79f8c8a8b Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 23 Mar 2017 01:04:35 -0700 Subject: [PATCH 59/95] drop db API --- chronograf.go | 1 + influx/databases.go | 10 ++++++++++ server/databases.go | 39 +++++++++++++++++++++++++++++++++++++++ server/mux.go | 14 +++++++------- 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/chronograf.go b/chronograf.go index b8e2e09b4..b6cb5dc90 100644 --- a/chronograf.go +++ b/chronograf.go @@ -306,6 +306,7 @@ type Databases interface { AllDB(context.Context) ([]Database, error) Connect(context.Context, *Source) error CreateDB(context.Context, *Database) (*Database, error) + DropDB(context.Context, string) error } // DashboardID is the dashboard ID diff --git a/influx/databases.go b/influx/databases.go index e3b29a194..7be081f63 100644 --- a/influx/databases.go +++ b/influx/databases.go @@ -30,6 +30,16 @@ func (c *Client) CreateDB(ctx context.Context, db *chronograf.Database) (*chrono return res, nil } +func (c *Client) DropDB(ctx context.Context, name string) error { + _, err := c.Query(ctx, chronograf.Query{ + Command: fmt.Sprintf(`DROP DATABASE "%s"`, name), + }) + if err != nil { + return err + } + return nil +} + func (c *Client) showDatabases(ctx context.Context) ([]chronograf.Database, error) { res, err := c.Query(ctx, chronograf.Query{ Command: `SHOW DATABASES`, diff --git a/server/databases.go b/server/databases.go index af707de07..83539267e 100644 --- a/server/databases.go +++ b/server/databases.go @@ -5,6 +5,7 @@ import ( "net/http" "encoding/json" + "github.com/bouk/httprouter" "github.com/influxdata/chronograf" ) @@ -113,6 +114,44 @@ func (h *Service) NewDatabase(w http.ResponseWriter, r *http.Request) { encodeJSON(w, http.StatusCreated, res, h.Logger) } +func (h *Service) DropDatabase(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + srcID, err := paramID("id", r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) + return + } + + src, err := h.SourcesStore.Get(ctx, srcID) + if err != nil { + notFound(w, srcID, h.Logger) + return + } + + db := h.Databases + + if err = db.Connect(ctx, &src); err != nil { + msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err) + Error(w, http.StatusBadRequest, msg, h.Logger) + return + } + + dbID := httprouter.GetParamFromContext(ctx, "dbid") + if err != nil { + Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) + return + } + + dropErr := db.DropDB(ctx, dbID) + if dropErr != nil { + Error(w, http.StatusBadRequest, dropErr.Error(), h.Logger) + return + } + + w.WriteHeader(http.StatusNoContent) +} + func ValidDatabaseRequest(d *chronograf.Database) error { if len(d.Name) == 0 { return fmt.Errorf("name is required") diff --git a/server/mux.go b/server/mux.go index bb788459c..5c7ea3f26 100644 --- a/server/mux.go +++ b/server/mux.go @@ -134,15 +134,15 @@ func NewMux(opts MuxOpts, service Service) http.Handler { // Databases router.GET("/chronograf/v1/sources/:id/dbs", service.GetDatabases) router.POST("/chronograf/v1/sources/:id/dbs", service.NewDatabase) - // - // router.DELETE("/chronograf/v1/sources/:id/dbs/:did", service.DropDatabase) - // + + router.DELETE("/chronograf/v1/sources/:id/dbs/:dbid", service.DropDatabase) + // // Retention Policies - // router.GET("/chronograf/v1/sources/:id/dbs/:did/rps", service.RetentionPolicies) - // router.POST("/chronograf/v1/sources/:id/dbs/:did/rps", service.NewRetentionPolicy) + // router.GET("/chronograf/v1/sources/:id/dbs/:dbid/rps", service.RetentionPolicies) + // router.POST("/chronograf/v1/sources/:id/dbs/:dbid/rps", service.NewRetentionPolicy) // - // router.PATCH("/chronograf/v1/sources/:id/dbs/:did/rps/:rpid", service.UpdateRetentionPolicy) - // router.DELETE("/chronograf/v1/sources/:id/dbs/:did/rps/:rpid", service.DropRetentionPolicy) + // router.PATCH("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", service.UpdateRetentionPolicy) + // router.DELETE("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", service.DropRetentionPolicy) var authRoutes AuthRoutes From 2438090e70ad6b75d37814463014c8157859dac2 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 23 Mar 2017 03:06:59 -0700 Subject: [PATCH 60/95] get retention policies --- chronograf.go | 11 ++++++- influx/databases.go | 30 +++++++++++++++++ influx/permissions.go | 31 ++++++++++++++++++ server/databases.go | 76 +++++++++++++++++++++++++++++++++++++++++-- server/mux.go | 4 +-- 5 files changed, 146 insertions(+), 6 deletions(-) diff --git a/chronograf.go b/chronograf.go index b6cb5dc90..1e32234bd 100644 --- a/chronograf.go +++ b/chronograf.go @@ -298,7 +298,15 @@ type Database struct { Name string `json:"name"` // a unique string identifier for the database Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy) - ShardDuration string `json:shardDuration,omitempty` // the shard duration (when creating a default retention policy) + ShardDuration string `json:"shardDuration,omitempty"` // the shard duration (when creating a default retention policy) +} + +type RetentionPolicy struct { + Name string `json:"name"` // a unique string identifier for the retention policy + Duration string `json:"duration,omitempty"` // the duration + Replication int32 `json:"replication,omitempty"` // the replication factor + ShardDuration string `json:"shardDuration,omitempty"` // the shard duration + Default bool `json:"default,omitempty"` // whether the RP should be the default } type Databases interface { @@ -307,6 +315,7 @@ type Databases interface { Connect(context.Context, *Source) error CreateDB(context.Context, *Database) (*Database, error) DropDB(context.Context, string) error + AllRP(context.Context, string) ([]RetentionPolicy, error) } // DashboardID is the dashboard ID diff --git a/influx/databases.go b/influx/databases.go index 7be081f63..4450744c1 100644 --- a/influx/databases.go +++ b/influx/databases.go @@ -40,6 +40,15 @@ func (c *Client) DropDB(ctx context.Context, name string) error { return nil } +func (c *Client) AllRP(ctx context.Context, name string) ([]chronograf.RetentionPolicy, error) { + retentionPolicies, err := c.showRetentionPolicies(ctx, name) + if err != nil { + return nil, err + } + + return retentionPolicies, nil +} + func (c *Client) showDatabases(ctx context.Context) ([]chronograf.Database, error) { res, err := c.Query(ctx, chronograf.Query{ Command: `SHOW DATABASES`, @@ -59,3 +68,24 @@ func (c *Client) showDatabases(ctx context.Context) ([]chronograf.Database, erro return results.Databases(), nil } + +func (c *Client) showRetentionPolicies(ctx context.Context, name string) ([]chronograf.RetentionPolicy, error) { + retentionPolicies, err := c.Query(ctx, chronograf.Query{ + Command: fmt.Sprintf(`SHOW RETENTION POLICIES ON "%s"`, name), + }) + + if err != nil { + return nil, err + } + octets, err := retentionPolicies.MarshalJSON() + if err != nil { + return nil, err + } + + results := showResults{} + if err := json.Unmarshal(octets, &results); err != nil { + return nil, err + } + + return results.RetentionPolicies(), nil +} diff --git a/influx/permissions.go b/influx/permissions.go index 07fe482c0..573414797 100644 --- a/influx/permissions.go +++ b/influx/permissions.go @@ -93,6 +93,37 @@ func (r *showResults) Databases() []chronograf.Database { return res } +func (r *showResults) RetentionPolicies() []chronograf.RetentionPolicy { + res := []chronograf.RetentionPolicy{} + for _, u := range *r { + for _, s := range u.Series { + for _, v := range s.Values { + if name, ok := v[0].(string); !ok { + continue + } else if duration, ok := v[1].(string); !ok { + continue + } else if sduration, ok := v[2].(string); !ok { + continue + } else if replication, ok := v[3].(int32); !ok { + continue + } else if def, ok := v[4].(bool); !ok { + continue + } else { + d := chronograf.RetentionPolicy{ + Name: name, + Duration: duration, + ShardDuration: sduration, + Replication: replication, + Default: def, + } + res = append(res, d) + } + } + } + } + return res +} + // Permissions converts SHOW GRANTS to chronograf.Permissions func (r *showResults) Permissions() chronograf.Permissions { res := []chronograf.Permission{} diff --git a/server/databases.go b/server/databases.go index 83539267e..d0a1ef9ba 100644 --- a/server/databases.go +++ b/server/databases.go @@ -15,9 +15,9 @@ type dbLinks struct { } type dbResponse struct { - Name string `json:"name"` // a unique string identifier for the database - Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) - Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy) + Name string `json:"name"` // a unique string identifier for the database + Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) + Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy) ShardDuration string `json:"shardDuration,omitempty"` // the shard duration (when creating a default retention policy) Links dbLinks `json:"links"` // Links are URI locations related to the database } @@ -26,6 +26,23 @@ type dbsResponse struct { Databases []dbResponse `json:"databases"` } +type rpLinks struct { + Self string `json:"self"` // Self link mapping to this resource +} + +type rpResponse struct { + Name string `json:"name"` // a unique string identifier for the retention policy + Duration string `json:"duration"` // the duration + Replication int32 `json:"replication"` // the replication factor + ShardDuration string `json:"shardDuration"` // the shard duration + Default bool `json:"default"` // whether the RP should be the default + Links rpLinks `json:"links"` // Links are URI locations related to the database +} + +type rpsResponse struct { + RetentionPolicies []rpResponse `json:"retentionPolicies"` +} + // Databases queries the list of all databases for a source func (h *Service) GetDatabases(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -152,6 +169,59 @@ func (h *Service) DropDatabase(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) } +func (h *Service) RetentionPolicies(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + srcID, err := paramID("id", r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) + return + } + + src, err := h.SourcesStore.Get(ctx, srcID) + if err != nil { + notFound(w, srcID, h.Logger) + return + } + + db := h.Databases + + if err = db.Connect(ctx, &src); err != nil { + msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err) + Error(w, http.StatusBadRequest, msg, h.Logger) + return + } + + dbID := httprouter.GetParamFromContext(ctx, "dbid") + if err != nil { + Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) + return + } + + allRP, err := db.AllRP(ctx, dbID) + if err != nil { + Error(w, http.StatusBadRequest, err.Error(), h.Logger) + return + } + + rps := make([]rpResponse, len(allRP)) + for i, rp := range allRP { + rps[i] = rpResponse{ + Name: rp.Name, + Duration: rp.Duration, + Replication: rp.Replication, + ShardDuration: rp.ShardDuration, + Default: rp.Default, + } + } + + res := rpsResponse{ + RetentionPolicies: rps, + } + + encodeJSON(w, http.StatusOK, res, h.Logger) +} + func ValidDatabaseRequest(d *chronograf.Database) error { if len(d.Name) == 0 { return fmt.Errorf("name is required") diff --git a/server/mux.go b/server/mux.go index 5c7ea3f26..1a8cff534 100644 --- a/server/mux.go +++ b/server/mux.go @@ -137,8 +137,8 @@ func NewMux(opts MuxOpts, service Service) http.Handler { router.DELETE("/chronograf/v1/sources/:id/dbs/:dbid", service.DropDatabase) - // // Retention Policies - // router.GET("/chronograf/v1/sources/:id/dbs/:dbid/rps", service.RetentionPolicies) + // Retention Policies + router.GET("/chronograf/v1/sources/:id/dbs/:dbid/rps", service.RetentionPolicies) // router.POST("/chronograf/v1/sources/:id/dbs/:dbid/rps", service.NewRetentionPolicy) // // router.PATCH("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", service.UpdateRetentionPolicy) From 073c03303dea300cd4dc495744199b61ef846b02 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 23 Mar 2017 03:37:32 -0700 Subject: [PATCH 61/95] fix rp GET --- influx/databases.go | 3 ++- influx/permissions.go | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/influx/databases.go b/influx/databases.go index 4450744c1..b014f6536 100644 --- a/influx/databases.go +++ b/influx/databases.go @@ -71,7 +71,8 @@ func (c *Client) showDatabases(ctx context.Context) ([]chronograf.Database, erro func (c *Client) showRetentionPolicies(ctx context.Context, name string) ([]chronograf.RetentionPolicy, error) { retentionPolicies, err := c.Query(ctx, chronograf.Query{ - Command: fmt.Sprintf(`SHOW RETENTION POLICIES ON "%s"`, name), + Command: fmt.Sprintf(`SHOW RETENTION POLICIES`), + DB: name, }) if err != nil { diff --git a/influx/permissions.go b/influx/permissions.go index 573414797..ba3206acb 100644 --- a/influx/permissions.go +++ b/influx/permissions.go @@ -104,7 +104,7 @@ func (r *showResults) RetentionPolicies() []chronograf.RetentionPolicy { continue } else if sduration, ok := v[2].(string); !ok { continue - } else if replication, ok := v[3].(int32); !ok { + } else if replication, ok := v[3].(float64); !ok { continue } else if def, ok := v[4].(bool); !ok { continue @@ -113,7 +113,7 @@ func (r *showResults) RetentionPolicies() []chronograf.RetentionPolicy { Name: name, Duration: duration, ShardDuration: sduration, - Replication: replication, + Replication: int32(replication), Default: def, } res = append(res, d) From b378dd7c27ff077d8b03254941f581a8a63f5fbd Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 23 Mar 2017 04:27:53 -0700 Subject: [PATCH 62/95] createRP --- chronograf.go | 1 + influx/databases.go | 27 ++++++++++++++++--- server/databases.go | 63 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 4 deletions(-) diff --git a/chronograf.go b/chronograf.go index 1e32234bd..94c159a56 100644 --- a/chronograf.go +++ b/chronograf.go @@ -316,6 +316,7 @@ type Databases interface { CreateDB(context.Context, *Database) (*Database, error) DropDB(context.Context, string) error AllRP(context.Context, string) ([]RetentionPolicy, error) + CreateRP(context.Context, string, *RetentionPolicy) (*RetentionPolicy, error) } // DashboardID is the dashboard ID diff --git a/influx/databases.go b/influx/databases.go index b014f6536..b77d3ed45 100644 --- a/influx/databases.go +++ b/influx/databases.go @@ -30,9 +30,10 @@ func (c *Client) CreateDB(ctx context.Context, db *chronograf.Database) (*chrono return res, nil } -func (c *Client) DropDB(ctx context.Context, name string) error { +func (c *Client) DropDB(ctx context.Context, database string) error { _, err := c.Query(ctx, chronograf.Query{ - Command: fmt.Sprintf(`DROP DATABASE "%s"`, name), + Command: fmt.Sprintf(`DROP DATABASE`), + DB: database, }) if err != nil { return err @@ -40,8 +41,8 @@ func (c *Client) DropDB(ctx context.Context, name string) error { return nil } -func (c *Client) AllRP(ctx context.Context, name string) ([]chronograf.RetentionPolicy, error) { - retentionPolicies, err := c.showRetentionPolicies(ctx, name) +func (c *Client) AllRP(ctx context.Context, database string) ([]chronograf.RetentionPolicy, error) { + retentionPolicies, err := c.showRetentionPolicies(ctx, database) if err != nil { return nil, err } @@ -49,6 +50,24 @@ func (c *Client) AllRP(ctx context.Context, name string) ([]chronograf.Retention return retentionPolicies, nil } +func (c *Client) CreateRP(ctx context.Context, database string, rp *chronograf.RetentionPolicy) (*chronograf.RetentionPolicy, error) { + _, err := c.Query(ctx, chronograf.Query{ + Command: fmt.Sprintf(`CREATE RETENTION POLICY "%s" DURATION "%s" REPLICATION "%s"`, rp.Name, rp.Duration, rp.Replication), + DB: database, + }) + if err != nil { + return nil, err + } + + res := &chronograf.RetentionPolicy{ + Name: rp.Name, + Duration: rp.Duration, + Replication: rp.Replication, + } + + return res, nil +} + func (c *Client) showDatabases(ctx context.Context) ([]chronograf.Database, error) { res, err := c.Query(ctx, chronograf.Query{ Command: `SHOW DATABASES`, diff --git a/server/databases.go b/server/databases.go index d0a1ef9ba..6064b629b 100644 --- a/server/databases.go +++ b/server/databases.go @@ -222,9 +222,72 @@ func (h *Service) RetentionPolicies(w http.ResponseWriter, r *http.Request) { encodeJSON(w, http.StatusOK, res, h.Logger) } +func (h *Service) NewRetentionPolicy(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + srcID, err := paramID("id", r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) + return + } + + src, err := h.SourcesStore.Get(ctx, srcID) + if err != nil { + notFound(w, srcID, h.Logger) + return + } + + db := h.Databases + + if err = db.Connect(ctx, &src); err != nil { + msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err) + Error(w, http.StatusBadRequest, msg, h.Logger) + return + } + + postedRP := &chronograf.RetentionPolicy{} + if err := json.NewDecoder(r.Body).Decode(postedRP); err != nil { + invalidJSON(w, h.Logger) + return + } + if err := ValidRetentionPolicyRequest(postedRP); err != nil { + invalidData(w, err, h.Logger) + return + } + + dbID := httprouter.GetParamFromContext(ctx, "dbid") + if err != nil { + Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) + return + } + + database, err := db.CreateRP(ctx, dbID, postedRP) + if err != nil { + Error(w, http.StatusBadRequest, err.Error(), h.Logger) + return + } + + res := dbResponse{Name: database.Name} + encodeJSON(w, http.StatusCreated, res, h.Logger) +} + func ValidDatabaseRequest(d *chronograf.Database) error { if len(d.Name) == 0 { return fmt.Errorf("name is required") } return nil } + +func ValidRetentionPolicyRequest(rp *chronograf.RetentionPolicy) error { + if len(rp.Name) == 0 { + return fmt.Errorf("name is required") + } + if len(rp.Duration) == 0 { + return fmt.Errorf("duration is required") + } + if rp.Replication == 0 { + return fmt.Errorf("replication factor is invalid") + } + + return nil +} From b40a90b9d1e442cf3c614ad2337c364f41aedcf0 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 23 Mar 2017 04:51:08 -0700 Subject: [PATCH 63/95] dropRP --- chronograf.go | 1 + influx/databases.go | 12 ++++++++++++ server/databases.go | 47 +++++++++++++++++++++++++++++++++------------ server/mux.go | 6 +++--- 4 files changed, 51 insertions(+), 15 deletions(-) diff --git a/chronograf.go b/chronograf.go index 94c159a56..0bfd5e107 100644 --- a/chronograf.go +++ b/chronograf.go @@ -317,6 +317,7 @@ type Databases interface { DropDB(context.Context, string) error AllRP(context.Context, string) ([]RetentionPolicy, error) CreateRP(context.Context, string, *RetentionPolicy) (*RetentionPolicy, error) + DropRP(context.Context, string, string) error } // DashboardID is the dashboard ID diff --git a/influx/databases.go b/influx/databases.go index b77d3ed45..475e03994 100644 --- a/influx/databases.go +++ b/influx/databases.go @@ -68,6 +68,18 @@ func (c *Client) CreateRP(ctx context.Context, database string, rp *chronograf.R return res, nil } +func (c *Client) DropRP(ctx context.Context, database string, rp string) error { + _, err := c.Query(ctx, chronograf.Query{ + Command: fmt.Sprintf(`DROP RETENTION POLICY`), + DB: database, + RP: rp, + }) + if err != nil { + return err + } + return nil +} + func (c *Client) showDatabases(ctx context.Context) ([]chronograf.Database, error) { res, err := c.Query(ctx, chronograf.Query{ Command: `SHOW DATABASES`, diff --git a/server/databases.go b/server/databases.go index 6064b629b..4861c0570 100644 --- a/server/databases.go +++ b/server/databases.go @@ -155,10 +155,6 @@ func (h *Service) DropDatabase(w http.ResponseWriter, r *http.Request) { } dbID := httprouter.GetParamFromContext(ctx, "dbid") - if err != nil { - Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) - return - } dropErr := db.DropDB(ctx, dbID) if dropErr != nil { @@ -193,10 +189,6 @@ func (h *Service) RetentionPolicies(w http.ResponseWriter, r *http.Request) { } dbID := httprouter.GetParamFromContext(ctx, "dbid") - if err != nil { - Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) - return - } allRP, err := db.AllRP(ctx, dbID) if err != nil { @@ -256,10 +248,6 @@ func (h *Service) NewRetentionPolicy(w http.ResponseWriter, r *http.Request) { } dbID := httprouter.GetParamFromContext(ctx, "dbid") - if err != nil { - Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) - return - } database, err := db.CreateRP(ctx, dbID, postedRP) if err != nil { @@ -271,6 +259,41 @@ func (h *Service) NewRetentionPolicy(w http.ResponseWriter, r *http.Request) { encodeJSON(w, http.StatusCreated, res, h.Logger) } +func (h *Service) DropRetentionPolicy(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + srcID, err := paramID("id", r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) + return + } + + src, err := h.SourcesStore.Get(ctx, srcID) + if err != nil { + notFound(w, srcID, h.Logger) + return + } + + db := h.Databases + + if err = db.Connect(ctx, &src); err != nil { + msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err) + Error(w, http.StatusBadRequest, msg, h.Logger) + return + } + + dbID := httprouter.GetParamFromContext(ctx, "dbid") + rpID := httprouter.GetParamFromContext(ctx, "rpid") + + dropErr := db.DropRP(ctx, dbID, rpID) + if dropErr != nil { + Error(w, http.StatusBadRequest, dropErr.Error(), h.Logger) + return + } + + w.WriteHeader(http.StatusNoContent) +} + func ValidDatabaseRequest(d *chronograf.Database) error { if len(d.Name) == 0 { return fmt.Errorf("name is required") diff --git a/server/mux.go b/server/mux.go index 1a8cff534..3cf9d4041 100644 --- a/server/mux.go +++ b/server/mux.go @@ -139,10 +139,10 @@ func NewMux(opts MuxOpts, service Service) http.Handler { // Retention Policies router.GET("/chronograf/v1/sources/:id/dbs/:dbid/rps", service.RetentionPolicies) - // router.POST("/chronograf/v1/sources/:id/dbs/:dbid/rps", service.NewRetentionPolicy) - // + router.POST("/chronograf/v1/sources/:id/dbs/:dbid/rps", service.NewRetentionPolicy) + // router.PATCH("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", service.UpdateRetentionPolicy) - // router.DELETE("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", service.DropRetentionPolicy) + router.DELETE("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", service.DropRetentionPolicy) var authRoutes AuthRoutes From ba8609d7b69476f3750838c198a5bf30ebe3b506 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 23 Mar 2017 04:56:36 -0700 Subject: [PATCH 64/95] go format --- chronograf.go | 22 +++--- influx/databases.go | 166 ++++++++++++++++++++++---------------------- server/databases.go | 44 ++++++------ 3 files changed, 116 insertions(+), 116 deletions(-) diff --git a/chronograf.go b/chronograf.go index 0bfd5e107..742f43fee 100644 --- a/chronograf.go +++ b/chronograf.go @@ -295,23 +295,23 @@ type UsersStore interface { } type Database struct { - Name string `json:"name"` // a unique string identifier for the database - Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) - Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy) - ShardDuration string `json:"shardDuration,omitempty"` // the shard duration (when creating a default retention policy) + Name string `json:"name"` // a unique string identifier for the database + Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) + Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy) + ShardDuration string `json:"shardDuration,omitempty"` // the shard duration (when creating a default retention policy) } type RetentionPolicy struct { - Name string `json:"name"` // a unique string identifier for the retention policy - Duration string `json:"duration,omitempty"` // the duration - Replication int32 `json:"replication,omitempty"` // the replication factor - ShardDuration string `json:"shardDuration,omitempty"` // the shard duration - Default bool `json:"default,omitempty"` // whether the RP should be the default + Name string `json:"name"` // a unique string identifier for the retention policy + Duration string `json:"duration,omitempty"` // the duration + Replication int32 `json:"replication,omitempty"` // the replication factor + ShardDuration string `json:"shardDuration,omitempty"` // the shard duration + Default bool `json:"default,omitempty"` // whether the RP should be the default } type Databases interface { - // All lists all databases - AllDB(context.Context) ([]Database, error) + // All lists all databases + AllDB(context.Context) ([]Database, error) Connect(context.Context, *Source) error CreateDB(context.Context, *Database) (*Database, error) DropDB(context.Context, string) error diff --git a/influx/databases.go b/influx/databases.go index 475e03994..b60598c91 100644 --- a/influx/databases.go +++ b/influx/databases.go @@ -1,123 +1,123 @@ package influx import ( - "encoding/json" - "context" - "fmt" + "context" + "encoding/json" + "fmt" - "github.com/influxdata/chronograf" + "github.com/influxdata/chronograf" ) func (c *Client) AllDB(ctx context.Context) ([]chronograf.Database, error) { - databases, err := c.showDatabases(ctx) - if err != nil { - return nil, err - } + databases, err := c.showDatabases(ctx) + if err != nil { + return nil, err + } - return databases, nil + return databases, nil } func (c *Client) CreateDB(ctx context.Context, db *chronograf.Database) (*chronograf.Database, error) { - _, err := c.Query(ctx, chronograf.Query{ - Command: fmt.Sprintf(`CREATE DATABASE "%s"`, db.Name), - }) - if err != nil { - return nil, err - } + _, err := c.Query(ctx, chronograf.Query{ + Command: fmt.Sprintf(`CREATE DATABASE "%s"`, db.Name), + }) + if err != nil { + return nil, err + } - res := &chronograf.Database{Name: db.Name} + res := &chronograf.Database{Name: db.Name} - return res, nil + return res, nil } func (c *Client) DropDB(ctx context.Context, database string) error { - _, err := c.Query(ctx, chronograf.Query{ - Command: fmt.Sprintf(`DROP DATABASE`), - DB: database, - }) - if err != nil { - return err - } - return nil + _, err := c.Query(ctx, chronograf.Query{ + Command: fmt.Sprintf(`DROP DATABASE`), + DB: database, + }) + if err != nil { + return err + } + return nil } func (c *Client) AllRP(ctx context.Context, database string) ([]chronograf.RetentionPolicy, error) { - retentionPolicies, err := c.showRetentionPolicies(ctx, database) - if err != nil { - return nil, err - } + retentionPolicies, err := c.showRetentionPolicies(ctx, database) + if err != nil { + return nil, err + } - return retentionPolicies, nil + return retentionPolicies, nil } func (c *Client) CreateRP(ctx context.Context, database string, rp *chronograf.RetentionPolicy) (*chronograf.RetentionPolicy, error) { - _, err := c.Query(ctx, chronograf.Query{ - Command: fmt.Sprintf(`CREATE RETENTION POLICY "%s" DURATION "%s" REPLICATION "%s"`, rp.Name, rp.Duration, rp.Replication), - DB: database, - }) - if err != nil { - return nil, err - } + _, err := c.Query(ctx, chronograf.Query{ + Command: fmt.Sprintf(`CREATE RETENTION POLICY "%s" DURATION "%s" REPLICATION "%s"`, rp.Name, rp.Duration, rp.Replication), + DB: database, + }) + if err != nil { + return nil, err + } - res := &chronograf.RetentionPolicy{ - Name: rp.Name, - Duration: rp.Duration, - Replication: rp.Replication, - } + res := &chronograf.RetentionPolicy{ + Name: rp.Name, + Duration: rp.Duration, + Replication: rp.Replication, + } - return res, nil + return res, nil } func (c *Client) DropRP(ctx context.Context, database string, rp string) error { - _, err := c.Query(ctx, chronograf.Query{ - Command: fmt.Sprintf(`DROP RETENTION POLICY`), - DB: database, - RP: rp, - }) - if err != nil { - return err - } - return nil + _, err := c.Query(ctx, chronograf.Query{ + Command: fmt.Sprintf(`DROP RETENTION POLICY`), + DB: database, + RP: rp, + }) + if err != nil { + return err + } + return nil } func (c *Client) showDatabases(ctx context.Context) ([]chronograf.Database, error) { - res, err := c.Query(ctx, chronograf.Query{ - Command: `SHOW DATABASES`, - }) - if err != nil { - return nil, err - } - octets, err := res.MarshalJSON() - if err != nil { - return nil, err - } + res, err := c.Query(ctx, chronograf.Query{ + Command: `SHOW DATABASES`, + }) + if err != nil { + return nil, err + } + octets, err := res.MarshalJSON() + if err != nil { + return nil, err + } - results := showResults{} - if err := json.Unmarshal(octets, &results); err != nil { - return nil, err - } + results := showResults{} + if err := json.Unmarshal(octets, &results); err != nil { + return nil, err + } - return results.Databases(), nil + return results.Databases(), nil } func (c *Client) showRetentionPolicies(ctx context.Context, name string) ([]chronograf.RetentionPolicy, error) { - retentionPolicies, err := c.Query(ctx, chronograf.Query{ - Command: fmt.Sprintf(`SHOW RETENTION POLICIES`), - DB: name, - }) + retentionPolicies, err := c.Query(ctx, chronograf.Query{ + Command: fmt.Sprintf(`SHOW RETENTION POLICIES`), + DB: name, + }) - if err != nil { - return nil, err - } - octets, err := retentionPolicies.MarshalJSON() - if err != nil { - return nil, err - } + if err != nil { + return nil, err + } + octets, err := retentionPolicies.MarshalJSON() + if err != nil { + return nil, err + } - results := showResults{} - if err := json.Unmarshal(octets, &results); err != nil { - return nil, err - } + results := showResults{} + if err := json.Unmarshal(octets, &results); err != nil { + return nil, err + } - return results.RetentionPolicies(), nil + return results.RetentionPolicies(), nil } diff --git a/server/databases.go b/server/databases.go index 4861c0570..79647280b 100644 --- a/server/databases.go +++ b/server/databases.go @@ -1,11 +1,11 @@ package server import ( + "encoding/json" "fmt" "net/http" - "encoding/json" - "github.com/bouk/httprouter" + "github.com/bouk/httprouter" "github.com/influxdata/chronograf" ) @@ -23,7 +23,7 @@ type dbResponse struct { } type dbsResponse struct { - Databases []dbResponse `json:"databases"` + Databases []dbResponse `json:"databases"` } type rpLinks struct { @@ -40,14 +40,14 @@ type rpResponse struct { } type rpsResponse struct { - RetentionPolicies []rpResponse `json:"retentionPolicies"` + RetentionPolicies []rpResponse `json:"retentionPolicies"` } // Databases queries the list of all databases for a source func (h *Service) GetDatabases(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() + ctx := r.Context() - srcID, err := paramID("id", r) + srcID, err := paramID("id", r) if err != nil { Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) return @@ -59,7 +59,7 @@ func (h *Service) GetDatabases(w http.ResponseWriter, r *http.Request) { return } - db := h.Databases + db := h.Databases if err = db.Connect(ctx, &src); err != nil { msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err) @@ -67,18 +67,18 @@ func (h *Service) GetDatabases(w http.ResponseWriter, r *http.Request) { return } - databases, err := db.AllDB(ctx) - if err != nil { - Error(w, http.StatusBadRequest, err.Error(), h.Logger) - return - } + databases, err := db.AllDB(ctx) + if err != nil { + Error(w, http.StatusBadRequest, err.Error(), h.Logger) + return + } - dbs := make([]dbResponse, len(databases)) - for i, d := range databases { - dbs[i] = dbResponse{ - Name: d.Name, - } - } + dbs := make([]dbResponse, len(databases)) + for i, d := range databases { + dbs[i] = dbResponse{ + Name: d.Name, + } + } res := dbsResponse{ Databases: dbs, @@ -199,11 +199,11 @@ func (h *Service) RetentionPolicies(w http.ResponseWriter, r *http.Request) { rps := make([]rpResponse, len(allRP)) for i, rp := range allRP { rps[i] = rpResponse{ - Name: rp.Name, - Duration: rp.Duration, - Replication: rp.Replication, + Name: rp.Name, + Duration: rp.Duration, + Replication: rp.Replication, ShardDuration: rp.ShardDuration, - Default: rp.Default, + Default: rp.Default, } } From c547ca5ebc3664f4b40ead9b0748a5c8c903b655 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 23 Mar 2017 05:16:49 -0700 Subject: [PATCH 65/95] add missing patch responses swagger --- server/swagger.json | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/server/swagger.json b/server/swagger.json index 97e09c58f..e1afc826b 100644 --- a/server/swagger.json +++ b/server/swagger.json @@ -1023,7 +1023,27 @@ }, "required": true } - ] + ], + "responses": { + "200": { + "description": "Retention Policy was altered", + "schema": { + "$ref": "#/definitions/RetentionPolicy" + } + }, + "404": { + "description": "Database or source does not exist.", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "default": { + "description": "A processing or an unexpected error.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } }, "delete": { "tags": [ From 2a990e2b3e360810c60df8ac093fe850a9b991b8 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 23 Mar 2017 05:49:33 -0700 Subject: [PATCH 66/95] clean up some dashboards code --- server/dashboards.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/server/dashboards.go b/server/dashboards.go index 5eef491c9..9a662d63d 100644 --- a/server/dashboards.go +++ b/server/dashboards.go @@ -4,9 +4,7 @@ import ( "encoding/json" "fmt" "net/http" - "strconv" - "github.com/bouk/httprouter" "github.com/influxdata/chronograf" ) @@ -130,7 +128,7 @@ func (s *Service) RemoveDashboard(w http.ResponseWriter, r *http.Request) { // ReplaceDashboard completely replaces a dashboard func (s *Service) ReplaceDashboard(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - idParam, err := strconv.Atoi(httprouter.GetParamFromContext(ctx, "id")) + idParam, err := paramID("id", r) if err != nil { msg := fmt.Sprintf("Could not parse dashboard ID: %s", err) Error(w, http.StatusInternalServerError, msg, s.Logger) @@ -168,10 +166,11 @@ func (s *Service) ReplaceDashboard(w http.ResponseWriter, r *http.Request) { // UpdateDashboard completely updates either the dashboard name or the cells func (s *Service) UpdateDashboard(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - idParam, err := strconv.Atoi(httprouter.GetParamFromContext(ctx, "id")) + idParam, err := paramID("id", r) if err != nil { msg := fmt.Sprintf("Could not parse dashboard ID: %s", err) Error(w, http.StatusInternalServerError, msg, s.Logger) + return } id := chronograf.DashboardID(idParam) From e58a846f7b2a40e02ef26921e05320e0a48d740d Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 23 Mar 2017 06:13:41 -0700 Subject: [PATCH 67/95] updateRP --- chronograf.go | 1 + influx/databases.go | 25 +++++++++++++++++++++++ server/databases.go | 48 +++++++++++++++++++++++++++++++++++++++++++++ server/mux.go | 2 +- 4 files changed, 75 insertions(+), 1 deletion(-) diff --git a/chronograf.go b/chronograf.go index 742f43fee..f71ff508a 100644 --- a/chronograf.go +++ b/chronograf.go @@ -317,6 +317,7 @@ type Databases interface { DropDB(context.Context, string) error AllRP(context.Context, string) ([]RetentionPolicy, error) CreateRP(context.Context, string, *RetentionPolicy) (*RetentionPolicy, error) + UpdateRP(context.Context, string, string, *RetentionPolicy) (*RetentionPolicy, error) DropRP(context.Context, string, string) error } diff --git a/influx/databases.go b/influx/databases.go index b60598c91..84ac06439 100644 --- a/influx/databases.go +++ b/influx/databases.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "bytes" "github.com/influxdata/chronograf" ) @@ -68,6 +69,30 @@ func (c *Client) CreateRP(ctx context.Context, database string, rp *chronograf.R return res, nil } +func (c *Client) UpdateRP(ctx context.Context, database string, name string, rp *chronograf.RetentionPolicy) (*chronograf.RetentionPolicy, error) { + var buffer bytes.Buffer + buffer.WriteString("ALTER RETENTION POLICY") + if (len(rp.Duration) > 0) { + buffer.WriteString("DURATION " + rp.Duration) + } + + _, err := c.Query(ctx, chronograf.Query{ + Command: buffer.String(), + DB: database, + RP: name, + }) + if err != nil { + return nil, err + } + + // TODO: use actual information here + res := &chronograf.RetentionPolicy{ + Name: name, + } + + return res, nil +} + func (c *Client) DropRP(ctx context.Context, database string, rp string) error { _, err := c.Query(ctx, chronograf.Query{ Command: fmt.Sprintf(`DROP RETENTION POLICY`), diff --git a/server/databases.go b/server/databases.go index 79647280b..e51afc86f 100644 --- a/server/databases.go +++ b/server/databases.go @@ -259,6 +259,54 @@ func (h *Service) NewRetentionPolicy(w http.ResponseWriter, r *http.Request) { encodeJSON(w, http.StatusCreated, res, h.Logger) } +func (h *Service) UpdateRetentionPolicy(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + srcID, err := paramID("id", r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) + return + } + + src, err := h.SourcesStore.Get(ctx, srcID) + if err != nil { + notFound(w, srcID, h.Logger) + return + } + + db := h.Databases + + if err = db.Connect(ctx, &src); err != nil { + msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err) + Error(w, http.StatusBadRequest, msg, h.Logger) + return + } + + postedRP := &chronograf.RetentionPolicy{} + if err := json.NewDecoder(r.Body).Decode(postedRP); err != nil { + invalidJSON(w, h.Logger) + return + } + if err := ValidRetentionPolicyRequest(postedRP); err != nil { + invalidData(w, err, h.Logger) + return + } + + dbID := httprouter.GetParamFromContext(ctx, "dbid") + rpID := httprouter.GetParamFromContext(ctx, "rpid") + + rp, err := db.UpdateRP(ctx, dbID, rpID, postedRP) + + if err != nil { + Error(w, http.StatusBadRequest, err.Error(), h.Logger) + return + } + + // TODO: this needs to be the actual RP information + res := rpResponse{Name: rp.Name} + encodeJSON(w, http.StatusCreated, res, h.Logger) +} + func (h *Service) DropRetentionPolicy(w http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/server/mux.go b/server/mux.go index 3cf9d4041..b655fa0de 100644 --- a/server/mux.go +++ b/server/mux.go @@ -141,7 +141,7 @@ func NewMux(opts MuxOpts, service Service) http.Handler { router.GET("/chronograf/v1/sources/:id/dbs/:dbid/rps", service.RetentionPolicies) router.POST("/chronograf/v1/sources/:id/dbs/:dbid/rps", service.NewRetentionPolicy) - // router.PATCH("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", service.UpdateRetentionPolicy) + router.PATCH("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", service.UpdateRetentionPolicy) router.DELETE("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", service.DropRetentionPolicy) var authRoutes AuthRoutes From ffdc14ebdde6ae9b8e86fad4da94a4d086be1a82 Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 23 Mar 2017 06:21:19 -0700 Subject: [PATCH 68/95] add options to alter rp --- influx/databases.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/influx/databases.go b/influx/databases.go index 84ac06439..0e74b7b84 100644 --- a/influx/databases.go +++ b/influx/databases.go @@ -73,7 +73,16 @@ func (c *Client) UpdateRP(ctx context.Context, database string, name string, rp var buffer bytes.Buffer buffer.WriteString("ALTER RETENTION POLICY") if (len(rp.Duration) > 0) { - buffer.WriteString("DURATION " + rp.Duration) + buffer.WriteString(" DURATION " + rp.Duration) + } + if (rp.Replication > 0) { + buffer.WriteString(" REPLICATION " + fmt.Sprint(rp.Replication)) + } + if (len(rp.ShardDuration) > 0) { + buffer.WriteString(" SHARD DURATION " + rp.ShardDuration) + } + if (rp.Default == true) { + buffer.WriteString(" DEFAULT") } _, err := c.Query(ctx, chronograf.Query{ From 10a05412c0e37b6e38790e5fa3916c5ae17ab3da Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Thu, 23 Mar 2017 06:24:26 -0700 Subject: [PATCH 69/95] go fmt --- influx/databases.go | 56 ++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/influx/databases.go b/influx/databases.go index 0e74b7b84..0ca5d50ca 100644 --- a/influx/databases.go +++ b/influx/databases.go @@ -1,10 +1,10 @@ package influx import ( + "bytes" "context" "encoding/json" "fmt" - "bytes" "github.com/influxdata/chronograf" ) @@ -70,36 +70,36 @@ func (c *Client) CreateRP(ctx context.Context, database string, rp *chronograf.R } func (c *Client) UpdateRP(ctx context.Context, database string, name string, rp *chronograf.RetentionPolicy) (*chronograf.RetentionPolicy, error) { - var buffer bytes.Buffer - buffer.WriteString("ALTER RETENTION POLICY") - if (len(rp.Duration) > 0) { - buffer.WriteString(" DURATION " + rp.Duration) - } - if (rp.Replication > 0) { - buffer.WriteString(" REPLICATION " + fmt.Sprint(rp.Replication)) - } - if (len(rp.ShardDuration) > 0) { - buffer.WriteString(" SHARD DURATION " + rp.ShardDuration) - } - if (rp.Default == true) { - buffer.WriteString(" DEFAULT") - } + var buffer bytes.Buffer + buffer.WriteString("ALTER RETENTION POLICY") + if len(rp.Duration) > 0 { + buffer.WriteString(" DURATION " + rp.Duration) + } + if rp.Replication > 0 { + buffer.WriteString(" REPLICATION " + fmt.Sprint(rp.Replication)) + } + if len(rp.ShardDuration) > 0 { + buffer.WriteString(" SHARD DURATION " + rp.ShardDuration) + } + if rp.Default == true { + buffer.WriteString(" DEFAULT") + } - _, err := c.Query(ctx, chronograf.Query{ - Command: buffer.String(), - DB: database, - RP: name, - }) - if err != nil { - return nil, err - } + _, err := c.Query(ctx, chronograf.Query{ + Command: buffer.String(), + DB: database, + RP: name, + }) + if err != nil { + return nil, err + } - // TODO: use actual information here - res := &chronograf.RetentionPolicy{ - Name: name, - } + // TODO: use actual information here + res := &chronograf.RetentionPolicy{ + Name: name, + } - return res, nil + return res, nil } func (c *Client) DropRP(ctx context.Context, database string, rp string) error { From b0c57f950976a4356b2c01b0db26fcd448e7fc14 Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Thu, 23 Mar 2017 13:15:39 -0700 Subject: [PATCH 70/95] Fix retention policy InfluxQL creation string --- influx/databases.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/influx/databases.go b/influx/databases.go index 0ca5d50ca..b188ed488 100644 --- a/influx/databases.go +++ b/influx/databases.go @@ -53,7 +53,7 @@ func (c *Client) AllRP(ctx context.Context, database string) ([]chronograf.Reten func (c *Client) CreateRP(ctx context.Context, database string, rp *chronograf.RetentionPolicy) (*chronograf.RetentionPolicy, error) { _, err := c.Query(ctx, chronograf.Query{ - Command: fmt.Sprintf(`CREATE RETENTION POLICY "%s" DURATION "%s" REPLICATION "%s"`, rp.Name, rp.Duration, rp.Replication), + Command: fmt.Sprintf(`CREATE RETENTION POLICY "%s" ON "%s" DURATION %s REPLICATION %d`, rp.Name, database, rp.Duration, rp.Replication), DB: database, }) if err != nil { From 8827093bc65bbd1a1121b6508110bc733910571d Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Thu, 23 Mar 2017 13:16:02 -0700 Subject: [PATCH 71/95] Update databases/retention policy link responses --- server/databases.go | 34 +++++++++++++++++++++++++--------- server/sources.go | 2 ++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/server/databases.go b/server/databases.go index e51afc86f..ccad99a60 100644 --- a/server/databases.go +++ b/server/databases.go @@ -10,8 +10,8 @@ import ( ) type dbLinks struct { - Self string `json:"self"` // Self link mapping to this resource - RPs string `json:"rps"` // URL for retention policies for this database + Self string `json:"self"` // Self link mapping to this resource + RPs string `json:"retentionPolicies"` // URL for retention policies for this database } type dbResponse struct { @@ -22,6 +22,16 @@ type dbResponse struct { Links dbLinks `json:"links"` // Links are URI locations related to the database } +func NewDBResponse(srcID int, name string) dbResponse { + base := "/chronograf/v1/sources" + return dbResponse{ + Name: name, + Links: dbLinks{ + Self: fmt.Sprintf("%s/%d/dbs/%s", base, srcID, name), + }, + } +} + type dbsResponse struct { Databases []dbResponse `json:"databases"` } @@ -39,6 +49,13 @@ type rpResponse struct { Links rpLinks `json:"links"` // Links are URI locations related to the database } +func (r *rpResponse) WithLinks(srcID int, dbName string) { + base := "/chronograf/v1/sources" + r.Links = rpLinks{ + Self: fmt.Sprintf("%s/%d/dbs/%s/rps/%s", base, srcID, dbName, r.Name), + } +} + type rpsResponse struct { RetentionPolicies []rpResponse `json:"retentionPolicies"` } @@ -60,7 +77,6 @@ func (h *Service) GetDatabases(w http.ResponseWriter, r *http.Request) { } db := h.Databases - if err = db.Connect(ctx, &src); err != nil { msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err) Error(w, http.StatusBadRequest, msg, h.Logger) @@ -75,9 +91,7 @@ func (h *Service) GetDatabases(w http.ResponseWriter, r *http.Request) { dbs := make([]dbResponse, len(databases)) for i, d := range databases { - dbs[i] = dbResponse{ - Name: d.Name, - } + dbs[i] = NewDBResponse(srcID, d.Name) } res := dbsResponse{ @@ -127,7 +141,7 @@ func (h *Service) NewDatabase(w http.ResponseWriter, r *http.Request) { return } - res := dbResponse{Name: database.Name} + res := NewDBResponse(srcID, database.Name) encodeJSON(w, http.StatusCreated, res, h.Logger) } @@ -198,13 +212,15 @@ func (h *Service) RetentionPolicies(w http.ResponseWriter, r *http.Request) { rps := make([]rpResponse, len(allRP)) for i, rp := range allRP { - rps[i] = rpResponse{ + rp := rpResponse{ Name: rp.Name, Duration: rp.Duration, Replication: rp.Replication, ShardDuration: rp.ShardDuration, Default: rp.Default, } + rp.WithLinks(srcID, dbID) + rps[i] = rp } res := rpsResponse{ @@ -302,7 +318,7 @@ func (h *Service) UpdateRetentionPolicy(w http.ResponseWriter, r *http.Request) return } - // TODO: this needs to be the actual RP information + // TODO: this needs to be the actual RP information res := rpResponse{Name: rp.Name} encodeJSON(w, http.StatusCreated, res, h.Logger) } diff --git a/server/sources.go b/server/sources.go index 55c505229..d56a85ecd 100644 --- a/server/sources.go +++ b/server/sources.go @@ -18,6 +18,7 @@ type sourceLinks struct { Permissions string `json:"permissions"` // URL for all allowed permissions for this source Users string `json:"users"` // URL for all users associated with this source Roles string `json:"roles,omitempty"` // URL for all users associated with this source + Databases string `json:"databases"` // URL for the databases contained within this soure } type sourceResponse struct { @@ -43,6 +44,7 @@ func newSourceResponse(src chronograf.Source) sourceResponse { Kapacitors: fmt.Sprintf("%s/%d/kapacitors", httpAPISrcs, src.ID), Permissions: fmt.Sprintf("%s/%d/permissions", httpAPISrcs, src.ID), Users: fmt.Sprintf("%s/%d/users", httpAPISrcs, src.ID), + Databases: fmt.Sprintf("%s/%d/dbs", httpAPISrcs, src.ID), }, } From 2cd5e5044238146069f3b2b4c10b082e5019050a Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Thu, 23 Mar 2017 13:29:13 -0700 Subject: [PATCH 72/95] Add retention policy link to databases and add comments --- server/databases.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/server/databases.go b/server/databases.go index ccad99a60..c58b9456e 100644 --- a/server/databases.go +++ b/server/databases.go @@ -22,12 +22,14 @@ type dbResponse struct { Links dbLinks `json:"links"` // Links are URI locations related to the database } +// NewDBResponse creates the response for the /databases endpoint func NewDBResponse(srcID int, name string) dbResponse { base := "/chronograf/v1/sources" return dbResponse{ Name: name, Links: dbLinks{ Self: fmt.Sprintf("%s/%d/dbs/%s", base, srcID, name), + RPs: fmt.Sprintf("%s/%d/dbs/%s/rps", base, srcID, name), }, } } @@ -49,6 +51,7 @@ type rpResponse struct { Links rpLinks `json:"links"` // Links are URI locations related to the database } +// WithLinks adds links to an rpResponse in place func (r *rpResponse) WithLinks(srcID int, dbName string) { base := "/chronograf/v1/sources" r.Links = rpLinks{ @@ -60,7 +63,7 @@ type rpsResponse struct { RetentionPolicies []rpResponse `json:"retentionPolicies"` } -// Databases queries the list of all databases for a source +// GetDatabases queries the list of all databases for a source func (h *Service) GetDatabases(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -101,6 +104,7 @@ func (h *Service) GetDatabases(w http.ResponseWriter, r *http.Request) { encodeJSON(w, http.StatusOK, res, h.Logger) } +// NewDatabase creates a new database within the datastore func (h *Service) NewDatabase(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -145,6 +149,7 @@ func (h *Service) NewDatabase(w http.ResponseWriter, r *http.Request) { encodeJSON(w, http.StatusCreated, res, h.Logger) } +// DropDatabase removes a database from a data source func (h *Service) DropDatabase(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -179,6 +184,7 @@ func (h *Service) DropDatabase(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) } +// RetentionPolicies lists retention policies within a database func (h *Service) RetentionPolicies(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -195,7 +201,6 @@ func (h *Service) RetentionPolicies(w http.ResponseWriter, r *http.Request) { } db := h.Databases - if err = db.Connect(ctx, &src); err != nil { msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err) Error(w, http.StatusBadRequest, msg, h.Logger) @@ -203,7 +208,6 @@ func (h *Service) RetentionPolicies(w http.ResponseWriter, r *http.Request) { } dbID := httprouter.GetParamFromContext(ctx, "dbid") - allRP, err := db.AllRP(ctx, dbID) if err != nil { Error(w, http.StatusBadRequest, err.Error(), h.Logger) @@ -230,6 +234,7 @@ func (h *Service) RetentionPolicies(w http.ResponseWriter, r *http.Request) { encodeJSON(w, http.StatusOK, res, h.Logger) } +// NewRetentionPolicy creates a new retention policy for a database func (h *Service) NewRetentionPolicy(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -246,7 +251,6 @@ func (h *Service) NewRetentionPolicy(w http.ResponseWriter, r *http.Request) { } db := h.Databases - if err = db.Connect(ctx, &src); err != nil { msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err) Error(w, http.StatusBadRequest, msg, h.Logger) @@ -264,7 +268,6 @@ func (h *Service) NewRetentionPolicy(w http.ResponseWriter, r *http.Request) { } dbID := httprouter.GetParamFromContext(ctx, "dbid") - database, err := db.CreateRP(ctx, dbID, postedRP) if err != nil { Error(w, http.StatusBadRequest, err.Error(), h.Logger) @@ -275,6 +278,7 @@ func (h *Service) NewRetentionPolicy(w http.ResponseWriter, r *http.Request) { encodeJSON(w, http.StatusCreated, res, h.Logger) } +// UpdateRetentionPolicy modifies an existing retention policy for a database func (h *Service) UpdateRetentionPolicy(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -291,7 +295,6 @@ func (h *Service) UpdateRetentionPolicy(w http.ResponseWriter, r *http.Request) } db := h.Databases - if err = db.Connect(ctx, &src); err != nil { msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err) Error(w, http.StatusBadRequest, msg, h.Logger) @@ -310,7 +313,6 @@ func (h *Service) UpdateRetentionPolicy(w http.ResponseWriter, r *http.Request) dbID := httprouter.GetParamFromContext(ctx, "dbid") rpID := httprouter.GetParamFromContext(ctx, "rpid") - rp, err := db.UpdateRP(ctx, dbID, rpID, postedRP) if err != nil { @@ -323,6 +325,7 @@ func (h *Service) UpdateRetentionPolicy(w http.ResponseWriter, r *http.Request) encodeJSON(w, http.StatusCreated, res, h.Logger) } +// DropRetentionPolicy removes a retention policy from a database func (h *Service) DropRetentionPolicy(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -339,7 +342,6 @@ func (h *Service) DropRetentionPolicy(w http.ResponseWriter, r *http.Request) { } db := h.Databases - if err = db.Connect(ctx, &src); err != nil { msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err) Error(w, http.StatusBadRequest, msg, h.Logger) @@ -348,7 +350,6 @@ func (h *Service) DropRetentionPolicy(w http.ResponseWriter, r *http.Request) { dbID := httprouter.GetParamFromContext(ctx, "dbid") rpID := httprouter.GetParamFromContext(ctx, "rpid") - dropErr := db.DropRP(ctx, dbID, rpID) if dropErr != nil { Error(w, http.StatusBadRequest, dropErr.Error(), h.Logger) @@ -358,6 +359,7 @@ func (h *Service) DropRetentionPolicy(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) } +// ValidDatabaseRequest checks if the database posted is valid func ValidDatabaseRequest(d *chronograf.Database) error { if len(d.Name) == 0 { return fmt.Errorf("name is required") @@ -365,6 +367,7 @@ func ValidDatabaseRequest(d *chronograf.Database) error { return nil } +// ValidRetentionPolicyRequest checks if a retention policy is valid on POST func ValidRetentionPolicyRequest(rp *chronograf.RetentionPolicy) error { if len(rp.Name) == 0 { return fmt.Errorf("name is required") @@ -375,6 +378,5 @@ func ValidRetentionPolicyRequest(rp *chronograf.RetentionPolicy) error { if rp.Replication == 0 { return fmt.Errorf("replication factor is invalid") } - return nil } From 65e93125dd16c461cdcbfd062f2783d001e50eea Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Thu, 23 Mar 2017 13:33:46 -0700 Subject: [PATCH 73/95] Fix drop database InfluxQL statement --- influx/databases.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/influx/databases.go b/influx/databases.go index b188ed488..34ae189e8 100644 --- a/influx/databases.go +++ b/influx/databases.go @@ -33,7 +33,7 @@ func (c *Client) CreateDB(ctx context.Context, db *chronograf.Database) (*chrono func (c *Client) DropDB(ctx context.Context, database string) error { _, err := c.Query(ctx, chronograf.Query{ - Command: fmt.Sprintf(`DROP DATABASE`), + Command: fmt.Sprintf(`DROP DATABASE "%s"`, database), DB: database, }) if err != nil { From 83ee057f562fe0f67a9312fe78a5067008e4fb68 Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Thu, 23 Mar 2017 13:34:09 -0700 Subject: [PATCH 74/95] Fix exported method to be internal --- server/databases.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/databases.go b/server/databases.go index c58b9456e..7117ec225 100644 --- a/server/databases.go +++ b/server/databases.go @@ -22,8 +22,8 @@ type dbResponse struct { Links dbLinks `json:"links"` // Links are URI locations related to the database } -// NewDBResponse creates the response for the /databases endpoint -func NewDBResponse(srcID int, name string) dbResponse { +// newDBResponse creates the response for the /databases endpoint +func newDBResponse(srcID int, name string) dbResponse { base := "/chronograf/v1/sources" return dbResponse{ Name: name, @@ -94,7 +94,7 @@ func (h *Service) GetDatabases(w http.ResponseWriter, r *http.Request) { dbs := make([]dbResponse, len(databases)) for i, d := range databases { - dbs[i] = NewDBResponse(srcID, d.Name) + dbs[i] = newDBResponse(srcID, d.Name) } res := dbsResponse{ @@ -145,7 +145,7 @@ func (h *Service) NewDatabase(w http.ResponseWriter, r *http.Request) { return } - res := NewDBResponse(srcID, database.Name) + res := newDBResponse(srcID, database.Name) encodeJSON(w, http.StatusCreated, res, h.Logger) } From 768bd12541c3b12561c7b75f2f4df62b771cd379 Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Thu, 23 Mar 2017 13:53:28 -0700 Subject: [PATCH 75/95] Add retention policies to the database response --- influx/databases.go | 9 ++++++++- server/databases.go | 49 ++++++++++++++++++++++++++++++--------------- server/service.go | 2 +- 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/influx/databases.go b/influx/databases.go index 34ae189e8..2e8bc9cb2 100644 --- a/influx/databases.go +++ b/influx/databases.go @@ -9,6 +9,7 @@ import ( "github.com/influxdata/chronograf" ) +// AllDB returns all databases from within Influx func (c *Client) AllDB(ctx context.Context) ([]chronograf.Database, error) { databases, err := c.showDatabases(ctx) if err != nil { @@ -18,6 +19,7 @@ func (c *Client) AllDB(ctx context.Context) ([]chronograf.Database, error) { return databases, nil } +// CreateDB creates a database within Influx func (c *Client) CreateDB(ctx context.Context, db *chronograf.Database) (*chronograf.Database, error) { _, err := c.Query(ctx, chronograf.Query{ Command: fmt.Sprintf(`CREATE DATABASE "%s"`, db.Name), @@ -31,6 +33,7 @@ func (c *Client) CreateDB(ctx context.Context, db *chronograf.Database) (*chrono return res, nil } +// DropDB drops a database within Influx func (c *Client) DropDB(ctx context.Context, database string) error { _, err := c.Query(ctx, chronograf.Query{ Command: fmt.Sprintf(`DROP DATABASE "%s"`, database), @@ -42,6 +45,7 @@ func (c *Client) DropDB(ctx context.Context, database string) error { return nil } +// AllRP returns all the retention policies for a specific database func (c *Client) AllRP(ctx context.Context, database string) ([]chronograf.RetentionPolicy, error) { retentionPolicies, err := c.showRetentionPolicies(ctx, database) if err != nil { @@ -51,6 +55,7 @@ func (c *Client) AllRP(ctx context.Context, database string) ([]chronograf.Reten return retentionPolicies, nil } +// CreateRP creates a retention policy for a specific database func (c *Client) CreateRP(ctx context.Context, database string, rp *chronograf.RetentionPolicy) (*chronograf.RetentionPolicy, error) { _, err := c.Query(ctx, chronograf.Query{ Command: fmt.Sprintf(`CREATE RETENTION POLICY "%s" ON "%s" DURATION %s REPLICATION %d`, rp.Name, database, rp.Duration, rp.Replication), @@ -69,6 +74,7 @@ func (c *Client) CreateRP(ctx context.Context, database string, rp *chronograf.R return res, nil } +// UpdateRP updates a specific retention policy for a specific database func (c *Client) UpdateRP(ctx context.Context, database string, name string, rp *chronograf.RetentionPolicy) (*chronograf.RetentionPolicy, error) { var buffer bytes.Buffer buffer.WriteString("ALTER RETENTION POLICY") @@ -102,6 +108,7 @@ func (c *Client) UpdateRP(ctx context.Context, database string, name string, rp return res, nil } +// DropRP removes a specific retention policy for a specific database func (c *Client) DropRP(ctx context.Context, database string, rp string) error { _, err := c.Query(ctx, chronograf.Query{ Command: fmt.Sprintf(`DROP RETENTION POLICY`), @@ -136,7 +143,7 @@ func (c *Client) showDatabases(ctx context.Context) ([]chronograf.Database, erro func (c *Client) showRetentionPolicies(ctx context.Context, name string) ([]chronograf.RetentionPolicy, error) { retentionPolicies, err := c.Query(ctx, chronograf.Query{ - Command: fmt.Sprintf(`SHOW RETENTION POLICIES`), + Command: fmt.Sprintf(`SHOW RETENTION POLICIES ON "%s"`, name), DB: name, }) diff --git a/server/databases.go b/server/databases.go index 7117ec225..cb8906877 100644 --- a/server/databases.go +++ b/server/databases.go @@ -1,6 +1,7 @@ package server import ( + "context" "encoding/json" "fmt" "net/http" @@ -15,18 +16,20 @@ type dbLinks struct { } type dbResponse struct { - Name string `json:"name"` // a unique string identifier for the database - Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) - Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy) - ShardDuration string `json:"shardDuration,omitempty"` // the shard duration (when creating a default retention policy) - Links dbLinks `json:"links"` // Links are URI locations related to the database + Name string `json:"name"` // a unique string identifier for the database + Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) + Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy) + ShardDuration string `json:"shardDuration,omitempty"` // the shard duration (when creating a default retention policy) + RPs []rpResponse `json:"retentionPolicies,omitempty"` // RPs are the retention policies for a database + Links dbLinks `json:"links"` // Links are URI locations related to the database } // newDBResponse creates the response for the /databases endpoint -func newDBResponse(srcID int, name string) dbResponse { +func newDBResponse(srcID int, name string, rps []rpResponse) dbResponse { base := "/chronograf/v1/sources" return dbResponse{ Name: name, + RPs: rps, Links: dbLinks{ Self: fmt.Sprintf("%s/%d/dbs/%s", base, srcID, name), RPs: fmt.Sprintf("%s/%d/dbs/%s/rps", base, srcID, name), @@ -94,7 +97,12 @@ func (h *Service) GetDatabases(w http.ResponseWriter, r *http.Request) { dbs := make([]dbResponse, len(databases)) for i, d := range databases { - dbs[i] = newDBResponse(srcID, d.Name) + rps, err := h.allRPs(ctx, db, srcID, d.Name) + if err != nil { + Error(w, http.StatusBadRequest, err.Error(), h.Logger) + return + } + dbs[i] = newDBResponse(srcID, d.Name, rps) } res := dbsResponse{ @@ -145,7 +153,12 @@ func (h *Service) NewDatabase(w http.ResponseWriter, r *http.Request) { return } - res := newDBResponse(srcID, database.Name) + rps, err := h.allRPs(ctx, db, srcID, database.Name) + if err != nil { + Error(w, http.StatusBadRequest, err.Error(), h.Logger) + return + } + res := newDBResponse(srcID, database.Name, rps) encodeJSON(w, http.StatusCreated, res, h.Logger) } @@ -208,10 +221,19 @@ func (h *Service) RetentionPolicies(w http.ResponseWriter, r *http.Request) { } dbID := httprouter.GetParamFromContext(ctx, "dbid") + res, err := h.allRPs(ctx, db, srcID, dbID) + if err != nil { + msg := fmt.Sprintf("Unable to connect get RPs %d: %v", srcID, err) + Error(w, http.StatusBadRequest, msg, h.Logger) + return + } + encodeJSON(w, http.StatusOK, res, h.Logger) +} + +func (h *Service) allRPs(ctx context.Context, db chronograf.Databases, srcID int, dbID string) ([]rpResponse, error) { allRP, err := db.AllRP(ctx, dbID) if err != nil { - Error(w, http.StatusBadRequest, err.Error(), h.Logger) - return + return nil, err } rps := make([]rpResponse, len(allRP)) @@ -226,12 +248,7 @@ func (h *Service) RetentionPolicies(w http.ResponseWriter, r *http.Request) { rp.WithLinks(srcID, dbID) rps[i] = rp } - - res := rpsResponse{ - RetentionPolicies: rps, - } - - encodeJSON(w, http.StatusOK, res, h.Logger) + return rps, nil } // NewRetentionPolicy creates a new retention policy for a database diff --git a/server/service.go b/server/service.go index 475940d4c..3da9f2d64 100644 --- a/server/service.go +++ b/server/service.go @@ -20,7 +20,7 @@ type Service struct { TimeSeriesClient TimeSeriesClient Logger chronograf.Logger UseAuth bool - Databases chronograf.Databases + Databases chronograf.Databases } // TimeSeriesClient returns the correct client for a time series database. From b061369a56840feaeda911c69ac14ebca7367e39 Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Thu, 23 Mar 2017 13:59:28 -0700 Subject: [PATCH 76/95] Fix drop retention policy InfluxQL --- influx/databases.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/influx/databases.go b/influx/databases.go index 2e8bc9cb2..bad62a6ce 100644 --- a/influx/databases.go +++ b/influx/databases.go @@ -111,7 +111,7 @@ func (c *Client) UpdateRP(ctx context.Context, database string, name string, rp // DropRP removes a specific retention policy for a specific database func (c *Client) DropRP(ctx context.Context, database string, rp string) error { _, err := c.Query(ctx, chronograf.Query{ - Command: fmt.Sprintf(`DROP RETENTION POLICY`), + Command: fmt.Sprintf(`DROP RETENTION POLICY "%s" ON "%s"`, rp, database), DB: database, RP: rp, }) From 98799cc44268d734cb7ad4dd3cc635ec36b0cb52 Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Thu, 23 Mar 2017 14:06:52 -0700 Subject: [PATCH 77/95] Fix sources test to include databases link --- server/sources_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/sources_test.go b/server/sources_test.go index 729d9a504..23d79a07e 100644 --- a/server/sources_test.go +++ b/server/sources_test.go @@ -30,6 +30,7 @@ func Test_newSourceResponse(t *testing.T) { Kapacitors: "/chronograf/v1/sources/1/kapacitors", Users: "/chronograf/v1/sources/1/users", Permissions: "/chronograf/v1/sources/1/permissions", + Databases: "/chronograf/v1/sources/1/dbs", }, }, }, @@ -50,6 +51,7 @@ func Test_newSourceResponse(t *testing.T) { Kapacitors: "/chronograf/v1/sources/1/kapacitors", Users: "/chronograf/v1/sources/1/users", Permissions: "/chronograf/v1/sources/1/permissions", + Databases: "/chronograf/v1/sources/1/dbs", }, }, }, From 2c874e4e533b4224181c8bafc2d14a64025602fe Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Thu, 23 Mar 2017 14:30:24 -0700 Subject: [PATCH 78/95] Fix retention policies responses and change to PATCH --- influx/databases.go | 33 +++++++++++++++++++++++---------- server/databases.go | 22 +++++++++++++++++----- server/mux.go | 2 +- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/influx/databases.go b/influx/databases.go index bad62a6ce..a508fdba2 100644 --- a/influx/databases.go +++ b/influx/databases.go @@ -55,6 +55,20 @@ func (c *Client) AllRP(ctx context.Context, database string) ([]chronograf.Reten return retentionPolicies, nil } +func (c *Client) getRP(ctx context.Context, db, name string) (chronograf.RetentionPolicy, error) { + rps, err := c.AllRP(ctx, db) + if err != nil { + return chronograf.RetentionPolicy{}, err + } + + for _, rp := range rps { + if rp.Name == name { + return rp, nil + } + } + return chronograf.RetentionPolicy{}, fmt.Errorf("unknown retention policy") +} + // CreateRP creates a retention policy for a specific database func (c *Client) CreateRP(ctx context.Context, database string, rp *chronograf.RetentionPolicy) (*chronograf.RetentionPolicy, error) { _, err := c.Query(ctx, chronograf.Query{ @@ -65,19 +79,18 @@ func (c *Client) CreateRP(ctx context.Context, database string, rp *chronograf.R return nil, err } - res := &chronograf.RetentionPolicy{ - Name: rp.Name, - Duration: rp.Duration, - Replication: rp.Replication, + res, err := c.getRP(ctx, database, rp.Name) + if err != nil { + return nil, err } - return res, nil + return &res, nil } // UpdateRP updates a specific retention policy for a specific database func (c *Client) UpdateRP(ctx context.Context, database string, name string, rp *chronograf.RetentionPolicy) (*chronograf.RetentionPolicy, error) { var buffer bytes.Buffer - buffer.WriteString("ALTER RETENTION POLICY") + buffer.WriteString(fmt.Sprintf(`ALTER RETENTION POLICY "%s" ON "%s"`, name, database)) if len(rp.Duration) > 0 { buffer.WriteString(" DURATION " + rp.Duration) } @@ -100,12 +113,12 @@ func (c *Client) UpdateRP(ctx context.Context, database string, name string, rp return nil, err } - // TODO: use actual information here - res := &chronograf.RetentionPolicy{ - Name: name, + res, err := c.getRP(ctx, database, rp.Name) + if err != nil { + return nil, err } - return res, nil + return &res, nil } // DropRP removes a specific retention policy for a specific database diff --git a/server/databases.go b/server/databases.go index cb8906877..6a0d1496d 100644 --- a/server/databases.go +++ b/server/databases.go @@ -285,13 +285,19 @@ func (h *Service) NewRetentionPolicy(w http.ResponseWriter, r *http.Request) { } dbID := httprouter.GetParamFromContext(ctx, "dbid") - database, err := db.CreateRP(ctx, dbID, postedRP) + rp, err := db.CreateRP(ctx, dbID, postedRP) if err != nil { Error(w, http.StatusBadRequest, err.Error(), h.Logger) return } - - res := dbResponse{Name: database.Name} + res := rpResponse{ + Name: rp.Name, + Duration: rp.Duration, + Replication: rp.Replication, + ShardDuration: rp.ShardDuration, + Default: rp.Default, + } + res.WithLinks(srcID, dbID) encodeJSON(w, http.StatusCreated, res, h.Logger) } @@ -337,8 +343,14 @@ func (h *Service) UpdateRetentionPolicy(w http.ResponseWriter, r *http.Request) return } - // TODO: this needs to be the actual RP information - res := rpResponse{Name: rp.Name} + res := rpResponse{ + Name: rp.Name, + Duration: rp.Duration, + Replication: rp.Replication, + ShardDuration: rp.ShardDuration, + Default: rp.Default, + } + res.WithLinks(srcID, dbID) encodeJSON(w, http.StatusCreated, res, h.Logger) } diff --git a/server/mux.go b/server/mux.go index b655fa0de..82d047dad 100644 --- a/server/mux.go +++ b/server/mux.go @@ -141,7 +141,7 @@ func NewMux(opts MuxOpts, service Service) http.Handler { router.GET("/chronograf/v1/sources/:id/dbs/:dbid/rps", service.RetentionPolicies) router.POST("/chronograf/v1/sources/:id/dbs/:dbid/rps", service.NewRetentionPolicy) - router.PATCH("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", service.UpdateRetentionPolicy) + router.PUT("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", service.UpdateRetentionPolicy) router.DELETE("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", service.DropRetentionPolicy) var authRoutes AuthRoutes From 9280741773e92a26f4e9230d1a2c09246450cbd3 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 23 Mar 2017 15:06:22 -0700 Subject: [PATCH 79/95] Hook up DBRP manager to backend --- ui/src/admin/actions/index.js | 78 ++++++++++--------- ui/src/admin/apis/index.js | 42 +++++++++- ui/src/admin/components/DatabaseManager.js | 3 + ui/src/admin/components/DatabaseRow.js | 33 ++++++-- ui/src/admin/components/DatabaseTable.js | 3 + .../admin/containers/DatabaseManagerPage.js | 18 +++-- ui/src/admin/reducers/admin.js | 13 ++++ 7 files changed, 136 insertions(+), 54 deletions(-) diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index 7cfb9894e..25f83c5e2 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -1,25 +1,25 @@ -import uuid from 'node-uuid'; - import { getUsers as getUsersAJAX, getRoles as getRolesAJAX, getPermissions as getPermissionsAJAX, + getDbsAndRps as getDbsAndRpsAJAX, createUser as createUserAJAX, createRole as createRoleAJAX, + createDatabase as createDatabaseAJAX, + createRetentionPolicy as createRetentionPolicyAJAX, deleteUser as deleteUserAJAX, deleteRole as deleteRoleAJAX, + deleteDatabase as deleteDatabaseAJAX, + deleteRetentionPolicy as deleteRetentionPolicyAJAX, updateRole as updateRoleAJAX, updateUser as updateUserAJAX, + updateRetentionPolicy as updateRetentionPolicyAJAX, } from 'src/admin/apis' import { killQuery as killQueryProxy, - showDatabases, - showRetentionPolicies, } 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 {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) => ({ type: 'EDIT_USER', payload: { @@ -224,19 +234,8 @@ export const loadPermissionsAsync = (url) => async (dispatch) => { } export const loadDBsAndRPsAsync = (url) => async (dispatch) => { - const {data: dbs} = await showDatabases(url) - const {databases} = parseShowDatabases(dbs) - - 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)) + const {data: {databases}} = await getDbsAndRpsAJAX(url) + dispatch(loadDatabases(databases)) } 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) => { 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(syncDatabase(database, {...data, id: uuid.v4()})) } catch (error) { // undo optimistic update 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 { - // TODO: implement once server is up - // const {data} = await createRetentionPolicyAJAX(url, retentionPolicy) + const {data} = await createRetentionPolicyAJAX(database.links.retentionPolicies, retentionPolicy) dispatch(publishNotification('success', 'Retention policy created successfully')) - // dispatch(syncRetentionPolicy(retentionPolicy, {...data, id: uuid.v4()})) + dispatch(syncRetentionPolicy(database, retentionPolicy, data)) } catch (error) { // undo optimistic update 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) => { try { - // TODO: implement once server is up 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(syncRetentionPolicy(retentionPolicy, {...data, id: uuid.v4()})) + dispatch(syncRetentionPolicy(database, data)) } catch (error) { 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) } -export const deleteDatabaseAsync = (url, database) => (dispatch) => { +export const deleteDatabaseAsync = (database) => async (dispatch) => { dispatch(removeDatabase(database)) 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 - // try { - // await deleteDatabaseAJAX(url, database.name) - // } catch (error) { - // dispatch(publishNotification('error', `Failed to delete database: ${error.data.message}`)) - // } +export const deleteRetentionPolicyAsync = (database, retentionPolicy) => async (dispatch) => { + dispatch(removeRetentionPolicy(database, retentionPolicy)) + dispatch(publishNotification('success', `Retention policy ${retentionPolicy.name} deleted`)) + try { + await deleteRetentionPolicyAJAX(retentionPolicy.links.self) + } catch (error) { + dispatch(publishNotification('error', `Failed to delete retentionPolicy: ${error.data.message}`)) + } } export const updateRoleUsersAsync = (role, users) => async (dispatch) => { diff --git a/ui/src/admin/apis/index.js b/ui/src/admin/apis/index.js index 0d90536a0..5e372bca8 100644 --- a/ui/src/admin/apis/index.js +++ b/ui/src/admin/apis/index.js @@ -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) => { try { 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) => { try { 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 { return await AJAX({ method: 'DELETE', - url: `${url}/${name}`, + url, }) } catch (error) { console.error(error) @@ -167,3 +190,18 @@ export const updateUser = async (url, roles, permissions) => { throw error } } + +export const updateRetentionPolicy = async (url, retentionPolicy) => { + try { + return await AJAX({ + method: 'PUT', + url, + data: { + retentionPolicy, + }, + }) + } catch (error) { + console.error(error) + throw error + } +} diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index b9053052c..a203ca107 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -18,6 +18,7 @@ const DatabaseManager = ({ onCreateRetentionPolicy, onUpdateRetentionPolicy, onRemoveRetentionPolicy, + onDeleteRetentionPolicy, }) => { return (
@@ -45,6 +46,7 @@ const DatabaseManager = ({ onCreateRetentionPolicy={onCreateRetentionPolicy} onUpdateRetentionPolicy={onUpdateRetentionPolicy} onRemoveRetentionPolicy={onRemoveRetentionPolicy} + onDeleteRetentionPolicy={onDeleteRetentionPolicy} /> ) } @@ -78,6 +80,7 @@ DatabaseManager.propTypes = { onCreateRetentionPolicy: func, onUpdateRetentionPolicy: func, onRemoveRetentionPolicy: func, + onDeleteRetentionPolicy: func, } export default DatabaseManager diff --git a/ui/src/admin/components/DatabaseRow.js b/ui/src/admin/components/DatabaseRow.js index 777e62414..1ae721e10 100644 --- a/ui/src/admin/components/DatabaseRow.js +++ b/ui/src/admin/components/DatabaseRow.js @@ -8,6 +8,7 @@ class DatabaseRow extends Component { super(props) this.state = { isEditing: false, + isDeleting: false, } this.handleKeyDown = ::this.handleKeyDown this.handleClickOutside = ::this.handleClickOutside @@ -16,6 +17,8 @@ class DatabaseRow extends Component { this.handleCreate = ::this.handleCreate this.handleUpdate = ::this.handleUpdate this.getInputValues = ::this.getInputValues + this.handleStartDelete = ::this.handleStartDelete + this.handleEndDelete = ::this.handleEndDelete } componentWillMount() { @@ -30,12 +33,14 @@ class DatabaseRow extends Component { retentionPolicy: {name, duration, replication, isDefault, isNew}, retentionPolicy, database, + onDelete, isRFDisplayed, } = this.props + const {isEditing, isDeleting} = this.state const formattedDuration = formatInfiniteDuration(duration) - if (this.state.isEditing) { + if (isEditing) { return (
{isRFDisplayed ? : null} ) @@ -105,6 +114,7 @@ class DatabaseRow extends Component { handleClickOutside() { this.handleEndEdit() + this.handleEndDelete() } handleStartEdit() { @@ -115,14 +125,22 @@ class DatabaseRow extends Component { this.setState({isEditing: false}) } + handleStartDelete() { + this.setState({isDeleting: true}) + } + + handleEndDelete() { + this.setState({isDeleting: false}) + } + handleCreate() { - const {database, onCreate} = this.props + const {database, retentionPolicy, onCreate} = this.props const validInputs = this.getInputValues() if (!validInputs) { return } - onCreate(database, validInputs) + onCreate(database, {...retentionPolicy, ...validInputs}) this.handleEndEdit() } @@ -205,6 +223,7 @@ DatabaseRow.propTypes = { onRemove: func, onCreate: func, onUpdate: func, + onDelete: func, notify: func, isRFDisplayed: bool, } diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index 6ea510fcb..6f034d6c6 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -22,6 +22,7 @@ const DatabaseTable = ({ onCreateRetentionPolicy, onUpdateRetentionPolicy, onRemoveRetentionPolicy, + onDeleteRetentionPolicy, }) => { return (
@@ -57,6 +58,7 @@ const DatabaseTable = ({ onCreate={onCreateRetentionPolicy} onUpdate={onUpdateRetentionPolicy} onRemove={onRemoveRetentionPolicy} + onDelete={onDeleteRetentionPolicy} isRFDisplayed={isRFDisplayed} /> ) @@ -84,6 +86,7 @@ DatabaseTable.propTypes = { onCreateRetentionPolicy: func, onUpdateRetentionPolicy: func, onRemoveRetentionPolicy: func, + onDeleteRetentionPolicy: func, } const DatabaseTableHeader = ({ diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index 1104368ed..e8c22748c 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -15,9 +15,9 @@ class DatabaseManagerPage extends Component { } componentDidMount() { - const {source: {links: {proxy}}, actions} = this.props + const {source: {links: {databases}}, actions} = this.props - actions.loadDBsAndRPsAsync(proxy) + actions.loadDBsAndRPsAsync(databases) } render() { @@ -39,23 +39,24 @@ class DatabaseManagerPage extends Component { onCreateRetentionPolicy={actions.createRetentionPolicyAsync} onUpdateRetentionPolicy={actions.updateRetentionPolicyAsync} onRemoveRetentionPolicy={actions.removeRetentionPolicy} + onDeleteRetentionPolicy={actions.deleteRetentionPolicyAsync} /> ) } handleCreateDatabase(database) { - const {actions, notify} = this.props + const {actions, notify, source} = this.props if (!database.name) { return notify('error', 'Database name cannot be blank') } - actions.createDatabaseAsync(database) + actions.createDatabaseAsync(source.links.databases, database) } handleKeyDownDatabase(e, database) { const {key} = e - const {actions, notify} = this.props + const {actions, notify, source} = this.props if (key === 'Escape') { actions.removeDatabase(database) @@ -66,13 +67,13 @@ class DatabaseManagerPage extends Component { return notify('error', 'Database name cannot be blank') } - actions.createDatabaseAsync(database) + actions.createDatabaseAsync(source.links.databases, database) } } handleDatabaseDeleteConfirm(database, e) { const {key, target: {value}} = e - const {actions, source, notify} = this.props + const {actions, notify} = this.props if (key === 'Escape') { return actions.removeDatabaseDeleteCode(database) @@ -83,7 +84,7 @@ class DatabaseManagerPage extends Component { return notify('error', `Please type DELETE ${database.name} to confirm`) } - return actions.deleteDatabaseAsync(source, database) + return actions.deleteDatabaseAsync(database) } actions.editDatabase(database, {deleteCode: value}) @@ -125,6 +126,7 @@ DatabaseManagerPage.propTypes = { startDeleteDatabase: func, removeDatabaseDeleteCode: func, removeRetentionPolicy: func, + deleteRetentionPolicyAsync: func, }), notify: func, } diff --git a/ui/src/admin/reducers/admin.js b/ui/src/admin/reducers/admin.js index 2f607add9..88642ae4a 100644 --- a/ui/src/admin/reducers/admin.js +++ b/ui/src/admin/reducers/admin.js @@ -99,6 +99,19 @@ export default function admin(state = initialState, action) { const newState = { 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} } From a502ff3e3760e9d87577145222b764c41bea2bd4 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 23 Mar 2017 15:11:03 -0700 Subject: [PATCH 80/95] Remove noops from confirm db buttons --- ui/src/admin/components/DatabaseTable.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index 6f034d6c6..abbd427cc 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -117,6 +117,8 @@ const DatabaseTableHeader = ({ onStartDelete={onStartDelete} onDatabaseDeleteConfirm={onDatabaseDeleteConfirm} onAddRetentionPolicy={onAddRetentionPolicy} + onConfirm={onConfirm} + onCancel={onCancel} /> ) } @@ -137,6 +139,8 @@ const Header = ({ onStartDelete, onDatabaseDeleteConfirm, onAddRetentionPolicy, + onCancel, + onConfirm, }) => { const confirmStyle = { display: 'flex', @@ -169,7 +173,7 @@ const Header = ({ autoFocus={true} />
- {}} onCancel={() => {}} /> + ) @@ -186,6 +190,8 @@ Header.propTypes = { onStartDelete: func, onDatabaseDeleteConfirm: func, onAddRetentionPolicy: func, + onConfirm: func, + onCancel: func, } const EditHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel}) => ( From c252c6126c56f18918bf402dacd908d07e619b10 Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Thu, 23 Mar 2017 15:23:54 -0700 Subject: [PATCH 81/95] Fix create retention policies InfluxQL --- influx/databases.go | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/influx/databases.go b/influx/databases.go index a508fdba2..6cff80129 100644 --- a/influx/databases.go +++ b/influx/databases.go @@ -71,8 +71,17 @@ func (c *Client) getRP(ctx context.Context, db, name string) (chronograf.Retenti // CreateRP creates a retention policy for a specific database func (c *Client) CreateRP(ctx context.Context, database string, rp *chronograf.RetentionPolicy) (*chronograf.RetentionPolicy, error) { + query := fmt.Sprintf(`CREATE RETENTION POLICY "%s" ON "%s" DURATION %s REPLICATION %d`, rp.Name, database, rp.Duration, rp.Replication) + if len(rp.ShardDuration) != 0 { + query = fmt.Sprintf(`%s SHARD DURATION %s`, query, rp.ShardDuration) + } + + if rp.Default { + query = fmt.Sprintf(`%s DEFAULT`, query) + } + _, err := c.Query(ctx, chronograf.Query{ - Command: fmt.Sprintf(`CREATE RETENTION POLICY "%s" ON "%s" DURATION %s REPLICATION %d`, rp.Name, database, rp.Duration, rp.Replication), + Command: query, DB: database, }) if err != nil { @@ -103,8 +112,7 @@ func (c *Client) UpdateRP(ctx context.Context, database string, name string, rp if rp.Default == true { buffer.WriteString(" DEFAULT") } - - _, err := c.Query(ctx, chronograf.Query{ + queryRes, err := c.Query(ctx, chronograf.Query{ Command: buffer.String(), DB: database, RP: name, @@ -113,6 +121,25 @@ func (c *Client) UpdateRP(ctx context.Context, database string, name string, rp return nil, err } + // The ALTER RETENTION POLICIES statements puts the error within the results itself + // So, we have to crack open the results to see what happens + octets, err := queryRes.MarshalJSON() + if err != nil { + return nil, err + } + + results := make([]struct{ Error string }, 0) + if err := json.Unmarshal(octets, &results); err != nil { + return nil, err + } + + // At last, we can check if there are any error strings + for _, r := range results { + if r.Error != "" { + return nil, fmt.Errorf(r.Error) + } + } + res, err := c.getRP(ctx, database, rp.Name) if err != nil { return nil, err From 05bdb193ef687a87e49c77670026ca61d2036153 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 23 Mar 2017 15:36:48 -0700 Subject: [PATCH 82/95] Add database naming styles --- ui/src/admin/components/DatabaseTable.js | 26 ++++++++++-------------- ui/src/style/pages/admin.scss | 19 +++++++++++++++++ 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index abbd427cc..8fa901c28 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -195,21 +195,17 @@ Header.propTypes = { } const EditHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel}) => ( -
-

-
- onEdit(database, {name: e.target.value})} - onKeyDown={(e) => onKeyDown(e, database)} - autoFocus={true} - /> -
-

+
+ onEdit(database, {name: e.target.value})} + onKeyDown={(e) => onKeyDown(e, database)} + autoFocus={true} + />
) diff --git a/ui/src/style/pages/admin.scss b/ui/src/style/pages/admin.scss index d7975b00c..b20dcf09d 100644 --- a/ui/src/style/pages/admin.scss +++ b/ui/src/style/pages/admin.scss @@ -154,6 +154,25 @@ & .btn {display: none;} &:hover .btn {display: inline-block;} } +.db-manager-header-edit { + height: 32px; + display: flex; + align-items: center; + justify-content: flex-start; + margin-bottom: 11px; + + > .form-control { + width: 300px; + margin-right: 8px; + } + .btn { + height: 40px; + width: 40px; + padding: 0; + margin: 0 2px 0 0; + font-size: 18px; + } +} .db-manager { margin-top: 18px; } From 7c393d6159b7e4221d7492e8eb9b347c0c1ec8dc Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 23 Mar 2017 15:58:28 -0700 Subject: [PATCH 83/95] Disable creation of DB when currently creating one --- ui/src/admin/components/DatabaseManager.js | 7 +++---- ui/src/admin/containers/DatabaseManagerPage.js | 4 +++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index a203ca107..764e3d7d9 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -5,6 +5,7 @@ const DatabaseManager = ({ databases, notify, isRFDisplayed, + isCreateDBDisabled, addDatabase, onEditDatabase, onKeyDownDatabase, @@ -18,13 +19,12 @@ const DatabaseManager = ({ onCreateRetentionPolicy, onUpdateRetentionPolicy, onRemoveRetentionPolicy, - onDeleteRetentionPolicy, }) => { return (

{databases.length === 1 ? '1 Database' : `${databases.length} Databases`}

-
Create Database
+
Create Database
{ @@ -46,7 +46,6 @@ const DatabaseManager = ({ onCreateRetentionPolicy={onCreateRetentionPolicy} onUpdateRetentionPolicy={onUpdateRetentionPolicy} onRemoveRetentionPolicy={onRemoveRetentionPolicy} - onDeleteRetentionPolicy={onDeleteRetentionPolicy} /> ) } @@ -67,6 +66,7 @@ DatabaseManager.propTypes = { notify: func, addDatabase: func, isRFDisplayed: bool, + isCreateDBDisabled: bool, onEditDatabase: func, onKeyDownDatabase: func, onCancelDatabase: func, @@ -80,7 +80,6 @@ DatabaseManager.propTypes = { onCreateRetentionPolicy: func, onUpdateRetentionPolicy: func, onRemoveRetentionPolicy: func, - onDeleteRetentionPolicy: func, } export default DatabaseManager diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index e8c22748c..c86a61d65 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -11,7 +11,7 @@ class DatabaseManagerPage extends Component { super(props) this.handleKeyDownDatabase = ::this.handleKeyDownDatabase this.handleDatabaseDeleteConfirm = ::this.handleDatabaseDeleteConfirm - this.handleCreateDatabase = :: this.handleCreateDatabase + this.handleCreateDatabase = ::this.handleCreateDatabase } componentDidMount() { @@ -22,12 +22,14 @@ class DatabaseManagerPage extends Component { render() { const {source, databases, actions, notify} = this.props + const isCreateDBDisabled = databases.some(db => db.isEditing) return ( Date: Thu, 23 Mar 2017 16:06:17 -0700 Subject: [PATCH 84/95] Disable add rp button if currently adding an RP --- ui/src/admin/components/DatabaseManager.js | 6 +++--- ui/src/admin/components/DatabaseTable.js | 9 ++++++++- ui/src/admin/containers/DatabaseManagerPage.js | 5 ++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index 764e3d7d9..fb26a79b5 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -5,7 +5,7 @@ const DatabaseManager = ({ databases, notify, isRFDisplayed, - isCreateDBDisabled, + isAddDBDisabled, addDatabase, onEditDatabase, onKeyDownDatabase, @@ -24,7 +24,7 @@ const DatabaseManager = ({

{databases.length === 1 ? '1 Database' : `${databases.length} Databases`}

-
Create Database
+
Create Database
{ @@ -66,7 +66,7 @@ DatabaseManager.propTypes = { notify: func, addDatabase: func, isRFDisplayed: bool, - isCreateDBDisabled: bool, + isAddDBDisabled: bool, onEditDatabase: func, onKeyDownDatabase: func, onCancelDatabase: func, diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index 8fa901c28..342820a23 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -28,6 +28,7 @@ const DatabaseTable = ({
rp.isNew)} onEdit={onEditDatabase} onKeyDown={onKeyDownDatabase} onCancel={onCancelDatabase} @@ -76,6 +77,7 @@ DatabaseTable.propTypes = { database: shape(), notify: func, isRFDisplayed: bool, + isAddRPDisabled: bool, onKeyDownDatabase: func, onCancelDatabase: func, onConfirmDatabase: func, @@ -98,6 +100,7 @@ const DatabaseTableHeader = ({ onStartDelete, onDatabaseDeleteConfirm, onAddRetentionPolicy, + isAddRPDisabled, }) => { if (database.isEditing) { return ( @@ -119,6 +122,7 @@ const DatabaseTableHeader = ({ onAddRetentionPolicy={onAddRetentionPolicy} onConfirm={onConfirm} onCancel={onCancel} + isAddRPDisabled={isAddRPDisabled} /> ) } @@ -132,6 +136,7 @@ DatabaseTableHeader.propTypes = { onStartDelete: func, onDatabaseDeleteConfirm: func, onAddRetentionPolicy: func, + isAddRPDisabled: bool, } const Header = ({ @@ -139,6 +144,7 @@ const Header = ({ onStartDelete, onDatabaseDeleteConfirm, onAddRetentionPolicy, + isAddRPDisabled, onCancel, onConfirm, }) => { @@ -153,7 +159,7 @@ const Header = ({ -
@@ -190,6 +196,7 @@ Header.propTypes = { onStartDelete: func, onDatabaseDeleteConfirm: func, onAddRetentionPolicy: func, + isAddRPDisabled: bool, onConfirm: func, onCancel: func, } diff --git a/ui/src/admin/containers/DatabaseManagerPage.js b/ui/src/admin/containers/DatabaseManagerPage.js index c86a61d65..e6b6c3f9d 100644 --- a/ui/src/admin/containers/DatabaseManagerPage.js +++ b/ui/src/admin/containers/DatabaseManagerPage.js @@ -22,14 +22,13 @@ class DatabaseManagerPage extends Component { render() { const {source, databases, actions, notify} = this.props - const isCreateDBDisabled = databases.some(db => db.isEditing) - return ( + db.isEditing)} onKeyDownDatabase={this.handleKeyDownDatabase} onDatabaseDeleteConfirm={this.handleDatabaseDeleteConfirm} addDatabase={actions.addDatabase} From 003961e01619dcd805b7cec8db8420385181daa0 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 23 Mar 2017 16:12:59 -0700 Subject: [PATCH 85/95] Remove ability to delete if last RP --- ui/src/admin/components/DatabaseRow.js | 17 ++++++++++++++--- ui/src/admin/components/DatabaseTable.js | 1 + 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/ui/src/admin/components/DatabaseRow.js b/ui/src/admin/components/DatabaseRow.js index 1ae721e10..74caf22a6 100644 --- a/ui/src/admin/components/DatabaseRow.js +++ b/ui/src/admin/components/DatabaseRow.js @@ -103,15 +103,25 @@ class DatabaseRow extends Component { { isDeleting ? onDelete(database, retentionPolicy)} onCancel={this.handleEndDelete} /> : - + this.renderDeleteButton() } ) } + renderDeleteButton() { + if (!this.props.isDeletable) { + return + } + + return ( + + ) + } + handleClickOutside() { this.handleEndEdit() this.handleEndDelete() @@ -219,6 +229,7 @@ DatabaseRow.propTypes = { isDefault: bool, isEditing: bool, }), + isDeletable: bool, database: shape(), onRemove: func, onCreate: func, diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index 342820a23..ce0731fbb 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -61,6 +61,7 @@ const DatabaseTable = ({ onRemove={onRemoveRetentionPolicy} onDelete={onDeleteRetentionPolicy} isRFDisplayed={isRFDisplayed} + isDeletable={database.retentionPolicies.length > 1} /> ) }) From 7265f0f503c2fe24f195bc023f9367ec55201408 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 23 Mar 2017 16:21:19 -0700 Subject: [PATCH 86/95] Remove delete for _internal db --- ui/src/admin/components/DatabaseTable.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index ce0731fbb..f40b32f33 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -157,9 +157,12 @@ const Header = ({ const buttons = (
- + { + database.name === '_internal' ? null : + + } From 635fc9824c6840a88ee755688b9a3d008e300bc5 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 23 Mar 2017 17:09:47 -0700 Subject: [PATCH 87/95] Fix update sync --- ui/src/admin/actions/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js index 25f83c5e2..c23b71ba4 100644 --- a/ui/src/admin/actions/index.js +++ b/ui/src/admin/actions/index.js @@ -291,7 +291,7 @@ export const updateRetentionPolicyAsync = (database, retentionPolicy, updates) = dispatch(editRetentionPolicy(database, retentionPolicy, updates)) const {data} = await updateRetentionPolicyAJAX(retentionPolicy.links.self, updates) dispatch(publishNotification('success', 'Retention policy updated successfully')) - dispatch(syncRetentionPolicy(database, data)) + dispatch(syncRetentionPolicy(database, retentionPolicy, data)) } catch (error) { dispatch(publishNotification('error', `Failed to update retention policy: ${error.data.message}`)) } From 9648b66b6f2145107c9363c2c86bafe831ee9afd Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 23 Mar 2017 17:10:13 -0700 Subject: [PATCH 88/95] Fix update retention policy --- ui/src/admin/apis/index.js | 2 +- ui/src/admin/components/DatabaseRow.js | 19 ++++--------------- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/ui/src/admin/apis/index.js b/ui/src/admin/apis/index.js index 5e372bca8..7c411ba0f 100644 --- a/ui/src/admin/apis/index.js +++ b/ui/src/admin/apis/index.js @@ -197,7 +197,7 @@ export const updateRetentionPolicy = async (url, retentionPolicy) => { method: 'PUT', url, data: { - retentionPolicy, + ...retentionPolicy, }, }) } catch (error) { diff --git a/ui/src/admin/components/DatabaseRow.js b/ui/src/admin/components/DatabaseRow.js index 74caf22a6..37cab2ad7 100644 --- a/ui/src/admin/components/DatabaseRow.js +++ b/ui/src/admin/components/DatabaseRow.js @@ -44,18 +44,7 @@ class DatabaseRow extends Component { return (
@@ -190,12 +180,11 @@ class DatabaseRow extends Component { } getInputValues() { - const name = this.name.value.trim() let duration = this.duration.value.trim() const replication = +this.replication.value.trim() - const {notify} = this.props + const {notify, retentionPolicy: {name}} = this.props - if (!name || !duration || !replication) { + if (!duration || !replication) { notify('error', 'Fields cannot be empty') return } From 8380ede50e0792960cb56726958549f420ea8511 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 24 Mar 2017 09:49:32 -0700 Subject: [PATCH 89/95] Remove ability to update name --- ui/src/admin/components/DatabaseRow.js | 32 +++++++++++++++++++++----- ui/src/utils/formatting.js | 4 ++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/ui/src/admin/components/DatabaseRow.js b/ui/src/admin/components/DatabaseRow.js index 37cab2ad7..caa29af91 100644 --- a/ui/src/admin/components/DatabaseRow.js +++ b/ui/src/admin/components/DatabaseRow.js @@ -1,5 +1,5 @@ import React, {PropTypes, Component} from 'react' -import {formatInfiniteDuration} from 'utils/formatting' +import {formatRPDuration} from 'utils/formatting' import YesNoButtons from 'src/shared/components/YesNoButtons' import onClickOutside from 'react-onclickoutside' @@ -38,13 +38,28 @@ class DatabaseRow extends Component { } = this.props const {isEditing, isDeleting} = this.state - const formattedDuration = formatInfiniteDuration(duration) + const formattedDuration = formatRPDuration(duration) if (isEditing) { return ( - @@ -86,7 +101,7 @@ class DatabaseRow extends Component { return ( - + {isRFDisplayed ? : null}
+
+ onEdit(database, {...retentionPolicy, name: e.target.value})} + onKeyDown={() => {}} + autoFocus={true} + /> +
+
+
+ onEdit(database, {...retentionPolicy, duration: e.target.value})} + onKeyDown={() => {}} + /> +
+
+
+ onEdit(database, {...retentionPolicy, replication: +e.target.value})} + onKeyDown={() => {}} + /> +
+
+ {}} onCancel={() => {}} /> +
@@ -21,13 +78,20 @@ export const DatabaseRow = ({name, duration, replication, isDefault}) => { const { bool, + func, number, + shape, string, } = PropTypes DatabaseRow.propTypes = { - name: string, - duration: string, - replication: number, - isDefault: bool, + retentionPolicy: shape({ + name: string, + duration: string, + replication: number, + isDefault: bool, + isEditing: bool, + }), + database: shape(), + onEdit: func, } diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index 060f0a70b..360391e50 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -16,6 +16,7 @@ const DatabaseTable = ({ onStartDeleteDatabase, onDatabaseDeleteConfirm, onAddRetentionPolicy, + onEditRetentionPolicy, }) => { return (
@@ -41,14 +42,13 @@ const DatabaseTable = ({
- {}} onCancel={() => {}} /> + {}} onCancel={onCancel} />
- {}} onCancel={onCancel} /> +
- + {}} onCancel={isNew ? onCancel : onStopEdit} />
+ onEdit(database, retentionPolicy)}> {name} {isDefault ? default : null} {formatRPDuration(duration)}{replication} onEdit(database, retentionPolicy)}>{formatRPDuration(duration)} onEdit(database, retentionPolicy)}>{replication}
+
+ onKeyDown(e, database)} + autoFocus={true} + /> +
+
+
+ onKeyDown(e, database)} + /> +
+
+
+ onKeyDown(e, database)} + /> +
+
+ {}} onCancel={isNew ? onCancel : onStopEdit} /> +
-
- onEdit(database, {...retentionPolicy, name: e.target.value})} - onKeyDown={(e) => onKeyDown(e, database, retentionPolicy)} - autoFocus={true} - /> -
-
-
- onEdit(database, {...retentionPolicy, duration: e.target.value})} - onKeyDown={(e) => onKeyDown(e, database, retentionPolicy)} - /> -
-
-
- onEdit(database, {...retentionPolicy, replication: +e.target.value})} - onKeyDown={(e) => onKeyDown(e, database, retentionPolicy)} - /> -
+
onEdit(database, retentionPolicy)}> + {name} + {isDefault ? default : null} onEdit(database, retentionPolicy)}>{formatRPDuration(duration)} onEdit(database, retentionPolicy)}>{replication} - {}} onCancel={isNew ? onCancel : onStopEdit} /> +
onEdit(database, retentionPolicy)}> - {name} - {isDefault ? default : null} - onEdit(database, retentionPolicy)}>{formatRPDuration(duration)} onEdit(database, retentionPolicy)}>{replication} - -
@@ -54,8 +91,9 @@ class DatabaseRow extends Component { type="text" defaultValue={name} placeholder="give it a name" - onKeyDown={(e) => onKeyDown(e, database)} + onKeyDown={(e) => this.handleKeyDown(e, database)} autoFocus={true} + ref={(r) => this.name = r} /> - {}} onCancel={isNew ? onCancel : onStopEdit} /> +
onEdit(database, retentionPolicy)}> + {name} {isDefault ? default : null} onEdit(database, retentionPolicy)}>{formatRPDuration(duration)} onEdit(database, retentionPolicy)}>{replication}{formatRPDuration(duration)}{replication} + + +) + +const { + func, +} = PropTypes + +YesNoButtons.propTypes = { + onConfirm: func.isRequired, + onCancel: func.isRequired, +} + +export default YesNoButtons From 36ffe0c436e85105bfa4f3cebbc4fb6851a45adb Mon Sep 17 00:00:00 2001 From: Jade McGough Date: Tue, 21 Mar 2017 12:59:27 -0700 Subject: [PATCH 32/95] dbs GET returns list of databases --- server/databases.go | 82 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 13 deletions(-) diff --git a/server/databases.go b/server/databases.go index a11c69fbb..0bb112bf4 100644 --- a/server/databases.go +++ b/server/databases.go @@ -1,25 +1,81 @@ package server import ( - "net/http" - "fmt" + "fmt" + "net/http" + + "github.com/influxdata/chronograf" ) -type jsonResponse struct{ - id int +type dbLinks struct { + Self string `json:"self"` // Self link mapping to this resource + RPs string `json:"rps"` // URL for retention policies for this database +} + +type database struct { + Name string `json:"name"` // a unique string identifier for the database + Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) + Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy) + ShardDuration string `json:shardDuration,omitempty` // the shard duration (when creating a default retention policy) + Links dbLinks `json:links` // Links are URI locations related to the database +} + +type postInfluxResponze struct { + Results interface{} `json:"results"` // results from influx } // Databases queries the list of all databases for a source -func (h *Service) Databases (w http.ResponseWriter, r *http.Request) { - fmt.Print("database endpoint") +func (h *Service) Databases(w http.ResponseWriter, r *http.Request) { + id, err := paramID("id", r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) + return + } - srcID, err := paramID("id", r) - if err != nil { - Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger) - return - } + // res := []database{} - res := jsonResponse{id: srcID} + // move this influxdb communication code somewhere else after it's working + // START + ctx := r.Context() + src, err := h.SourcesStore.Get(ctx, id) + if err != nil { + notFound(w, id, h.Logger) + return + } - encodeJSON(w, http.StatusOK, res, h.Logger) + ts, err := h.TimeSeries(src) + if err != nil { + msg := fmt.Sprintf("Unable to connect to source %d: %v", id, err) + Error(w, http.StatusBadRequest, msg, h.Logger) + return + } + + if err = ts.Connect(ctx, &src); err != nil { + msg := fmt.Sprintf("Unable to connect to source %d: %v", id, err) + Error(w, http.StatusBadRequest, msg, h.Logger) + return + } + + req := chronograf.Query{Command: "SHOW DATABASES"} + + response, err := ts.Query(ctx, req) + if err != nil { + if err == chronograf.ErrUpstreamTimeout { + msg := "Timeout waiting for Influx response" + Error(w, http.StatusRequestTimeout, msg, h.Logger) + return + } + // TODO: Here I want to return the error code from influx. + Error(w, http.StatusBadRequest, err.Error(), h.Logger) + return + } + + res := postInfluxResponze{ + Results: response, + } + + //fmt.Printf("%+v\n", foo) + // END + + encodeJSON(w, http.StatusOK, res, h.Logger) } From ff6b9a3a21803c2693c50b2382d966997bbd43c2 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Mar 2017 14:00:48 -0700 Subject: [PATCH 33/95] Add rp input validation --- ui/src/admin/components/DatabaseManager.js | 3 + ui/src/admin/components/DatabaseRow.js | 136 ++++++++++-------- ui/src/admin/components/DatabaseTable.js | 3 + .../admin/containers/DatabaseManagerPage.js | 6 +- 4 files changed, 89 insertions(+), 59 deletions(-) diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index 983fe0130..bd2dd53cd 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -3,6 +3,7 @@ import DatabaseTable from 'src/admin/components/DatabaseTable' const DatabaseManager = ({ databases, + notify, addDatabase, onEditDatabase, onKeyDownDatabase, @@ -29,6 +30,7 @@ const DatabaseManager = ({ ) } + + handleClickOutside() { + this.handleEndEdit() + } + + handleStartEdit() { + this.setState({isEditing: true}) + } + + handleEndEdit() { + this.setState({isEditing: false}) + } + + handleCreate() { + const {database, onCreate} = this.props + const validInputs = this.getInputValues() + if (!validInputs) { + return + } + + onCreate(database, validInputs) + this.handleEndEdit() + } + + handleUpdate() { + const {database, retentionPolicy, onUpdate} = this.props + const validInputs = this.getInputValues() + if (!validInputs) { + return + } + + onUpdate(database, {...retentionPolicy, ...validInputs}) + this.handleEndEdit() + } + + handleKeyDown(e) { + const {key} = e + const {retentionPolicy, database, onCancel} = this.props + + + if (key === 'Escape') { + if (retentionPolicy.isNew) { + onCancel(database, retentionPolicy) + return + } + + this.handleEndEdit() + } + + if (key === 'Enter') { + if (retentionPolicy.isNew) { + this.handleCreate() + return + } + + this.handleUpdate() + } + } + + getInputValues() { + const name = this.name.value.trim() + const duration = this.duration.value.trim() + const replication = +this.replication.value.trim() + + if (!name || !duration || !replication) { + this.props.notify('error', 'Fields cannot be empty') + return + } + + return { + name, + duration, + replication, + } + } + } const { @@ -169,10 +189,10 @@ DatabaseRow.propTypes = { isEditing: bool, }), database: shape(), - onEdit: func, onCancel: func, onCreate: func, onUpdate: func, + notify: func, } export default onClickOutside(DatabaseRow) diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index f031fdd43..afe677987 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -9,6 +9,7 @@ const { const DatabaseTable = ({ database, + notify, onEditDatabase, onKeyDownDatabase, onCancelDatabase, @@ -47,6 +48,7 @@ const DatabaseTable = ({ return ( ({ @@ -116,6 +119,7 @@ const mapStateToProps = ({admin: {databases, retentionPolicies}}) => ({ const mapDispatchToProps = (dispatch) => ({ actions: bindActionCreators(adminActionCreators, dispatch), + notify: bindActionCreators(publishNotification, dispatch), }) export default connect(mapStateToProps, mapDispatchToProps)(DatabaseManagerPage) From ba442737793f0f51ce29f502f54f90178821035b Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Mar 2017 14:03:12 -0700 Subject: [PATCH 34/95] =?UTF-8?q?Add=20=E2=88=9E=20for=20infinite=20durati?= =?UTF-8?q?on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/src/utils/formatting.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/utils/formatting.js b/ui/src/utils/formatting.js index 25411e7be..c08999898 100644 --- a/ui/src/utils/formatting.js +++ b/ui/src/utils/formatting.js @@ -17,7 +17,7 @@ export const formatBytes = (bytes) => { export const formatRPDuration = (duration) => { if (duration === '0' || duration === '0s') { - return 'infinite'; + return '∞'; } let adjustedTime = duration; From 8aa99442d3e185c170ed86ba1c5462eac709cdff Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 21 Mar 2017 14:31:00 -0700 Subject: [PATCH 35/95] Fix cancel new rps --- ui/src/admin/components/DatabaseManager.js | 3 +++ ui/src/admin/components/DatabaseRow.js | 18 +++++++++++++----- ui/src/admin/components/DatabaseTable.js | 3 +++ ui/src/admin/containers/DatabaseManagerPage.js | 3 ++- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index bd2dd53cd..34e10f6c9 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -17,6 +17,7 @@ const DatabaseManager = ({ onCancelRetentionPolicy, onCreateRetentionPolicy, onUpdateRetentionPolicy, + onRemoveRetentionPolicy, }) => { return (
@@ -43,6 +44,7 @@ const DatabaseManager = ({ onCancelRetentionPolicy={onCancelRetentionPolicy} onCreateRetentionPolicy={onCreateRetentionPolicy} onUpdateRetentionPolicy={onUpdateRetentionPolicy} + onRemoveRetentionPolicy={onRemoveRetentionPolicy} /> ) } @@ -73,6 +75,7 @@ DatabaseManager.propTypes = { onCancelRetentionPolicy: func, onCreateRetentionPolicy: func, onUpdateRetentionPolicy: func, + onRemoveRetentionPolicy: func, } export default DatabaseManager diff --git a/ui/src/admin/components/DatabaseRow.js b/ui/src/admin/components/DatabaseRow.js index 878acb625..519da0812 100644 --- a/ui/src/admin/components/DatabaseRow.js +++ b/ui/src/admin/components/DatabaseRow.js @@ -18,9 +18,17 @@ class DatabaseRow extends Component { this.getInputValues = ::this.getInputValues } + componentWillMount() { + if (this.props.retentionPolicy.isNew) { + this.setState({isEditing: true}) + } + } + render() { const { + onRemove, retentionPolicy: {name, duration, replication, isDefault, isNew}, + retentionPolicy, database, } = this.props @@ -67,11 +75,11 @@ class DatabaseRow extends Component { ref={(r) => this.replication = r} />
-
onRemove(database, retentionPolicy) : this.handleEndEdit} />
+
this.handleKeyDown(e, database)} ref={(r) => this.replication = r} />
-
- - {name} - {isDefault ? default : null} - {formatRPDuration(duration)}{replication} {name} {isDefault ? default : null}{formattedDuration}{replication}
Retention Policy DurationReplication FactorReplication Factor
@@ -72,7 +77,7 @@ class DatabaseRow extends Component { name="name" type="number" min="1" - defaultValue={replication} + defaultValue={replication || 1} placeholder="how many nodes do you have" onKeyDown={(e) => this.handleKeyDown(e, database)} ref={(r) => this.replication = r} @@ -95,9 +100,13 @@ class DatabaseRow extends Component { {formattedDuration}{replication} - + { + isDeleting ? + onDelete(database, retentionPolicy)} onCancel={this.handleEndDelete} /> : + + }
-
- this.handleKeyDown(e, database)} - autoFocus={true} - ref={(r) => this.name = r} - /> -
+ {name}
@@ -67,6 +56,7 @@ class DatabaseRow extends Component { placeholder="how long should data last" onKeyDown={(e) => this.handleKeyDown(e, database)} ref={(r) => this.duration = r} + autoFocus={true} />
- {name} + { + isNew ? +
+ this.handleKeyDown(e, database)} + ref={(r) => this.name = r} + autoFocus={true} + /> +
: +
+ {name} +
+ }
@@ -56,7 +71,7 @@ class DatabaseRow extends Component { placeholder="how long should data last" onKeyDown={(e) => this.handleKeyDown(e, database)} ref={(r) => this.duration = r} - autoFocus={true} + autoFocus={!isNew} />
{name} {isDefault ? default : null}{name} {isDefault ? default : null} {formattedDuration}{replication} @@ -113,6 +128,11 @@ class DatabaseRow extends Component { } handleClickOutside() { + const {database, retentionPolicy, onRemove} = this.props + if (retentionPolicy.isNew) { + onRemove(database, retentionPolicy) + } + this.handleEndEdit() this.handleEndDelete() } diff --git a/ui/src/utils/formatting.js b/ui/src/utils/formatting.js index a70bd585d..9f0da688b 100644 --- a/ui/src/utils/formatting.js +++ b/ui/src/utils/formatting.js @@ -18,6 +18,10 @@ export const formatBytes = (bytes) => { // Using InfluxDB 1.2+ we should no longer need this formatter. // Times can now be submitted using multiple units i.e. 1d2h3m export const formatRPDuration = (duration) => { + if (!duration) { + return + } + if (duration === '0' || duration === '0s') { return '∞'; } From e8a77be244e202150c2193ee8bb16b753fd3494d Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Fri, 24 Mar 2017 09:58:57 -0700 Subject: [PATCH 90/95] Update RP JSON default to isDefault --- chronograf.go | 5 +- server/databases_test.go | 349 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 353 insertions(+), 1 deletion(-) create mode 100644 server/databases_test.go diff --git a/chronograf.go b/chronograf.go index 7a839a233..0e416f883 100644 --- a/chronograf.go +++ b/chronograf.go @@ -136,7 +136,7 @@ type Source struct { URL string `json:"url"` // URL are the connections to the source MetaURL string `json:"metaUrl,omitempty"` // MetaURL is the url for the meta node InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"` // InsecureSkipVerify as true means any certificate presented by the source is accepted. - Default bool `json:"default"` // Default specifies the default source for the application + Default bool `json:"isDefault"` // Default specifies the default source for the application Telegraf string `json:"telegraf"` // Telegraf is the db telegraf is written to. By default it is "telegraf" } @@ -316,6 +316,7 @@ type UsersStore interface { Update(context.Context, *User) error } +// Database represents a database in a time series source type Database struct { Name string `json:"name"` // a unique string identifier for the database Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) @@ -323,6 +324,7 @@ type Database struct { ShardDuration string `json:"shardDuration,omitempty"` // the shard duration (when creating a default retention policy) } +// RetentionPolicy represents a retention policy in a time series source type RetentionPolicy struct { Name string `json:"name"` // a unique string identifier for the retention policy Duration string `json:"duration,omitempty"` // the duration @@ -331,6 +333,7 @@ type RetentionPolicy struct { Default bool `json:"default,omitempty"` // whether the RP should be the default } +// Databases represents a databases in a time series source type Databases interface { // All lists all databases AllDB(context.Context) ([]Database, error) diff --git a/server/databases_test.go b/server/databases_test.go new file mode 100644 index 000000000..c17ee676a --- /dev/null +++ b/server/databases_test.go @@ -0,0 +1,349 @@ +package server + +import ( + "net/http" + "testing" + + "github.com/influxdata/chronograf" +) + +func TestService_GetDatabases(t *testing.T) { + type fields struct { + SourcesStore chronograf.SourcesStore + ServersStore chronograf.ServersStore + LayoutStore chronograf.LayoutStore + AlertRulesStore chronograf.AlertRulesStore + UsersStore chronograf.UsersStore + DashboardsStore chronograf.DashboardsStore + TimeSeriesClient TimeSeriesClient + Logger chronograf.Logger + UseAuth bool + Databases chronograf.Databases + } + type args struct { + w http.ResponseWriter + r *http.Request + } + tests := []struct { + name string + fields fields + args args + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := &Service{ + SourcesStore: tt.fields.SourcesStore, + ServersStore: tt.fields.ServersStore, + LayoutStore: tt.fields.LayoutStore, + AlertRulesStore: tt.fields.AlertRulesStore, + UsersStore: tt.fields.UsersStore, + DashboardsStore: tt.fields.DashboardsStore, + TimeSeriesClient: tt.fields.TimeSeriesClient, + Logger: tt.fields.Logger, + UseAuth: tt.fields.UseAuth, + Databases: tt.fields.Databases, + } + h.GetDatabases(tt.args.w, tt.args.r) + }) + } +} + +func TestService_NewDatabase(t *testing.T) { + type fields struct { + SourcesStore chronograf.SourcesStore + ServersStore chronograf.ServersStore + LayoutStore chronograf.LayoutStore + AlertRulesStore chronograf.AlertRulesStore + UsersStore chronograf.UsersStore + DashboardsStore chronograf.DashboardsStore + TimeSeriesClient TimeSeriesClient + Logger chronograf.Logger + UseAuth bool + Databases chronograf.Databases + } + type args struct { + w http.ResponseWriter + r *http.Request + } + tests := []struct { + name string + fields fields + args args + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := &Service{ + SourcesStore: tt.fields.SourcesStore, + ServersStore: tt.fields.ServersStore, + LayoutStore: tt.fields.LayoutStore, + AlertRulesStore: tt.fields.AlertRulesStore, + UsersStore: tt.fields.UsersStore, + DashboardsStore: tt.fields.DashboardsStore, + TimeSeriesClient: tt.fields.TimeSeriesClient, + Logger: tt.fields.Logger, + UseAuth: tt.fields.UseAuth, + Databases: tt.fields.Databases, + } + h.NewDatabase(tt.args.w, tt.args.r) + }) + } +} + +func TestService_DropDatabase(t *testing.T) { + type fields struct { + SourcesStore chronograf.SourcesStore + ServersStore chronograf.ServersStore + LayoutStore chronograf.LayoutStore + AlertRulesStore chronograf.AlertRulesStore + UsersStore chronograf.UsersStore + DashboardsStore chronograf.DashboardsStore + TimeSeriesClient TimeSeriesClient + Logger chronograf.Logger + UseAuth bool + Databases chronograf.Databases + } + type args struct { + w http.ResponseWriter + r *http.Request + } + tests := []struct { + name string + fields fields + args args + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := &Service{ + SourcesStore: tt.fields.SourcesStore, + ServersStore: tt.fields.ServersStore, + LayoutStore: tt.fields.LayoutStore, + AlertRulesStore: tt.fields.AlertRulesStore, + UsersStore: tt.fields.UsersStore, + DashboardsStore: tt.fields.DashboardsStore, + TimeSeriesClient: tt.fields.TimeSeriesClient, + Logger: tt.fields.Logger, + UseAuth: tt.fields.UseAuth, + Databases: tt.fields.Databases, + } + h.DropDatabase(tt.args.w, tt.args.r) + }) + } +} + +func TestService_RetentionPolicies(t *testing.T) { + type fields struct { + SourcesStore chronograf.SourcesStore + ServersStore chronograf.ServersStore + LayoutStore chronograf.LayoutStore + AlertRulesStore chronograf.AlertRulesStore + UsersStore chronograf.UsersStore + DashboardsStore chronograf.DashboardsStore + TimeSeriesClient TimeSeriesClient + Logger chronograf.Logger + UseAuth bool + Databases chronograf.Databases + } + type args struct { + w http.ResponseWriter + r *http.Request + } + tests := []struct { + name string + fields fields + args args + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := &Service{ + SourcesStore: tt.fields.SourcesStore, + ServersStore: tt.fields.ServersStore, + LayoutStore: tt.fields.LayoutStore, + AlertRulesStore: tt.fields.AlertRulesStore, + UsersStore: tt.fields.UsersStore, + DashboardsStore: tt.fields.DashboardsStore, + TimeSeriesClient: tt.fields.TimeSeriesClient, + Logger: tt.fields.Logger, + UseAuth: tt.fields.UseAuth, + Databases: tt.fields.Databases, + } + h.RetentionPolicies(tt.args.w, tt.args.r) + }) + } +} + +func TestService_NewRetentionPolicy(t *testing.T) { + type fields struct { + SourcesStore chronograf.SourcesStore + ServersStore chronograf.ServersStore + LayoutStore chronograf.LayoutStore + AlertRulesStore chronograf.AlertRulesStore + UsersStore chronograf.UsersStore + DashboardsStore chronograf.DashboardsStore + TimeSeriesClient TimeSeriesClient + Logger chronograf.Logger + UseAuth bool + Databases chronograf.Databases + } + type args struct { + w http.ResponseWriter + r *http.Request + } + tests := []struct { + name string + fields fields + args args + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := &Service{ + SourcesStore: tt.fields.SourcesStore, + ServersStore: tt.fields.ServersStore, + LayoutStore: tt.fields.LayoutStore, + AlertRulesStore: tt.fields.AlertRulesStore, + UsersStore: tt.fields.UsersStore, + DashboardsStore: tt.fields.DashboardsStore, + TimeSeriesClient: tt.fields.TimeSeriesClient, + Logger: tt.fields.Logger, + UseAuth: tt.fields.UseAuth, + Databases: tt.fields.Databases, + } + h.NewRetentionPolicy(tt.args.w, tt.args.r) + }) + } +} + +func TestService_UpdateRetentionPolicy(t *testing.T) { + type fields struct { + SourcesStore chronograf.SourcesStore + ServersStore chronograf.ServersStore + LayoutStore chronograf.LayoutStore + AlertRulesStore chronograf.AlertRulesStore + UsersStore chronograf.UsersStore + DashboardsStore chronograf.DashboardsStore + TimeSeriesClient TimeSeriesClient + Logger chronograf.Logger + UseAuth bool + Databases chronograf.Databases + } + type args struct { + w http.ResponseWriter + r *http.Request + } + tests := []struct { + name string + fields fields + args args + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := &Service{ + SourcesStore: tt.fields.SourcesStore, + ServersStore: tt.fields.ServersStore, + LayoutStore: tt.fields.LayoutStore, + AlertRulesStore: tt.fields.AlertRulesStore, + UsersStore: tt.fields.UsersStore, + DashboardsStore: tt.fields.DashboardsStore, + TimeSeriesClient: tt.fields.TimeSeriesClient, + Logger: tt.fields.Logger, + UseAuth: tt.fields.UseAuth, + Databases: tt.fields.Databases, + } + h.UpdateRetentionPolicy(tt.args.w, tt.args.r) + }) + } +} + +func TestService_DropRetentionPolicy(t *testing.T) { + type fields struct { + SourcesStore chronograf.SourcesStore + ServersStore chronograf.ServersStore + LayoutStore chronograf.LayoutStore + AlertRulesStore chronograf.AlertRulesStore + UsersStore chronograf.UsersStore + DashboardsStore chronograf.DashboardsStore + TimeSeriesClient TimeSeriesClient + Logger chronograf.Logger + UseAuth bool + Databases chronograf.Databases + } + type args struct { + w http.ResponseWriter + r *http.Request + } + tests := []struct { + name string + fields fields + args args + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := &Service{ + SourcesStore: tt.fields.SourcesStore, + ServersStore: tt.fields.ServersStore, + LayoutStore: tt.fields.LayoutStore, + AlertRulesStore: tt.fields.AlertRulesStore, + UsersStore: tt.fields.UsersStore, + DashboardsStore: tt.fields.DashboardsStore, + TimeSeriesClient: tt.fields.TimeSeriesClient, + Logger: tt.fields.Logger, + UseAuth: tt.fields.UseAuth, + Databases: tt.fields.Databases, + } + h.DropRetentionPolicy(tt.args.w, tt.args.r) + }) + } +} + +func TestValidDatabaseRequest(t *testing.T) { + type args struct { + d *chronograf.Database + } + tests := []struct { + name string + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := ValidDatabaseRequest(tt.args.d); (err != nil) != tt.wantErr { + t.Errorf("ValidDatabaseRequest() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestValidRetentionPolicyRequest(t *testing.T) { + type args struct { + rp *chronograf.RetentionPolicy + } + tests := []struct { + name string + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := ValidRetentionPolicyRequest(tt.args.rp); (err != nil) != tt.wantErr { + t.Errorf("ValidRetentionPolicyRequest() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} From 53fda10deb84f8e58de13effdc476861d98867d3 Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Fri, 24 Mar 2017 10:03:38 -0700 Subject: [PATCH 91/95] FIx RP default to isDefault --- chronograf.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chronograf.go b/chronograf.go index 0e416f883..821b3e0a2 100644 --- a/chronograf.go +++ b/chronograf.go @@ -136,7 +136,7 @@ type Source struct { URL string `json:"url"` // URL are the connections to the source MetaURL string `json:"metaUrl,omitempty"` // MetaURL is the url for the meta node InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"` // InsecureSkipVerify as true means any certificate presented by the source is accepted. - Default bool `json:"isDefault"` // Default specifies the default source for the application + Default bool `json:"default"` // Default specifies the default source for the application Telegraf string `json:"telegraf"` // Telegraf is the db telegraf is written to. By default it is "telegraf" } @@ -330,7 +330,7 @@ type RetentionPolicy struct { Duration string `json:"duration,omitempty"` // the duration Replication int32 `json:"replication,omitempty"` // the replication factor ShardDuration string `json:"shardDuration,omitempty"` // the shard duration - Default bool `json:"default,omitempty"` // whether the RP should be the default + Default bool `json:"isDefault,omitempty"` // whether the RP should be the default } // Databases represents a databases in a time series source From 6bf79c7cb33307cbcc8475eb59fcf33e9f14d068 Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Fri, 24 Mar 2017 10:06:59 -0700 Subject: [PATCH 92/95] Update rpResponse default to isDefault --- server/databases.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/databases.go b/server/databases.go index 6a0d1496d..1e0456609 100644 --- a/server/databases.go +++ b/server/databases.go @@ -50,7 +50,7 @@ type rpResponse struct { Duration string `json:"duration"` // the duration Replication int32 `json:"replication"` // the replication factor ShardDuration string `json:"shardDuration"` // the shard duration - Default bool `json:"default"` // whether the RP should be the default + Default bool `json:"isDefault"` // whether the RP should be the default Links rpLinks `json:"links"` // Links are URI locations related to the database } From 43c0961e6945ffe572e184b0fd36213b0e649e33 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 24 Mar 2017 10:42:59 -0700 Subject: [PATCH 93/95] Pass delete retention policy down down down --- ui/src/admin/components/DatabaseManager.js | 3 +++ ui/src/admin/components/DatabaseTable.js | 1 + 2 files changed, 4 insertions(+) diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index fb26a79b5..147c8697b 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -19,6 +19,7 @@ const DatabaseManager = ({ onCreateRetentionPolicy, onUpdateRetentionPolicy, onRemoveRetentionPolicy, + onDeleteRetentionPolicy, }) => { return (
@@ -46,6 +47,7 @@ const DatabaseManager = ({ onCreateRetentionPolicy={onCreateRetentionPolicy} onUpdateRetentionPolicy={onUpdateRetentionPolicy} onRemoveRetentionPolicy={onRemoveRetentionPolicy} + onDeleteRetentionPolicy={onDeleteRetentionPolicy} /> ) } @@ -80,6 +82,7 @@ DatabaseManager.propTypes = { onCreateRetentionPolicy: func, onUpdateRetentionPolicy: func, onRemoveRetentionPolicy: func, + onDeleteRetentionPolicy: func, } export default DatabaseManager diff --git a/ui/src/admin/components/DatabaseTable.js b/ui/src/admin/components/DatabaseTable.js index f40b32f33..420ee4d94 100644 --- a/ui/src/admin/components/DatabaseTable.js +++ b/ui/src/admin/components/DatabaseTable.js @@ -36,6 +36,7 @@ const DatabaseTable = ({ onStartDelete={onStartDeleteDatabase} onDatabaseDeleteConfirm={onDatabaseDeleteConfirm} onAddRetentionPolicy={onAddRetentionPolicy} + onDeleteRetentionPolicy={onDeleteRetentionPolicy} />
From 148140effacb5663973d348ccaaaaca47a56cef0 Mon Sep 17 00:00:00 2001 From: lukevmorris Date: Fri, 24 Mar 2017 13:24:11 -0700 Subject: [PATCH 94/95] NameableGraph Dropdown now contains an active Delete button (#1058) * Dropdown now contains an active Delete button * Prefer `filter` over `findIndex` => `slice` --- ui/src/dashboards/actions/index.js | 18 ++++++++++++++++++ ui/src/dashboards/apis/index.js | 12 ++++++++++++ ui/src/dashboards/components/Dashboard.js | 7 +++++-- ui/src/dashboards/containers/DashboardPage.js | 5 +++++ ui/src/dashboards/reducers/ui.js | 17 +++++++++++++++++ ui/src/shared/components/LayoutRenderer.js | 5 ++++- ui/src/shared/components/NameableGraph.js | 7 +++++-- 7 files changed, 66 insertions(+), 5 deletions(-) diff --git a/ui/src/dashboards/actions/index.js b/ui/src/dashboards/actions/index.js index b48541de3..58b003dba 100644 --- a/ui/src/dashboards/actions/index.js +++ b/ui/src/dashboards/actions/index.js @@ -3,6 +3,7 @@ import { updateDashboard as updateDashboardAJAX, updateDashboardCell as updateDashboardCellAJAX, addDashboardCell as addDashboardCellAJAX, + deleteDashboardCell as deleteDashboardCellAJAX, } from 'src/dashboards/apis' import {NEW_DEFAULT_DASHBOARD_CELL} from 'src/dashboards/constants' @@ -86,6 +87,13 @@ export const renameDashboardCell = (x, y, name) => ({ }, }) +export const deleteDashboardCell = (cell) => ({ + type: 'DELETE_DASHBOARD_CELL', + payload: { + cell, + }, +}) + // Async Action Creators export const getDashboards = (dashboardID) => (dispatch) => { @@ -117,3 +125,13 @@ export const addDashboardCellAsync = (dashboard) => async (dispatch) => { throw error } } + +export const deleteDashboardCellAsync = (cell) => async (dispatch) => { + try { + await deleteDashboardCellAJAX(cell) + dispatch(deleteDashboardCell(cell)) + } catch (error) { + console.error(error) + throw error + } +} diff --git a/ui/src/dashboards/apis/index.js b/ui/src/dashboards/apis/index.js index 7a4168f09..2abf5db8f 100644 --- a/ui/src/dashboards/apis/index.js +++ b/ui/src/dashboards/apis/index.js @@ -48,3 +48,15 @@ export const addDashboardCell = async (dashboard, cell) => { throw error } } + +export const deleteDashboardCell = async (cell) => { + try { + return await AJAX({ + method: 'DELETE', + url: cell.links.self, + }) + } catch (error) { + console.error(error) + throw error + } +} diff --git a/ui/src/dashboards/components/Dashboard.js b/ui/src/dashboards/components/Dashboard.js index 2b8c6751a..fd1dc3d46 100644 --- a/ui/src/dashboards/components/Dashboard.js +++ b/ui/src/dashboards/components/Dashboard.js @@ -12,6 +12,7 @@ const Dashboard = ({ onEditCell, onRenameCell, onUpdateCell, + onDeleteCell, onSummonOverlayTechnologies, source, autoRefresh, @@ -25,13 +26,13 @@ const Dashboard = ({
{isEditMode ? : null} - {Dashboard.renderDashboard(dashboard, autoRefresh, timeRange, source, onPositionChange, onEditCell, onRenameCell, onUpdateCell, onSummonOverlayTechnologies)} + {Dashboard.renderDashboard(dashboard, autoRefresh, timeRange, source, onPositionChange, onEditCell, onRenameCell, onUpdateCell, onDeleteCell, onSummonOverlayTechnologies)}
) } -Dashboard.renderDashboard = (dashboard, autoRefresh, timeRange, source, onPositionChange, onEditCell, onRenameCell, onUpdateCell, onSummonOverlayTechnologies) => { +Dashboard.renderDashboard = (dashboard, autoRefresh, timeRange, source, onPositionChange, onEditCell, onRenameCell, onUpdateCell, onDeleteCell, onSummonOverlayTechnologies) => { const cells = dashboard.cells.map((cell, i) => { i = `${i}` const dashboardCell = {...cell, i} @@ -58,6 +59,7 @@ Dashboard.renderDashboard = (dashboard, autoRefresh, timeRange, source, onPositi onEditCell={onEditCell} onRenameCell={onRenameCell} onUpdateCell={onUpdateCell} + onDeleteCell={onDeleteCell} onSummonOverlayTechnologies={onSummonOverlayTechnologies} /> ) @@ -79,6 +81,7 @@ Dashboard.propTypes = { onEditCell: func, onRenameCell: func, onUpdateCell: func, + onDeleteCell: func, onSummonOverlayTechnologies: func, source: shape({ links: shape({ diff --git a/ui/src/dashboards/containers/DashboardPage.js b/ui/src/dashboards/containers/DashboardPage.js index 5610da0f6..de8250890 100644 --- a/ui/src/dashboards/containers/DashboardPage.js +++ b/ui/src/dashboards/containers/DashboardPage.js @@ -154,6 +154,10 @@ const DashboardPage = React.createClass({ } }, + handleDeleteDashboardCell(cell) { + this.props.dashboardActions.deleteDashboardCellAsync(cell) + }, + render() { const { dashboards, @@ -223,6 +227,7 @@ const DashboardPage = React.createClass({ onEditCell={this.handleEditDashboardCell} onRenameCell={this.handleRenameDashboardCell} onUpdateCell={this.handleUpdateDashboardCell} + onDeleteCell={this.handleDeleteDashboardCell} onSummonOverlayTechnologies={this.handleSummonOverlayTechnologies} /> diff --git a/ui/src/dashboards/reducers/ui.js b/ui/src/dashboards/reducers/ui.js index abc1c73db..982a7ddd4 100644 --- a/ui/src/dashboards/reducers/ui.js +++ b/ui/src/dashboards/reducers/ui.js @@ -109,6 +109,23 @@ export default function ui(state = initialState, action) { return {...state, ...newState} } + case 'DELETE_DASHBOARD_CELL': { + const {cell} = action.payload + const {dashboard} = state + + const newCells = dashboard.cells.filter((c) => !(c.x === cell.x && c.y === cell.y)) + const newDashboard = { + ...dashboard, + cells: newCells, + } + const newState = { + dashboard: newDashboard, + dashboards: state.dashboards.map((d) => d.id === dashboard.id ? newDashboard : d), + } + + return {...state, ...newState} + } + case 'SYNC_DASHBOARD_CELL': { const {cell} = action.payload const {dashboard} = state diff --git a/ui/src/shared/components/LayoutRenderer.js b/ui/src/shared/components/LayoutRenderer.js index 9932f4ae4..89c744a1f 100644 --- a/ui/src/shared/components/LayoutRenderer.js +++ b/ui/src/shared/components/LayoutRenderer.js @@ -50,6 +50,7 @@ export const LayoutRenderer = React.createClass({ onEditCell: func, onRenameCell: func, onUpdateCell: func, + onDeleteCell: func, onSummonOverlayTechnologies: func, }, @@ -86,7 +87,7 @@ export const LayoutRenderer = React.createClass({ }, generateVisualizations() { - const {autoRefresh, source, cells, onEditCell, onRenameCell, onUpdateCell, onSummonOverlayTechnologies} = this.props; + const {autoRefresh, source, cells, onEditCell, onRenameCell, onUpdateCell, onDeleteCell, onSummonOverlayTechnologies} = this.props; return cells.map((cell) => { const qs = cell.queries.map((query) => { @@ -103,6 +104,7 @@ export const LayoutRenderer = React.createClass({ onEditCell={onEditCell} onRenameCell={onRenameCell} onUpdateCell={onUpdateCell} + onDeleteCell={onDeleteCell} onSummonOverlayTechnologies={onSummonOverlayTechnologies} cell={cell} > @@ -123,6 +125,7 @@ export const LayoutRenderer = React.createClass({ onEditCell={onEditCell} onRenameCell={onRenameCell} onUpdateCell={onUpdateCell} + onDeleteCell={onDeleteCell} onSummonOverlayTechnologies={onSummonOverlayTechnologies} cell={cell} > diff --git a/ui/src/shared/components/NameableGraph.js b/ui/src/shared/components/NameableGraph.js index 3ab2d991b..7c4cf2669 100644 --- a/ui/src/shared/components/NameableGraph.js +++ b/ui/src/shared/components/NameableGraph.js @@ -23,6 +23,7 @@ const NameableGraph = React.createClass({ onEditCell: func, onRenameCell: func, onUpdateCell: func, + onDeleteCell: func, onSummonOverlayTechnologies: func, }, @@ -56,6 +57,7 @@ const NameableGraph = React.createClass({ onEditCell, onRenameCell, onUpdateCell, + onDeleteCell, onSummonOverlayTechnologies, children, } = this.props @@ -96,7 +98,7 @@ const NameableGraph = React.createClass({
{nameOrField}
- +
{children} @@ -106,13 +108,14 @@ const NameableGraph = React.createClass({ }, }) -const ContextMenu = OnClickOutside(({isOpen, toggleMenu, onEdit, cell}) => ( +const ContextMenu = OnClickOutside(({isOpen, toggleMenu, onEdit, onDelete, cell}) => (
  • onEdit(cell)}>Edit
  • +
  • onDelete(cell)}>Delete
)) From 89f1d0b4b42f9282f8a3ca46394ee563b82ac9bb Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Fri, 24 Mar 2017 13:26:22 -0700 Subject: [PATCH 95/95] Make div a button --- ui/src/admin/components/DatabaseManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index 147c8697b..02bcc4c28 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -25,7 +25,7 @@ const DatabaseManager = ({

{databases.length === 1 ? '1 Database' : `${databases.length} Databases`}

-
Create Database
+
{