Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions test/helpers/dashboard-url.helper.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const should = require('should') // eslint-disable-line no-unused-vars

describe('dashboard API URL helper', function () {
let getDashboardApiUrl

before(async function () {
const module = await import('../../ui/src/api/dashboard-url.mjs')
getDashboardApiUrl = module.getDashboardApiUrl
})

it('builds the default dashboard API URL', function () {
const url = getDashboardApiUrl(
'http://localhost:1880/dashboard/page?edit-key=key',
'',
'/dashboard',
'dashboard-id',
'flows'
)

url.href.should.equal('http://localhost:1880/dashboard/api/v1/dashboard-id/flows')
})

it('does not duplicate a shared admin and node root', function () {
const url = getDashboardApiUrl(
'http://localhost:1880/node-red/dashboard/page?edit-key=key',
'/node-red/',
'/dashboard/',
'dashboard-id',
'flows'
)

url.href.should.equal('http://localhost:1880/node-red/dashboard/api/v1/dashboard-id/flows')
})

it('uses the admin root when the node root is different', function () {
const url = getDashboardApiUrl(
'http://localhost:1880/node-red/dashboard/page?edit-key=key',
'/admin',
'/dashboard',
'dashboard-id',
'flows'
)

url.href.should.equal('http://localhost:1880/admin/dashboard/api/v1/dashboard-id/flows')
})
})
22 changes: 22 additions & 0 deletions ui/src/api/dashboard-url.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
function trimPathPart (part) {
return String(part || '').replace(/^\/+|\/+$/g, '')
}

function getDashboardApiUrl (locationHref, editorPath, dashboardPath, dashboardId, ...path) {
const result = new URL(locationHref)
const pathParts = [
editorPath,
dashboardPath,
'api',
'v1',
dashboardId,
...path
].map(trimPathPart).filter(Boolean)

result.pathname = pathParts.join('/')
result.search = ''
result.hash = ''
return result
}

export { getDashboardApiUrl }
31 changes: 5 additions & 26 deletions ui/src/api/node-red.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import axios from 'axios'

import { getDashboardApiUrl } from './dashboard-url.mjs'

function applyAuth (headers, editorPath) {
let itemName = 'auth-tokens'
if (editorPath) {
Expand All @@ -18,46 +20,23 @@ function applyAuth (headers, editorPath) {
return headers
}

/**
* Generates a full dashboard api url based off the current location and the dashboard id
* The path parts are appended to the url
* @example
* // returns a URL object for 'http://localhost:1880/dashboard/api/v1/my-dashboard/flows'
* const url = getDashboardApiUrl('', 'my-dashboard', 'flows')
* @param {String} editorPath - The node-red editor path as provided by httpAdminRoot (e.g. 'editor') (can be empty)
* @param {String} dashboardId - The dashboard id
* @param {...any} path - 1 or more path parts to append to the url
* @returns {String} The full dashboard api url
*/
function getDashboardApiUrl (editorPath, dashboardId, ...path) {
const url = new URL(window.location.href)
const urlBase = url.pathname.split('/').slice(0, -1).join('/') + '/api/v1/' // e.g 'http://localhost:1880/dashboard/api/v1/'
const pathParts = [urlBase, dashboardId, ...(path || [])].map(p => p.replace(/^\/|\/$/g, ''))
if (editorPath) {
pathParts.unshift(editorPath.replace(/\/$/, '').replace(/^\/+/, ''))
}
const result = new URL(url)
result.search = ''
result.pathname = pathParts.join('/')
return result
}

export default {
/**
* Deploy changes to the Node-RED instance via the dashboards httpAdmin endpoint
* @param {Object} options - The options to deploy changes via the http admin endpoint
* @param {string} options.dashboard - The dashboard id
* @param {string} options.dashboardPath - The dashboard base path
* @param {string} options.page - The page id
* @param {string} options.key - The edit key for verification
* @param {Array<Object>} options.groups - The updated group objects to apply
* @param {Array<Object>} options.widgets - The updated widget objects to apply
* @returns the axios request
*/
deployChanges: async function deployChangesViaHttpAdminEndpoint ({ dashboard, page, groups, widgets, key, editorPath }) {
deployChanges: async function deployChangesViaHttpAdminEndpoint ({ dashboard, dashboardPath, page, groups, widgets, key, editorPath }) {
const changes = { groups, widgets }
return axios.request({
method: 'PATCH',
url: getDashboardApiUrl(editorPath || '', dashboard, 'flows'),
url: getDashboardApiUrl(window.location.href, editorPath || '', dashboardPath, dashboard, 'flows'),
headers: applyAuth({ 'Content-type': 'application/json' }, editorPath),
data: { dashboard, page, key, changes }
})
Expand Down
1 change: 1 addition & 0 deletions ui/src/layouts/wysiwyg/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export default {

return NodeRedApi.deployChanges({
dashboard,
dashboardPath: this.$store.getters['ui/dashboards']?.[dashboard]?.path || 'dashboard',
page,
groups: currentPageGroups,
widgets: normalisedWidgets,
Expand Down