mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-05 19:21:46 +00:00
Merge pull request #2202 from weaveworks/2154-api-url
Re-factor API URL generation code
This commit is contained in:
@@ -681,3 +681,15 @@ export function toggleTroubleshootingMenu(ev) {
|
||||
type: ActionTypes.TOGGLE_TROUBLESHOOTING_MENU
|
||||
};
|
||||
}
|
||||
|
||||
export function changeInstance() {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
getNodesDelta(
|
||||
getCurrentTopologyUrl(state),
|
||||
getActiveTopologyOptions(state),
|
||||
dispatch,
|
||||
true // forces websocket teardown and reconnect to new instance
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import Term from 'xterm';
|
||||
import { clickCloseTerminal } from '../actions/app-actions';
|
||||
import { getNeutralColor } from '../utils/color-utils';
|
||||
import { setDocumentTitle } from '../utils/title-utils';
|
||||
import { getPipeStatus, doResizeTty, wsUrl } from '../utils/web-api-utils';
|
||||
import { getPipeStatus, doResizeTty, getWebsocketUrl, getApiPath } from '../utils/web-api-utils';
|
||||
|
||||
const log = debug('scope:terminal');
|
||||
|
||||
@@ -101,14 +101,14 @@ class Terminal extends React.Component {
|
||||
}
|
||||
|
||||
createWebsocket(term) {
|
||||
const socket = new WebSocket(`${wsUrl}/api/pipe/${this.getPipeId()}`);
|
||||
const socket = new WebSocket(`${getWebsocketUrl()}/api/pipe/${this.getPipeId()}`);
|
||||
socket.binaryType = 'arraybuffer';
|
||||
|
||||
getPipeStatus(this.getPipeId(), this.props.dispatch);
|
||||
|
||||
socket.onopen = () => {
|
||||
clearTimeout(this.reconnectTimeout);
|
||||
log('socket open to', wsUrl);
|
||||
log('socket open to', getWebsocketUrl());
|
||||
this.setState({connected: true});
|
||||
};
|
||||
|
||||
@@ -243,7 +243,7 @@ class Terminal extends React.Component {
|
||||
|
||||
const bcr = this.node.getBoundingClientRect();
|
||||
const minWidth = (this.state.characterWidth * 80) + (8 * 2);
|
||||
openNewWindow(`terminal.html#!/state/${paramString}`, bcr, minWidth);
|
||||
openNewWindow(`${getApiPath()}/terminal.html#!/state/${paramString}`, bcr, minWidth);
|
||||
}
|
||||
|
||||
handleResize() {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
exports.reducer = require('./reducers/root').default;
|
||||
exports.Scope = require('./components/app').default;
|
||||
exports.ActionTypes = require('./constants/action-types').default;
|
||||
exports.actions = require('./actions/app-actions');
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
|
||||
import {OrderedMap as makeOrderedMap} from 'immutable';
|
||||
import { buildOptionsQuery, basePath, getApiPath, getWebsocketUrl } from '../web-api-utils';
|
||||
|
||||
describe('WebApiUtils', () => {
|
||||
const WebApiUtils = require('../web-api-utils');
|
||||
|
||||
describe('basePath', () => {
|
||||
const basePath = WebApiUtils.basePath;
|
||||
|
||||
it('should handle /scope/terminal.html', () => {
|
||||
expect(basePath('/scope/terminal.html')).toBe('/scope');
|
||||
});
|
||||
@@ -25,8 +22,6 @@ describe('WebApiUtils', () => {
|
||||
});
|
||||
|
||||
describe('buildOptionsQuery', () => {
|
||||
const buildOptionsQuery = WebApiUtils.buildOptionsQuery;
|
||||
|
||||
it('should handle empty options', () => {
|
||||
expect(buildOptionsQuery(makeOrderedMap({}))).toBe('');
|
||||
});
|
||||
@@ -38,4 +33,55 @@ describe('WebApiUtils', () => {
|
||||
]))).toBe('foo=2&bar=4');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getApiPath', () => {
|
||||
afterEach(() => {
|
||||
delete process.env.SCOPE_API_PREFIX;
|
||||
});
|
||||
it('returns the correct url when running standalone', () => {
|
||||
expect(getApiPath('/')).toEqual('');
|
||||
});
|
||||
it('returns the correct url when running in an iframe', () => {
|
||||
expect(getApiPath('/api/app/proud-cloud-77')).toEqual('/api/app/proud-cloud-77');
|
||||
});
|
||||
it('returns the correct url when running as a component', () => {
|
||||
process.env.SCOPE_API_PREFIX = '/api';
|
||||
expect(getApiPath('/app/proud-cloud-77')).toEqual('/api/app/proud-cloud-77');
|
||||
});
|
||||
it('returns the correct url from an arbitrary path', () => {
|
||||
expect(getApiPath('/demo/')).toEqual('/demo');
|
||||
});
|
||||
it('returns the correct url from an *.html page', () => {
|
||||
expect(getApiPath('/contrast.html')).toEqual('');
|
||||
});
|
||||
it('returns the correct url from an /*.html page while in an iframe', () => {
|
||||
expect(getApiPath('/api/app/proud-cloud-77/contrast.html')).toEqual('/api/app/proud-cloud-77');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getWebsocketUrl', () => {
|
||||
const host = 'localhost:4042';
|
||||
afterEach(() => {
|
||||
delete process.env.SCOPE_API_PREFIX;
|
||||
});
|
||||
it('returns the correct url when running standalone', () => {
|
||||
expect(getWebsocketUrl(host, '/')).toEqual(`ws://${host}`);
|
||||
});
|
||||
it('returns the correct url when running in an iframe', () => {
|
||||
expect(getWebsocketUrl(host, '/api/app/proud-cloud-77')).toEqual(`ws://${host}/api/app/proud-cloud-77`);
|
||||
});
|
||||
it('returns the correct url when running as a component', () => {
|
||||
process.env.SCOPE_API_PREFIX = '/api';
|
||||
expect(getWebsocketUrl(host, '/app/proud-cloud-77')).toEqual(`ws://${host}/api/app/proud-cloud-77`);
|
||||
});
|
||||
it('returns the correct url from an arbitrary path', () => {
|
||||
expect(getWebsocketUrl(host, '/demo/')).toEqual(`ws://${host}/demo`);
|
||||
});
|
||||
it('returns the correct url from an *.html page', () => {
|
||||
expect(getWebsocketUrl(host, '/contrast.html')).toEqual(`ws://${host}`);
|
||||
});
|
||||
it('returns the correct url from an /*.html page while in an iframe', () => {
|
||||
expect(getWebsocketUrl(host, '/api/app/proud-cloud-77/contrast.html')).toEqual(`ws://${host}/api/app/proud-cloud-77`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -58,26 +58,18 @@ export function basePathSlash(urlPath) {
|
||||
return `${basePath(urlPath)}/`;
|
||||
}
|
||||
|
||||
// JJP - `apiPath` is used to get API URLs right when running as a React component.
|
||||
// This needs to be refactored to just accept a URL prop on the scope component.
|
||||
let apiPath;
|
||||
let websocketUrl;
|
||||
const isIframe = window.location !== window.parent.location;
|
||||
const isStandalone = window.location.pathname === '/'
|
||||
|| window.location.pathname === '/demo/'
|
||||
|| window.location.pathname === '/scoped/'
|
||||
|| /\/(.+).html/.test(window.location.pathname);
|
||||
const wsProto = location.protocol === 'https:' ? 'wss' : 'ws';
|
||||
export function getApiPath(pathname = window.location.pathname) {
|
||||
if (process.env.SCOPE_API_PREFIX) {
|
||||
return basePath(`${process.env.SCOPE_API_PREFIX}${pathname}`);
|
||||
}
|
||||
|
||||
if (isIframe || isStandalone) {
|
||||
apiPath = 'api';
|
||||
websocketUrl = `${wsProto}://${location.host}${basePath(location.pathname)}`;
|
||||
} else {
|
||||
apiPath = `/api${basePath(window.location.pathname)}/api`;
|
||||
websocketUrl = `${wsProto}://${location.host}/api${basePath(window.location.pathname)}`;
|
||||
return basePath(pathname);
|
||||
}
|
||||
|
||||
export const wsUrl = websocketUrl;
|
||||
export function getWebsocketUrl(host = window.location.host, pathname = window.location.pathname) {
|
||||
const wsProto = location.protocol === 'https:' ? 'wss' : 'ws';
|
||||
return `${wsProto}://${host}${process.env.SCOPE_API_PREFIX || ''}${basePath(pathname)}`;
|
||||
}
|
||||
|
||||
function createWebsocket(topologyUrl, optionsQuery, dispatch) {
|
||||
if (socket) {
|
||||
@@ -92,7 +84,7 @@ function createWebsocket(topologyUrl, optionsQuery, dispatch) {
|
||||
createWebsocketAt = new Date();
|
||||
firstMessageOnWebsocketAt = 0;
|
||||
|
||||
socket = new WebSocket(`${wsUrl}${topologyUrl}/ws?t=${updateFrequency}&${optionsQuery}`);
|
||||
socket = new WebSocket(`${getWebsocketUrl()}${topologyUrl}/ws?t=${updateFrequency}&${optionsQuery}`);
|
||||
|
||||
socket.onopen = () => {
|
||||
dispatch(openWebsocket());
|
||||
@@ -154,9 +146,10 @@ export function getAllNodes(getState, dispatch) {
|
||||
export function getTopologies(options, dispatch) {
|
||||
clearTimeout(topologyTimer);
|
||||
const optionsQuery = buildOptionsQuery(options);
|
||||
const url = `${apiPath}/topology?${optionsQuery}`;
|
||||
const url = `${getApiPath()}/api/topology?${optionsQuery}`;
|
||||
reqwest({
|
||||
url,
|
||||
type: 'json',
|
||||
success: (res) => {
|
||||
dispatch(receiveTopologies(res));
|
||||
topologyTimer = setTimeout(() => {
|
||||
@@ -173,11 +166,12 @@ export function getTopologies(options, dispatch) {
|
||||
});
|
||||
}
|
||||
|
||||
export function getNodesDelta(topologyUrl, options, dispatch) {
|
||||
export function getNodesDelta(topologyUrl, options, dispatch, forceReload) {
|
||||
const optionsQuery = buildOptionsQuery(options);
|
||||
// only recreate websocket if url changed or if forced (weave cloud instance reload);
|
||||
const isNewUrl = topologyUrl && (topologyUrl !== currentUrl || currentOptions !== optionsQuery);
|
||||
|
||||
// only recreate websocket if url changed
|
||||
if (topologyUrl && (topologyUrl !== currentUrl || currentOptions !== optionsQuery)) {
|
||||
if (forceReload || isNewUrl) {
|
||||
createWebsocket(topologyUrl, optionsQuery, dispatch);
|
||||
currentUrl = topologyUrl;
|
||||
currentOptions = optionsQuery;
|
||||
@@ -189,7 +183,7 @@ export function getNodeDetails(topologyUrlsById, currentTopologyId, options, nod
|
||||
const obj = nodeMap.last();
|
||||
if (obj && topologyUrlsById.has(obj.topologyId)) {
|
||||
const topologyUrl = topologyUrlsById.get(obj.topologyId);
|
||||
let urlComponents = [apiPath, '/', trimStart(topologyUrl, '/api'), '/', encodeURIComponent(obj.id)];
|
||||
let urlComponents = [getApiPath(), topologyUrl, '/', encodeURIComponent(obj.id)];
|
||||
if (currentTopologyId === obj.topologyId) {
|
||||
// Only forward filters for nodes in the current topology
|
||||
const optionsQuery = buildOptionsQuery(options);
|
||||
@@ -199,6 +193,7 @@ export function getNodeDetails(topologyUrlsById, currentTopologyId, options, nod
|
||||
|
||||
reqwest({
|
||||
url,
|
||||
type: 'json',
|
||||
success: (res) => {
|
||||
// make sure node is still selected
|
||||
if (nodeMap.has(res.node.id)) {
|
||||
@@ -222,9 +217,10 @@ export function getNodeDetails(topologyUrlsById, currentTopologyId, options, nod
|
||||
|
||||
export function getApiDetails(dispatch) {
|
||||
clearTimeout(apiDetailsTimer);
|
||||
const url = apiPath;
|
||||
const url = `${getApiPath()}/api`;
|
||||
reqwest({
|
||||
url,
|
||||
type: 'json',
|
||||
success: (res) => {
|
||||
dispatch(receiveApiDetails(res));
|
||||
apiDetailsTimer = setTimeout(() => {
|
||||
@@ -243,11 +239,12 @@ export function getApiDetails(dispatch) {
|
||||
|
||||
export function doControlRequest(nodeId, control, dispatch) {
|
||||
clearTimeout(controlErrorTimer);
|
||||
const url = `${apiPath}/control/${encodeURIComponent(control.probeId)}/`
|
||||
const url = `${getApiPath()}/api/control/${encodeURIComponent(control.probeId)}/`
|
||||
+ `${encodeURIComponent(control.nodeId)}/${control.id}`;
|
||||
reqwest({
|
||||
method: 'POST',
|
||||
url,
|
||||
type: 'json',
|
||||
success: (res) => {
|
||||
dispatch(receiveControlSuccess(nodeId));
|
||||
if (res) {
|
||||
@@ -279,12 +276,13 @@ export function doControlRequest(nodeId, control, dispatch) {
|
||||
|
||||
|
||||
export function doResizeTty(pipeId, control, cols, rows) {
|
||||
const url = `${apiPath}/control/${encodeURIComponent(control.probeId)}/`
|
||||
const url = `${getApiPath()}/api/control/${encodeURIComponent(control.probeId)}/`
|
||||
+ `${encodeURIComponent(control.nodeId)}/${control.id}`;
|
||||
|
||||
return reqwest({
|
||||
method: 'POST',
|
||||
url,
|
||||
type: 'json',
|
||||
data: JSON.stringify({pipeID: pipeId, width: cols.toString(), height: rows.toString()}),
|
||||
})
|
||||
.fail((err) => {
|
||||
@@ -294,10 +292,11 @@ export function doResizeTty(pipeId, control, cols, rows) {
|
||||
|
||||
|
||||
export function deletePipe(pipeId, dispatch) {
|
||||
const url = `${apiPath}/pipe/${encodeURIComponent(pipeId)}`;
|
||||
const url = `${getApiPath()}/api/pipe/${encodeURIComponent(pipeId)}`;
|
||||
reqwest({
|
||||
method: 'DELETE',
|
||||
url,
|
||||
type: 'json',
|
||||
success: () => {
|
||||
log('Closed the pipe!');
|
||||
},
|
||||
@@ -310,10 +309,11 @@ export function deletePipe(pipeId, dispatch) {
|
||||
|
||||
|
||||
export function getPipeStatus(pipeId, dispatch) {
|
||||
const url = `${apiPath}/pipe/${encodeURIComponent(pipeId)}/check`;
|
||||
const url = `${getApiPath()}/api/pipe/${encodeURIComponent(pipeId)}/check`;
|
||||
reqwest({
|
||||
method: 'GET',
|
||||
url,
|
||||
type: 'json',
|
||||
complete: (res) => {
|
||||
const status = {
|
||||
204: 'PIPE_ALIVE',
|
||||
|
||||
Reference in New Issue
Block a user