From 62c5c37729bb0b21e2e1982522219a5f7995be37 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 26 Sep 2016 13:43:04 -0700 Subject: [PATCH 1/8] WIP make create explorer AJAX call --- ui/src/chronograf/actions/view/index.js | 8 ++++---- ui/src/chronograf/containers/DataExplorer.js | 10 +++++----- ui/src/chronograf/containers/Header.js | 17 +++++++++++------ 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/ui/src/chronograf/actions/view/index.js b/ui/src/chronograf/actions/view/index.js index 6ffadd9ee..dee0205b8 100644 --- a/ui/src/chronograf/actions/view/index.js +++ b/ui/src/chronograf/actions/view/index.js @@ -142,14 +142,13 @@ export function toggleTagAcceptance(queryId) { }; } -export function createExplorer(clusterID, push) { +export function createExploration(url, push) { return (dispatch) => { const initialState = getInitialState(); AJAX({ - url: '/api/int/v1/explorers', + url: `${url}/users/1/explorations`, // TODO: change this to use actual user link once users are introduced method: 'POST', data: JSON.stringify({ - cluster_id: clusterID, data: JSON.stringify(initialState), }), headers: { @@ -158,6 +157,7 @@ export function createExplorer(clusterID, push) { }).then((resp) => { const explorer = parseRawExplorer(resp.data); dispatch(loadExplorer(explorer)); + debugger; push(`/chronograf/data_explorer/${explorer.id}`); }); }; @@ -181,7 +181,7 @@ export function deleteExplorer(clusterID, explorerID, push) { dispatch(loadExplorer(explorer)); push(`/chronograf/data_explorer/${explorer.id}`); } else { - dispatch(createExplorer(clusterID, push)); + dispatch(createExploration(clusterID, push)); } } diff --git a/ui/src/chronograf/containers/DataExplorer.js b/ui/src/chronograf/containers/DataExplorer.js index 342af6ff1..637961e90 100644 --- a/ui/src/chronograf/containers/DataExplorer.js +++ b/ui/src/chronograf/containers/DataExplorer.js @@ -7,7 +7,7 @@ import ResizeContainer from 'shared/components/ResizeContainer'; import {FETCHING} from '../reducers/explorers'; import { setTimeRange as setTimeRangeAction, - createExplorer as createExplorerAction, + createExploration as createExplorationAction, chooseExplorer as chooseExplorerAction, deleteExplorer as deleteExplorerAction, editExplorer as editExplorerAction, @@ -28,7 +28,7 @@ const DataExplorer = React.createClass({ lower: PropTypes.string, }).isRequired, setTimeRange: PropTypes.func.isRequired, - createExplorer: PropTypes.func.isRequired, + createExploration: PropTypes.func.isRequired, chooseExplorer: PropTypes.func.isRequired, deleteExplorer: PropTypes.func.isRequired, editExplorer: PropTypes.func.isRequired, @@ -60,7 +60,7 @@ const DataExplorer = React.createClass({ }, render() { - const {timeRange, explorers, explorerID, setTimeRange, createExplorer, chooseExplorer, deleteExplorer, editExplorer} = this.props; + const {timeRange, explorers, explorerID, setTimeRange, createExploration, chooseExplorer, deleteExplorer, editExplorer} = this.props; if (explorers === FETCHING) { // TODO: page-wide spinner @@ -75,7 +75,7 @@ const DataExplorer = React.createClass({ return (
-

Session:

+

Exploration:

-
New Session
+
New Exploration
@@ -174,7 +179,7 @@ const EditExplorerModal = React.createClass({ -

Rename Explorer Session

+

Rename Exploreration

From e06806767c98a71371fa00d7c88b1dc8bbf442c8 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 27 Sep 2016 14:52:37 -0700 Subject: [PATCH 2/8] Successfully create new explorer --- ui/src/chronograf/actions/view/index.js | 9 ++++----- ui/src/chronograf/containers/Header.js | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/ui/src/chronograf/actions/view/index.js b/ui/src/chronograf/actions/view/index.js index dee0205b8..40598b8ab 100644 --- a/ui/src/chronograf/actions/view/index.js +++ b/ui/src/chronograf/actions/view/index.js @@ -142,11 +142,11 @@ export function toggleTagAcceptance(queryId) { }; } -export function createExploration(url, push) { +export function createExploration(sourceLink, push) { return (dispatch) => { const initialState = getInitialState(); AJAX({ - url: `${url}/users/1/explorations`, // TODO: change this to use actual user link once users are introduced + url: `${sourceLink}/users/1/explorations`, // TODO: change this to use actual user link once users are introduced method: 'POST', data: JSON.stringify({ data: JSON.stringify(initialState), @@ -157,8 +157,7 @@ export function createExploration(url, push) { }).then((resp) => { const explorer = parseRawExplorer(resp.data); dispatch(loadExplorer(explorer)); - debugger; - push(`/chronograf/data_explorer/${explorer.id}`); + push(`/chronograf/data_explorer/${btoa(explorer.link.href)}`); // Base64 encode explorer URI }); }; } @@ -239,7 +238,7 @@ export function fetchExplorers({source, userID, explorerID, push}) { // Create a new explorer session for a user if they don't have any // saved (e.g. when they visit for the first time). if (!explorers.length) { - dispatch(createExplorer(push)); + dispatch(createExploration(push)); return; } diff --git a/ui/src/chronograf/containers/Header.js b/ui/src/chronograf/containers/Header.js index 23393d7af..c5f477ade 100644 --- a/ui/src/chronograf/containers/Header.js +++ b/ui/src/chronograf/containers/Header.js @@ -179,7 +179,7 @@ const EditExplorerModal = React.createClass({ -

Rename Exploreration

+

Rename Explorer

From 9f716af5872ea6d9aa6341fb12349a6bd13516a7 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 27 Sep 2016 15:03:37 -0700 Subject: [PATCH 3/8] Fix typo --- ui/src/chronograf/containers/Header.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/chronograf/containers/Header.js b/ui/src/chronograf/containers/Header.js index c5f477ade..fec891a73 100644 --- a/ui/src/chronograf/containers/Header.js +++ b/ui/src/chronograf/containers/Header.js @@ -179,7 +179,7 @@ const EditExplorerModal = React.createClass({ -

Rename Explorer

+

Rename Exploration

From 49685ce60f8cbafadf4b88b822a4eca7bd90a431 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 28 Sep 2016 21:15:19 -0700 Subject: [PATCH 4/8] Successfully switch between data explorerations --- mock/mock.go | 4 ++-- ui/src/chronograf/actions/view/index.js | 7 ++++--- ui/src/chronograf/api/index.js | 8 ++++---- ui/src/chronograf/containers/DataExplorer.js | 2 +- ui/src/chronograf/reducers/activeExplorer.js | 3 ++- ui/src/store/persistStateEnhancer.js | 3 ++- 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/mock/mock.go b/mock/mock.go index 37bf4424f..233cf25d8 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -24,8 +24,8 @@ func NewExplorationStore(nowFunc func() time.Time) mrfusion.ExplorationStore { CreatedAt: nowFunc(), UpdatedAt: nowFunc(), } - e.db[1] = &mrfusion.Exploration{ - Name: "Ferdinand Magellan", + e.db[1] = mrfusion.Exploration{ + Name: "Your Mom", Data: "{\"panels\":{\"123\":{\"id\":\"123\",\"queryIds\":[\"456\"]}},\"queryConfigs\":{\"456\":{\"id\":\"456\",\"database\":null,\"measurement\":null,\"retentionPolicy\":null,\"fields\":[],\"tags\":{},\"groupBy\":{\"time\":null,\"tags\":[]},\"areTagsAccepted\":true,\"rawText\":null}}}", CreatedAt: nowFunc(), UpdatedAt: nowFunc(), diff --git a/ui/src/chronograf/actions/view/index.js b/ui/src/chronograf/actions/view/index.js index 40598b8ab..950a74c52 100644 --- a/ui/src/chronograf/actions/view/index.js +++ b/ui/src/chronograf/actions/view/index.js @@ -283,12 +283,13 @@ function saveExplorer(error) { }; } -export function chooseExplorer(clusterID, explorerID, push) { +export function chooseExplorer(explorerID, push) { return (dispatch, getState) => { // Save the previous session explicitly in case an auto-save was unable to complete. const {panels, queryConfigs, activeExplorer} = getState(); api.saveExplorer({ explorerID: activeExplorer.id, + name: activeExplorer.name, panels, queryConfigs, }).then(() => { @@ -301,11 +302,11 @@ export function chooseExplorer(clusterID, explorerID, push) { dispatch(fetchExplorer()); AJAX({ - url: `/api/int/v1/explorers/${explorerID}`, + url: explorerID, }).then((resp) => { const explorer = parseRawExplorer(resp.data); dispatch(loadExplorer(explorer)); - push(`/chronograf/data_explorer/${explorerID}`); + push(`/chronograf/data_explorer/${btoa(explorerID)}`); }); }; } diff --git a/ui/src/chronograf/api/index.js b/ui/src/chronograf/api/index.js index 93a2d5d94..70018e968 100644 --- a/ui/src/chronograf/api/index.js +++ b/ui/src/chronograf/api/index.js @@ -1,15 +1,15 @@ import AJAX from 'utils/ajax'; -export function saveExplorer({panels, queryConfigs, explorerID}) { +export function saveExplorer({name, panels, queryConfigs, explorerID}) { return AJAX({ - url: `/api/int/v1/explorers/${explorerID}`, - method: 'PUT', + url: explorerID, + method: 'PATCH', headers: { 'Content-Type': 'application/json', }, data: JSON.stringify({ data: JSON.stringify({panels, queryConfigs}), + name, }), }); } - diff --git a/ui/src/chronograf/containers/DataExplorer.js b/ui/src/chronograf/containers/DataExplorer.js index 637961e90..152e7a28a 100644 --- a/ui/src/chronograf/containers/DataExplorer.js +++ b/ui/src/chronograf/containers/DataExplorer.js @@ -69,7 +69,7 @@ const DataExplorer = React.createClass({ const activeExplorer = explorers[explorerID]; if (!activeExplorer) { - return null; // TODO: handle no explorers; + return
You have no active explorers
; // TODO: handle no explorers; } return ( diff --git a/ui/src/chronograf/reducers/activeExplorer.js b/ui/src/chronograf/reducers/activeExplorer.js index cb190cac8..cadc17b08 100644 --- a/ui/src/chronograf/reducers/activeExplorer.js +++ b/ui/src/chronograf/reducers/activeExplorer.js @@ -1,7 +1,8 @@ export default function activeExplorer(state = {}, action) { switch (action.type) { case 'LOAD_EXPLORER': { - return {id: action.payload.explorer.id}; + const {link, name} = action.payload.explorer; + return {id: link.href, name}; } } diff --git a/ui/src/store/persistStateEnhancer.js b/ui/src/store/persistStateEnhancer.js index dc92b91d2..bde890fce 100644 --- a/ui/src/store/persistStateEnhancer.js +++ b/ui/src/store/persistStateEnhancer.js @@ -33,13 +33,14 @@ export default function persistState() { store.subscribe(() => { const state = Object.assign({}, store.getState()); const explorerID = state.activeExplorer.id; + const name = state.activeExplorer.name; if (!explorerID) { return; } const {panels, queryConfigs} = state; autoSaveTimer.clear(); autoSaveTimer.set(() => { - saveExplorer({panels, queryConfigs, explorerID}).then((_) => { + saveExplorer({panels, queryConfigs, explorerID, name}).then((_) => { // TODO: This is a no-op currently because we don't have any feedback in the UI around saving, but maybe we do something in the future? // If we ever show feedback in the UI, we could potentially indicate to remove it here. }).catch(({response}) => { From ed62d8d4017cabc6db0798fa3a85f64a5d07580f Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 29 Sep 2016 11:28:20 -0700 Subject: [PATCH 5/8] Work with scoping under /sources and single source on context --- ui/src/chronograf/actions/view/index.js | 10 +++++----- ui/src/chronograf/containers/Header.js | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ui/src/chronograf/actions/view/index.js b/ui/src/chronograf/actions/view/index.js index 950a74c52..ece719b4c 100644 --- a/ui/src/chronograf/actions/view/index.js +++ b/ui/src/chronograf/actions/view/index.js @@ -142,11 +142,11 @@ export function toggleTagAcceptance(queryId) { }; } -export function createExploration(sourceLink, push) { +export function createExploration(source, push) { return (dispatch) => { const initialState = getInitialState(); AJAX({ - url: `${sourceLink}/users/1/explorations`, // TODO: change this to use actual user link once users are introduced + url: `${source.links.self}/users/1/explorations`, // TODO: change this to use actual user link once users are introduced method: 'POST', data: JSON.stringify({ data: JSON.stringify(initialState), @@ -157,7 +157,7 @@ export function createExploration(sourceLink, push) { }).then((resp) => { const explorer = parseRawExplorer(resp.data); dispatch(loadExplorer(explorer)); - push(`/chronograf/data_explorer/${btoa(explorer.link.href)}`); // Base64 encode explorer URI + push(`/sources/${source.id}/chronograf/data_explorer/${btoa(explorer.link.href)}`); // Base64 encode explorer URI }); }; } @@ -283,7 +283,7 @@ function saveExplorer(error) { }; } -export function chooseExplorer(explorerID, push) { +export function chooseExplorer(explorerID, source, push) { return (dispatch, getState) => { // Save the previous session explicitly in case an auto-save was unable to complete. const {panels, queryConfigs, activeExplorer} = getState(); @@ -306,7 +306,7 @@ export function chooseExplorer(explorerID, push) { }).then((resp) => { const explorer = parseRawExplorer(resp.data); dispatch(loadExplorer(explorer)); - push(`/chronograf/data_explorer/${btoa(explorerID)}`); + push(`/sources/${source.id}/chronograf/data_explorer/${btoa(explorerID)}`); }); }; } diff --git a/ui/src/chronograf/containers/Header.js b/ui/src/chronograf/containers/Header.js index fec891a73..61d9ff54f 100644 --- a/ui/src/chronograf/containers/Header.js +++ b/ui/src/chronograf/containers/Header.js @@ -33,7 +33,7 @@ const Header = React.createClass({ }, contextTypes: { - sources: PropTypes.arrayOf(PropTypes.shape().isRequired).isRequired, + source: PropTypes.shape(), }, handleChooseTimeRange(bounds) { @@ -54,7 +54,7 @@ const Header = React.createClass({ // TODO: passing in this.props.router.push is a big smell, getting something like // react-router-redux might be better here - this.props.actions.createExploration(this.context.sources[0].links.self, this.props.router.push); + this.props.actions.createExploration(this.context.source, this.props.router.push); }, handleChooseExplorer({id}) { @@ -62,7 +62,7 @@ const Header = React.createClass({ return; } - this.props.actions.chooseExplorer(id, this.props.router.push); + this.props.actions.chooseExplorer(id, this.context.source, this.props.router.push); }, /** From f508a969400106408bc76f64fe170c3b39479c49 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 29 Sep 2016 11:32:55 -0700 Subject: [PATCH 6/8] Change explorerID to explorerURI --- ui/src/chronograf/actions/view/index.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ui/src/chronograf/actions/view/index.js b/ui/src/chronograf/actions/view/index.js index ece719b4c..43528fddb 100644 --- a/ui/src/chronograf/actions/view/index.js +++ b/ui/src/chronograf/actions/view/index.js @@ -226,7 +226,7 @@ function loadExplorer(explorer) { }; } -export function fetchExplorers({source, userID, explorerID, push}) { +export function fetchExplorers({source, userID, explorerURI, push}) { return (dispatch) => { dispatch({type: 'FETCH_EXPLORERS'}); AJAX({ @@ -242,18 +242,18 @@ export function fetchExplorers({source, userID, explorerID, push}) { return; } - // If no explorerID is provided, it means the user wasn't attempting to visit + // If no explorerURI is provided, it means the user wasn't attempting to visit // a specific explorer (i.e. `/data_explorer/:id`). In this case, pick the // most recently updated explorer and navigate to it. - if (!explorerID) { + if (!explorerURI) { const explorer = _.maxBy(explorers, (ex) => ex.updated_at); dispatch(loadExplorer(explorer)); push(`/sources/${source.id}/chronograf/data_explorer/${btoa(explorer.link.href)}`); return; } - // We have an explorerID, meaning a specific explorer was requested. - const explorer = explorers.find((ex) => ex.id === explorerID); + // We have an explorerURI, meaning a specific explorer was requested. + const explorer = explorers.find((ex) => ex.id === explorerURI); // Attempting to request a non-existent explorer if (!explorer) { @@ -283,7 +283,7 @@ function saveExplorer(error) { }; } -export function chooseExplorer(explorerID, source, push) { +export function chooseExplorer(explorerURI, source, push) { return (dispatch, getState) => { // Save the previous session explicitly in case an auto-save was unable to complete. const {panels, queryConfigs, activeExplorer} = getState(); @@ -302,11 +302,11 @@ export function chooseExplorer(explorerID, source, push) { dispatch(fetchExplorer()); AJAX({ - url: explorerID, + url: explorerURI, }).then((resp) => { const explorer = parseRawExplorer(resp.data); dispatch(loadExplorer(explorer)); - push(`/sources/${source.id}/chronograf/data_explorer/${btoa(explorerID)}`); + push(`/sources/${source.id}/chronograf/data_explorer/${btoa(explorerURI)}`); }); }; } From 591568c69a5417905af51c93c5d49cb796967064 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 29 Sep 2016 11:35:39 -0700 Subject: [PATCH 7/8] Rename chooseExplorer to chooseExploration --- ui/src/chronograf/actions/view/index.js | 2 +- ui/src/chronograf/containers/DataExplorer.js | 10 +++++----- ui/src/chronograf/containers/Header.js | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ui/src/chronograf/actions/view/index.js b/ui/src/chronograf/actions/view/index.js index 43528fddb..cf20da77b 100644 --- a/ui/src/chronograf/actions/view/index.js +++ b/ui/src/chronograf/actions/view/index.js @@ -283,7 +283,7 @@ function saveExplorer(error) { }; } -export function chooseExplorer(explorerURI, source, push) { +export function chooseExploration(explorerURI, source, push) { return (dispatch, getState) => { // Save the previous session explicitly in case an auto-save was unable to complete. const {panels, queryConfigs, activeExplorer} = getState(); diff --git a/ui/src/chronograf/containers/DataExplorer.js b/ui/src/chronograf/containers/DataExplorer.js index 152e7a28a..8498dce0d 100644 --- a/ui/src/chronograf/containers/DataExplorer.js +++ b/ui/src/chronograf/containers/DataExplorer.js @@ -8,7 +8,7 @@ import {FETCHING} from '../reducers/explorers'; import { setTimeRange as setTimeRangeAction, createExploration as createExplorationAction, - chooseExplorer as chooseExplorerAction, + chooseExploration as chooseExplorationAction, deleteExplorer as deleteExplorerAction, editExplorer as editExplorerAction, } from '../actions/view'; @@ -29,7 +29,7 @@ const DataExplorer = React.createClass({ }).isRequired, setTimeRange: PropTypes.func.isRequired, createExploration: PropTypes.func.isRequired, - chooseExplorer: PropTypes.func.isRequired, + chooseExploration: PropTypes.func.isRequired, deleteExplorer: PropTypes.func.isRequired, editExplorer: PropTypes.func.isRequired, }, @@ -60,7 +60,7 @@ const DataExplorer = React.createClass({ }, render() { - const {timeRange, explorers, explorerID, setTimeRange, createExploration, chooseExplorer, deleteExplorer, editExplorer} = this.props; + const {timeRange, explorers, explorerID, setTimeRange, createExploration, chooseExploration, deleteExplorer, editExplorer} = this.props; if (explorers === FETCHING) { // TODO: page-wide spinner @@ -75,7 +75,7 @@ const DataExplorer = React.createClass({ return (
Date: Thu, 29 Sep 2016 11:38:32 -0700 Subject: [PATCH 8/8] Rename loadExplorer to loadExploration --- mock/mock.go | 2 +- ui/src/chronograf/actions/view/index.js | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mock/mock.go b/mock/mock.go index 233cf25d8..c5445b85a 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -24,7 +24,7 @@ func NewExplorationStore(nowFunc func() time.Time) mrfusion.ExplorationStore { CreatedAt: nowFunc(), UpdatedAt: nowFunc(), } - e.db[1] = mrfusion.Exploration{ + e.db[1] = &mrfusion.Exploration{ Name: "Your Mom", Data: "{\"panels\":{\"123\":{\"id\":\"123\",\"queryIds\":[\"456\"]}},\"queryConfigs\":{\"456\":{\"id\":\"456\",\"database\":null,\"measurement\":null,\"retentionPolicy\":null,\"fields\":[],\"tags\":{},\"groupBy\":{\"time\":null,\"tags\":[]},\"areTagsAccepted\":true,\"rawText\":null}}}", CreatedAt: nowFunc(), diff --git a/ui/src/chronograf/actions/view/index.js b/ui/src/chronograf/actions/view/index.js index cf20da77b..9e85e9973 100644 --- a/ui/src/chronograf/actions/view/index.js +++ b/ui/src/chronograf/actions/view/index.js @@ -156,7 +156,7 @@ export function createExploration(source, push) { }, }).then((resp) => { const explorer = parseRawExplorer(resp.data); - dispatch(loadExplorer(explorer)); + dispatch(loadExploration(explorer)); push(`/sources/${source.id}/chronograf/data_explorer/${btoa(explorer.link.href)}`); // Base64 encode explorer URI }); }; @@ -177,7 +177,7 @@ export function deleteExplorer(clusterID, explorerID, push) { // If we don't have an explorer to navigate to, it means we're deleting the last // explorer and should create a new one. if (explorer) { - dispatch(loadExplorer(explorer)); + dispatch(loadExploration(explorer)); push(`/chronograf/data_explorer/${explorer.id}`); } else { dispatch(createExploration(clusterID, push)); @@ -219,7 +219,7 @@ function loadExplorers(explorers) { }; } -function loadExplorer(explorer) { +function loadExploration(explorer) { return { type: 'LOAD_EXPLORER', payload: {explorer}, @@ -247,7 +247,7 @@ export function fetchExplorers({source, userID, explorerURI, push}) { // most recently updated explorer and navigate to it. if (!explorerURI) { const explorer = _.maxBy(explorers, (ex) => ex.updated_at); - dispatch(loadExplorer(explorer)); + dispatch(loadExploration(explorer)); push(`/sources/${source.id}/chronograf/data_explorer/${btoa(explorer.link.href)}`); return; } @@ -260,7 +260,7 @@ export function fetchExplorers({source, userID, explorerURI, push}) { return; } - dispatch(loadExplorer(explorer)); + dispatch(loadExploration(explorer)); }); }; } @@ -305,7 +305,7 @@ export function chooseExploration(explorerURI, source, push) { url: explorerURI, }).then((resp) => { const explorer = parseRawExplorer(resp.data); - dispatch(loadExplorer(explorer)); + dispatch(loadExploration(explorer)); push(`/sources/${source.id}/chronograf/data_explorer/${btoa(explorerURI)}`); }); };