Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Layer UUIDs #308

Merged
merged 3 commits into from
Jan 19, 2023
Merged
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
80 changes: 48 additions & 32 deletions API/Backend/Config/routes/configs.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ const Config = require("../models/config");
const config_template = require("../../../templates/config_template");

const validate = require("../validate");
const populateUUIDs = require("../uuids");
const Utils = require("../../../utils.js");

const websocket = require("../../../websocket.js");
const WebSocket = require('isomorphic-ws');
const WebSocket = require("isomorphic-ws");

const fs = require("fs");
const deepmerge = require("deepmerge");
Expand Down Expand Up @@ -294,6 +295,8 @@ function upsert(req, res, next, cb, info) {
}
} else configJSON = req.body.config;
}

const newlyAddedUUIDs = populateUUIDs(configJSON);
const validation = validate(configJSON);

if (!validation.valid) {
Expand Down Expand Up @@ -332,20 +335,26 @@ function upsert(req, res, next, cb, info) {
status: "success",
mission: created.mission,
version: created.version,
newlyAddedUUIDs: newlyAddedUUIDs,
});
else
res.send({
status: "success",
mission: created.mission,
version: created.version,
newlyAddedUUIDs: newlyAddedUUIDs,
});
openWebSocket(req.body, {
status: "success",
mission: created.mission,
version: created.version,
}, info,
forceClientUpdate
);
openWebSocket(
req.body,
{
status: "success",
mission: created.mission,
version: created.version,
newlyAddedUUIDs: newlyAddedUUIDs,
},
info,
forceClientUpdate
);
return null;
})
.catch((err) => {
Expand Down Expand Up @@ -577,12 +586,13 @@ if (fullAccess)
function openWebSocket(body, response, info, forceClientUpdate) {
if (
!process.env.hasOwnProperty("ENABLE_MMGIS_WEBSOCKETS") ||
process.env.ENABLE_MMGIS_WEBSOCKETS != "true") {
return
process.env.ENABLE_MMGIS_WEBSOCKETS != "true"
) {
return;
}

const port = parseInt(process.env.PORT || "8888", 10);
const path = `ws://localhost:${port}/`
const path = `ws://localhost:${port}/`;
const ws = new WebSocket(path);
ws.onopen = function () {
const data = {
Expand All @@ -591,15 +601,15 @@ function openWebSocket(body, response, info, forceClientUpdate) {
forceClientUpdate,
};
ws.send(JSON.stringify(data));
}
};
}

// === Quick API Functions ===
function addLayer(req, res, next, cb, forceConfig, caller = "addLayer") {
const exampleBody = {
mission: "{mission_name}",
layer: {
name: "{new_unique_layer_name}",
name: "{new_layer_name}",
type: "header || vector || vectortile || query || model || tile || data",
"more...": "...",
},
Expand Down Expand Up @@ -694,7 +704,11 @@ function addLayer(req, res, next, cb, forceConfig, caller = "addLayer") {
if (didSet) {
upsert(
{
body: { mission: req.body.mission, config: config, forceClientUpdate: req.body.forceClientUpdate },
body: {
mission: req.body.mission,
config: config,
forceClientUpdate: req.body.forceClientUpdate,
},
},
null,
null,
Expand All @@ -706,13 +720,15 @@ function addLayer(req, res, next, cb, forceConfig, caller = "addLayer") {
message: `Added layer to the ${response.mission} mission. Configuration versioned ${response.version}.`,
mission: response.mission,
version: response.version,
newlyAddedUUIDs: response.newlyAddedUUIDs,
});
} else {
res.send({
status: "success",
message: `Added layer to the ${response.mission} mission. Configuration versioned ${response.version}.`,
mission: response.mission,
version: response.version,
newlyAddedUUIDs: response.newlyAddedUUIDs,
});
}
} else {
Expand Down Expand Up @@ -764,7 +780,7 @@ if (fullAccess)
router.post("/updateLayer", function (req, res, next) {
const exampleBody = {
mission: "{mission_name}",
layerName: "{existing_layer_name}",
layerUUID: "{existing_layer_uuid}",
layer: {
"...": "...",
},
Expand All @@ -785,10 +801,10 @@ if (fullAccess)
});
return;
}
if (req.body.layerName == null) {
if (req.body.layerUUID == null) {
res.send({
status: "failure",
message: `Required parameter 'layerName' is unset. (a layer.name is not sufficient)`,
message: `Required parameter 'layerUUID' is unset. (a layer.uuid is not sufficient)`,
example: exampleBody,
});
return;
Expand Down Expand Up @@ -821,7 +837,7 @@ if (fullAccess)
let placementIndex = req.body.placement?.index;

Utils.traverseLayers(config.layers, (layer, path, index) => {
if (layer.name === req.body.layerName) {
if (layer.uuid === req.body.layerUUID) {
existingLayer = JSON.parse(JSON.stringify(layer));
if (placementPath == null) placementPath = path;
if (placementIndex == null) placementIndex = index;
Expand All @@ -832,7 +848,7 @@ if (fullAccess)
if (existingLayer == null) {
res.send({
status: "failure",
message: `Layer ${req.body.layerName} not found. Cannot update.`,
message: `Layer ${req.body.layerUUID} not found. Cannot update.`,
});
return;
}
Expand Down Expand Up @@ -862,7 +878,7 @@ if (fullAccess)
if (resp.status === "success") {
res.send({
status: "success",
message: `Updated layer '${req.body.layerName}' in the ${resp.mission} mission. Configuration versioned ${resp.version}.`,
message: `Updated layer '${req.body.layerUUID}' in the ${resp.mission} mission. Configuration versioned ${resp.version}.`,
});
} else {
resp.message = `Update layer failed with: ${resp.message}`;
Expand All @@ -876,14 +892,14 @@ if (fullAccess)
} catch (err) {
logger(
"error",
`Failed to update layer: ${req.body.layerName}.`,
`Failed to update layer: ${req.body.layerUUID}.`,
req.originalUrl,
req,
err
);
res.send({
status: "failure",
message: `Failed to update layer: ${req.body.layerName}. Uncaught reason.`,
message: `Failed to update layer: ${req.body.layerUUID}. Uncaught reason.`,
});
}
}
Expand All @@ -894,7 +910,7 @@ if (fullAccess)
function removeLayer(req, res, next, cb) {
const exampleBody = {
mission: "{mission_name}",
layerName: "{existing_layer_name}",
layerUUID: "{existing_layer_uuid}",
"forceClientUpdate?": "{true}; default false",
};

Expand All @@ -906,10 +922,10 @@ function removeLayer(req, res, next, cb) {
});
return;
}
if (req.body.layerName == null) {
if (req.body.layerUUID == null) {
res.send({
status: "failure",
message: `Required parameter 'layerName' is unset.`,
message: `Required parameter 'layerUUID' is unset.`,
example: exampleBody,
});
return;
Expand All @@ -930,7 +946,7 @@ function removeLayer(req, res, next, cb) {
try {
let didRemove = false;
Utils.traverseLayers(config.layers, (layer, path, index) => {
if (layer.name === req.body.layerName) {
if (layer.uuid === req.body.layerUUID) {
didRemove = true;
return "remove";
}
Expand All @@ -951,24 +967,24 @@ function removeLayer(req, res, next, cb) {
if (resp.status === "success") {
res.send({
status: "success",
message: `Successfully removed layer '${req.body.layerName}'.`,
message: `Successfully removed layer '${req.body.layerUUID}'.`,
});
} else {
res.send({
status: "failure",
message: `Failed to remove layer '${req.body.layerName}': ${resp.message}`,
message: `Failed to remove layer '${req.body.layerUUID}': ${resp.message}`,
});
}
},
{
type: 'removeLayer',
layerName: req.body.layerName,
type: "removeLayer",
layerName: req.body.layerUUID,
}
);
} else {
res.send({
status: "failure",
message: `Failed to remove layer '${req.body.layerName}'. Layer not found.`,
message: `Failed to remove layer '${req.body.layerUUID}'. Layer not found.`,
});
}
} catch (err) {}
Expand All @@ -981,7 +997,7 @@ if (fullAccess)
* /removeLayer
* body: {
"mission": "",
"layerName": ""
"layerUUID": ""
"forceClientUpdate?": true
}
*/
Expand Down
28 changes: 28 additions & 0 deletions API/Backend/Config/uuids.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const Utils = require("../../utils.js");

const { v4: uuidv4, validate: uuidValidate } = require("uuid");

const populateUUIDs = (config) => {
const newlyAddedUUIDs = [];

Utils.traverseLayers(config.layers, (layer) => {
if (layer.uuid == null) {
layer.uuid = uuidv4();
newlyAddedUUIDs.push({
name: layer.name,
uuid: layer.uuid,
});
} else if (!uuidValidate(layer.uuid)) {
const badUUID = layer.uuid;
layer.uuid = uuidv4();
newlyAddedUUIDs.push({
name: layer.name,
uuid: layer.uuid,
replacesBadUUID: badUUID,
});
}
});
return newlyAddedUUIDs;
};

module.exports = populateUUIDs;
29 changes: 0 additions & 29 deletions API/Backend/Config/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ const validateLayers = (config) => {
}
});

errs = errs.concat(hasDuplicateLayerNames(config));
errs = errs.concat(hasNonHeaderWithSublayers(config));

return errs;
Expand Down Expand Up @@ -291,34 +290,6 @@ const hasNonHeaderWithSublayers = (config) => {
return errs;
};

const hasDuplicateLayerNames = (config) => {
let allNames = [];

depthTraversal(config.layers, 0);

function depthTraversal(node, depth) {
for (var i = 0; i < node.length; i++) {
allNames.push(node[i].name);
//Add other feature information while we're at it
if (node[i].sublayers != null && node[i].sublayers.length > 0) {
depthTraversal(node[i].sublayers, depth + 1);
}
}
}

let unique = [];
const errs = [];
allNames.forEach((name) => {
if (!unique.includes(name)) unique.push(name);
else
errs.push(
err(`Found duplicate layer name: '${name}'`, ["layers[layer].name"])
);
});

return errs;
};

const fillInMissingFieldsWithDefaults = (layer) => {
if (layer.type != "header") {
layer.initialOpacity =
Expand Down
2 changes: 1 addition & 1 deletion API/Backend/Draw/routes/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const express = require("express");
const logger = require("../../../logger");
const database = require("../../../database");
const Sequelize = require("sequelize");
const uuidv4 = require("uuid/v4");
const { v4: uuidv4 } = require("uuid");
const fhistories = require("../models/filehistories");
const Filehistories = fhistories.Filehistories;
const FilehistoriesTEST = fhistories.FilehistoriesTEST;
Expand Down
6 changes: 6 additions & 0 deletions config/css/config.css
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,12 @@ textarea {
.modal .clone:hover {
color: #000;
}
#modal_uuid {
text-align: center;
margin: 20px 0px;
font-size: 14px;
color: #555;
}

#toast-container {
pointer-events: none;
Expand Down
1 change: 1 addition & 0 deletions config/js/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -1561,6 +1561,7 @@ function makeLayerBarAndModal(d, level, options) {
"</div>" +

"</p>" +
"<div id='modal_uuid'>Layer UUID: " + d.uuid + "</div>" +
"</div>" +

"<div class='modal-footer' style='background-color: " + barColor + "; display: flex; justify-content: space-between;'>" +
Expand Down
8 changes: 4 additions & 4 deletions docs/pages/APIs/Configure/Configure_REST_API.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,15 @@ Updates a single layer. Specified layer values are deep merged and overwrite exi
| Parameter | Type | Required | Default | Description |
| :-------------------: | :-------: | :------: | :-----: | :----------------------------------------------------------------------------------------------------------------: |
| **mission** | _string_ | true | N/A | Mission name |
| **layerName** | _string_ | true | N/A | Layer to update |
| **layerUUID** | _string_ | true | N/A | Layer to update |
| **layer** | _object_ | true | N/A | A partial layer configuration object. See browser console-network tab responses for examples. |
| **placement.path** | _string_ | false | '' | A path to a header in 'layers' to place the new layer. A simple path ('sublayers' are added). Defaults to no group |
| **placement.index** | _number_ | false | end | Index in 'layers' (or path) to place the new layer. Out of range placement indices are best fit. |
| **forceClientUpdate** | _boolean_ | false | false | Push the change out to clients. |

#### Example

`curl -X POST -H "Authorization:Bearer <token>" -H "Content-Type: application/json" -d '{"mission":"Test", "layerName":"name", "layer":{}}' http://localhost:8889/api/configure/updateLayer`
`curl -X POST -H "Authorization:Bearer <token>" -H "Content-Type: application/json" -d '{"mission":"Test", "layerUUID":"uuid", "layer":{}}' http://localhost:8889/api/configure/updateLayer`

---

Expand All @@ -151,12 +151,12 @@ Removes a single layer from the configuration object.
| Parameter | Type | Required | Default | Description |
| :-------------------: | :-------: | :------: | :-----: | :-----------------------------: |
| **mission** | _string_ | true | N/A | Mission name |
| **layerName** | _string_ | true | N/A | Layer to update |
| **layerUUID** | _string_ | true | N/A | Layer to update |
| **forceClientUpdate** | _boolean_ | false | false | Push the change out to clients. |

#### Example

`curl -X POST -H "Authorization:Bearer <token>" -H "Content-Type: application/json" -d '{"mission":"Test", "layerName":"name"}' http://localhost:8889/api/configure/removeLayer`
`curl -X POST -H "Authorization:Bearer <token>" -H "Content-Type: application/json" -d '{"mission":"Test", "layerUUID":"name"}' http://localhost:8889/api/configure/removeLayer`

---

Expand Down
Loading