diff --git a/mock/mock.go b/mock/mock.go index 37bf4424f..c5445b85a 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -25,7 +25,7 @@ func NewExplorationStore(nowFunc func() time.Time) mrfusion.ExplorationStore { UpdatedAt: nowFunc(), } e.db[1] = &mrfusion.Exploration{ - Name: "Ferdinand Magellan", + 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 6ffadd9ee..9e85e9973 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(source, push) { return (dispatch) => { const initialState = getInitialState(); AJAX({ - url: '/api/int/v1/explorers', + url: `${source.links.self}/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: { @@ -157,8 +156,8 @@ export function createExplorer(clusterID, push) { }, }).then((resp) => { const explorer = parseRawExplorer(resp.data); - dispatch(loadExplorer(explorer)); - push(`/chronograf/data_explorer/${explorer.id}`); + dispatch(loadExploration(explorer)); + push(`/sources/${source.id}/chronograf/data_explorer/${btoa(explorer.link.href)}`); // Base64 encode explorer URI }); }; } @@ -178,10 +177,10 @@ 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(createExplorer(clusterID, push)); + dispatch(createExploration(clusterID, push)); } } @@ -220,14 +219,14 @@ function loadExplorers(explorers) { }; } -function loadExplorer(explorer) { +function loadExploration(explorer) { return { type: 'LOAD_EXPLORER', payload: {explorer}, }; } -export function fetchExplorers({source, userID, explorerID, push}) { +export function fetchExplorers({source, userID, explorerURI, push}) { return (dispatch) => { dispatch({type: 'FETCH_EXPLORERS'}); AJAX({ @@ -239,29 +238,29 @@ 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; } - // 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)); + dispatch(loadExploration(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) { return; } - dispatch(loadExplorer(explorer)); + dispatch(loadExploration(explorer)); }); }; } @@ -284,12 +283,13 @@ function saveExplorer(error) { }; } -export function chooseExplorer(clusterID, explorerID, 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(); api.saveExplorer({ explorerID: activeExplorer.id, + name: activeExplorer.name, panels, queryConfigs, }).then(() => { @@ -302,11 +302,11 @@ export function chooseExplorer(clusterID, explorerID, push) { dispatch(fetchExplorer()); AJAX({ - url: `/api/int/v1/explorers/${explorerID}`, + url: explorerURI, }).then((resp) => { const explorer = parseRawExplorer(resp.data); - dispatch(loadExplorer(explorer)); - push(`/chronograf/data_explorer/${explorerID}`); + dispatch(loadExploration(explorer)); + push(`/sources/${source.id}/chronograf/data_explorer/${btoa(explorerURI)}`); }); }; } 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 342af6ff1..8498dce0d 100644 --- a/ui/src/chronograf/containers/DataExplorer.js +++ b/ui/src/chronograf/containers/DataExplorer.js @@ -7,8 +7,8 @@ import ResizeContainer from 'shared/components/ResizeContainer'; import {FETCHING} from '../reducers/explorers'; import { setTimeRange as setTimeRangeAction, - createExplorer as createExplorerAction, - chooseExplorer as chooseExplorerAction, + createExploration as createExplorationAction, + chooseExploration as chooseExplorationAction, deleteExplorer as deleteExplorerAction, editExplorer as editExplorerAction, } from '../actions/view'; @@ -28,8 +28,8 @@ const DataExplorer = React.createClass({ lower: PropTypes.string, }).isRequired, setTimeRange: PropTypes.func.isRequired, - createExplorer: PropTypes.func.isRequired, - chooseExplorer: PropTypes.func.isRequired, + createExploration: 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, createExplorer, chooseExplorer, deleteExplorer, editExplorer} = this.props; + const {timeRange, explorers, explorerID, setTimeRange, createExploration, chooseExploration, deleteExplorer, editExplorer} = this.props; if (explorers === FETCHING) { // TODO: page-wide spinner @@ -69,13 +69,13 @@ 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 (
-

Session:

+

Exploration:

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

Rename Explorer Session

+

Rename Exploration

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}) => {