Skip to content

Commit

Permalink
feature: add support to DELETE meta data (#1728)
Browse files Browse the repository at this point in the history
  • Loading branch information
sbender9 committed May 11, 2024
1 parent 973091a commit 0756f1d
Show file tree
Hide file tree
Showing 5 changed files with 395 additions and 6 deletions.
24 changes: 22 additions & 2 deletions src/interfaces/playground.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

const Parser0183 = require('@signalk/nmea0183-signalk')
const N2kMapper = require('@signalk/n2k-signalk').N2kMapper
const { putPath } = require('../put')
const { putPath, deletePath } = require('../put')
const {
isN2KString,
FromPgn,
Expand Down Expand Up @@ -67,7 +67,7 @@ module.exports = function (app) {

if (first.pgn) {
type = 'n2k-json'
} else if (first.updates || first.put) {
} else if (first.updates || first.put || first.delete) {
type = 'signalk'
} else {
return { error: 'unknown JSON format' }
Expand Down Expand Up @@ -139,6 +139,26 @@ module.exports = function (app) {
)
})
)
} else if (msg.delete) {
puts.push(
new Promise((resolve) => {
setTimeout(() => {
resolve('Timed out waiting for put result')
}, 5000)
deletePath(
app,
msg.context,
msg.delete.path,
req,
msg.requestId,
(reply) => {
if (reply.state !== 'PENDING') {
resolve(reply)
}
}
)
})
)
} else {
app.handleMessage('input-test', msg)
}
Expand Down
28 changes: 27 additions & 1 deletion src/interfaces/ws.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const {
updateRequest,
queryRequest
} = require('../requestResponse')
const { putPath } = require('../put')
const { putPath, deletePath } = require('../put')
import { createDebug } from '../debug'
import { JsonWebTokenError, TokenExpiredError } from 'jsonwebtoken'
import { startEvents, startServerEvents } from '../events'
Expand Down Expand Up @@ -235,6 +235,10 @@ module.exports = function (app) {
processPutRequest(spark, msg)
}

if (msg.delete) {
processDeleteRequest(spark, msg)
}

if (msg.requestId && msg.query) {
processReuestQuery(spark, msg)
}
Expand Down Expand Up @@ -348,6 +352,28 @@ module.exports = function (app) {
})
}

function processDeleteRequest(spark, msg) {
deletePath(
app,
msg.context,
msg.delete.path,
spark.request,
msg.requestId,
(reply) => {
debug('sending put update %j', reply)
spark.write(reply)
}
).catch((err) => {
console.error(err)
spark.write({
requestId: msg.requestId,
state: 'COMPLETED',
statusCode: 502,
message: err.message
})
})
}

function processAccessRequest(spark, msg) {
if (spark.skPendingAccessRequest) {
spark.write({
Expand Down
207 changes: 204 additions & 3 deletions src/put.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createDebug } from './debug'
const debug = createDebug('signalk-server:put')
const { createRequest, updateRequest } = require('./requestResponse')
const skConfig = require('./config/config')
const { getMetadata } = require('@signalk/signalk-schema')

const pathPrefix = '/signalk'
const versionPrefix = '/v1'
Expand All @@ -24,13 +25,39 @@ const Result = {
}

const actionHandlers = {}
let putMetaHandler
let putMetaHandler, deleteMetaHandler

module.exports = {
start: function (app) {
app.registerActionHandler = registerActionHandler
app.deRegisterActionHandler = deRegisterActionHandler

app.delete(apiPathPrefix + '*', function (req, res) {
let path = String(req.path).replace(apiPathPrefix, '')

path = path.replace(/\/$/, '').replace(/\//g, '.')

const parts = path.length > 0 ? path.split('.') : []

if (parts.length < 3) {
res.status(400).send('invalid path')
return
}

const context = `${parts[0]}.${parts[1]}`
const skpath = parts.slice(2).join('.')

deletePath(app, context, skpath, req)
.then((reply) => {
res.status(reply.statusCode)
res.json(reply)
})
.catch((err) => {
console.error(err)
res.status(500).send(err.message)
})
})

app.put(apiPathPrefix + '*', function (req, res) {
let path = String(req.path).replace(apiPathPrefix, '')

Expand Down Expand Up @@ -82,7 +109,22 @@ module.exports = {
}

app.config.baseDeltaEditor.setMeta(context, metaPath, metaValue)
skConfig.sendBaseDeltas(app)

let full_meta = getMetadata('vessels.self.' + metaPath)

app.handleMessage('defaults', {
context: 'vessels.self',
updates: [
{
meta: [
{
path: metaPath,
value: { ...full_meta, ...metaValue }
}
]
}
]
})

if (app.config.hasOldDefaults) {
let data
Expand Down Expand Up @@ -122,10 +164,169 @@ module.exports = {

return { state: 'PENDING' }
}

deleteMetaHandler = (context, path, cb) => {
let parts = path.split('.')
let metaPath = path
let full_meta

//fixme, make sure meta path exists...

if (parts[parts.length - 1] !== 'meta') {
let name = parts[parts.length - 1]
metaPath = parts.slice(0, parts.length - 2).join('.')

let metaValue = {
...app.config.baseDeltaEditor.getMeta(context, metaPath)
}

if (typeof metaValue[name] === 'undefined') {
return { state: 'COMPLETED', statusCode: 404 }
}

delete metaValue[name]

full_meta = getMetadata('vessels.self.' + metaPath)
delete full_meta[name]

app.config.baseDeltaEditor.setMeta(context, metaPath, metaValue)

if (Object.keys(metaValue).length == 0) {
app.config.baseDeltaEditor.removeMeta(context, metaPath)
}
} else {
metaPath = parts.slice(0, parts.length - 1).join('.')

full_meta = getMetadata('vessels.self.' + metaPath)
let metaValue = app.config.baseDeltaEditor.getMeta(context, metaPath)

if (!metaValue) {
return { state: 'COMPLETED', statusCode: 404 }
}

Object.keys(metaValue).forEach((key) => {
delete full_meta[key]
})

app.config.baseDeltaEditor.removeMeta(context, metaPath)
}

app.handleMessage('defaults', {
context: 'vessels.self',
updates: [
{
meta: [
{
path: metaPath,
value: full_meta
}
]
}
]
})

skConfig
.writeBaseDeltasFile(app, app.config.baseDeltas)
.then(() => {
cb({ state: 'COMPLETED', statusCode: 200 })
})
.catch(() => {
cb({
state: 'COMPLETED',
statusCode: 502,
message: 'Unable to save to defaults file'
})
})

return { state: 'PENDING' }
}
},

registerActionHandler: registerActionHandler,
putPath: putPath
putPath: putPath,
deletePath
}

function deletePath(app, contextParam, path, req, requestId, updateCb) {
const context = contextParam || 'vessels.self'
debug('received delete %s %s', context, path)
return new Promise((resolve, reject) => {
createRequest(
app,
'delete',
{
context: context,
requestId: requestId,
delete: { path: path }
},
req && req.skPrincipal ? req.skPrincipal.identifier : undefined,
null,
updateCb
)
.then((request) => {
if (
req &&
app.securityStrategy.shouldAllowPut(req, context, null, path) ===
false
) {
updateRequest(request.requestId, 'COMPLETED', { statusCode: 403 })
.then(resolve)
.catch(reject)
return
}

const parts = path.split('.')
let handler

if (
(parts.length > 1 && parts[parts.length - 1] === 'meta') ||
(parts.length > 1 && parts[parts.length - 2] === 'meta')
) {
handler = deleteMetaHandler
}

if (handler) {
const actionResult = handler(context, path, (reply) => {
debug('got result: %j', reply)
updateRequest(request.requestId, reply.state, reply)
.then(() => undefined)
.catch((err) => {
console.error(err)
})
})

Promise.resolve(actionResult)
.then((result) => {
debug('got result: %j', result)
updateRequest(request.requestId, result.state, result)
.then((reply) => {
if (reply.state === 'PENDING') {
// backwards compatibility
reply.action = { href: reply.href }
}
resolve(reply)
})
.catch(reject)
})
.catch((err) => {
updateRequest(request.requestId, 'COMPLETED', {
statusCode: 500,
message: err.message
})
.then(resolve)
.catch(reject)
})
} else {
updateRequest(request.requestId, 'COMPLETED', {
statusCode: 405,
message: `DELTETE not supported for ${path}`
})
.then(resolve)
.catch(reject)
}
})
.catch(reject)
})
}

function putPath(app, contextParam, path, body, req, requestId, updateCb) {
Expand Down

0 comments on commit 0756f1d

Please sign in to comment.