diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 5b326dbe2..96a520a45 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -56,6 +56,8 @@ jobs: uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} + - name: Generate self-signed certificate + run: ./scripts/ci_generate_certificate.sh - run: npm install - run: npm run test-older env: diff --git a/.gitignore b/.gitignore index f52db00d1..c9c63328c 100644 --- a/.gitignore +++ b/.gitignore @@ -57,4 +57,5 @@ libraries/objectLogs/* /screenshots /spatialToolbox /vuforia-spatial-toolbox-userinterface +/.vscode /*.pem diff --git a/README.md b/README.md index c2b45a509..958439738 100644 --- a/README.md +++ b/README.md @@ -56,10 +56,17 @@ on GitHub. Fork this repository using the button on the top right, make and commit your changes, then GitHub will prompt you to make a pull request. ### Automated Tests + Note that we do run some automated testing to ensure that our code remains consistently styled and functional. If you want to see the results of this -testing locally, run the following command in your vuforia-toolbox-server -folder: +testing locally, you can follow +[this Github Actions workflow](https://github.com/ptcrealitylab/vuforia-spatial-edge-server/blob/master/.github/workflows/nodejs.yml). + +The most important parts of the workflow are running the commands of +[scripts/ci.sh](https://github.com/ptcrealitylab/vuforia-spatial-edge-server/blob/master/scripts/ci.sh) +to setup (note that some repos may not be available, causing test failures +locally). After this, you can run tests using the following command in your +vuforia-toolbox-server folder: ```bash npm run test diff --git a/generate_cert.ps1 b/generate_cert.ps1 new file mode 100644 index 000000000..8a75f2732 --- /dev/null +++ b/generate_cert.ps1 @@ -0,0 +1,4 @@ +$openssl = (Join-Path (Split-Path -Parent (Get-Command git).Path) "../mingw64/bin/openssl.exe") + +# generate certificate and key +& $openssl req -x509 -newkey rsa:4096 -nodes -keyout key.pem -sha256 -days 356 -out cert.pem diff --git a/generate_cert.sh b/generate_cert.sh new file mode 100644 index 000000000..36d2bb780 --- /dev/null +++ b/generate_cert.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +# generate certificate and key +libressl req -x509 -newkey rsa:4096 -nodes -keyout key.pem -sha256 -days 356 -out cert.pem diff --git a/libraries/hardwareInterfaces.js b/libraries/hardwareInterfaces.js index 25103962f..f4b12f6e6 100644 --- a/libraries/hardwareInterfaces.js +++ b/libraries/hardwareInterfaces.js @@ -734,7 +734,7 @@ exports.hasTool = function(object, tool) { }; exports.getObjectIdFromObjectName = async function (object) { - return await utilities.getObjectIdFromTargetOrObjectFile(object); + return await utilities.getObjectIdFromObjectFile(object); }; exports.getObjectNameFromObjectId = function (objectId) { diff --git a/libraries/nodeUtilities.js b/libraries/nodeUtilities.js index a072683d0..227ccb12c 100644 --- a/libraries/nodeUtilities.js +++ b/libraries/nodeUtilities.js @@ -16,7 +16,7 @@ exports.deepCopy = utilities.deepCopy; exports.searchNodeByType = async function (nodeType, objectKey, tool, node, callback) { let thisObjectKey = objectKey; if (!(objectKey in objects)) { - thisObjectKey = await utilities.getObjectIdFromTargetOrObjectFile(objectKey); + thisObjectKey = await utilities.getObjectIdFromObjectFile(objectKey); } let thisObject = utilities.getObject(objects, thisObjectKey); if (!thisObject) { diff --git a/libraries/utilities.js b/libraries/utilities.js index 3725786c4..60f55a71e 100644 --- a/libraries/utilities.js +++ b/libraries/utilities.js @@ -63,6 +63,7 @@ const dgram = require('dgram'); // UDP Broadcasting library const path = require('path'); const request = require('request'); const fetch = require('node-fetch'); +const DecompressZip = require('decompress-zip'); const ObjectModel = require('../models/ObjectModel.js'); const {objectsPath, beatPort} = require('../config.js'); const {isLightweightMobile} = require('../isMobile.js'); @@ -185,38 +186,15 @@ exports.uuidTime = function () { return '_' + stampUuidTime; }; -async function getObjectIdFromTargetOrObjectFile(folderName) { +async function getObjectIdFromObjectFile(folderName) { if (folderName === 'allTargetsPlaceholder') { return 'allTargetsPlaceholder000000000000'; } - var xmlFile = objectsPath + '/' + folderName + '/' + identityFolderName + '/target/target.xml'; - var jsonFile = objectsPath + '/' + folderName + '/' + identityFolderName + '/object.json'; + let jsonFile = objectsPath + '/' + folderName + '/' + identityFolderName + '/object.json'; - if (await fileExists(xmlFile)) { - try { - let resultXML = ''; - xml2js.Parser().parseString(await fsProm.readFile(xmlFile, 'utf8'), - function (err, result) { - for (var first in result) { - for (var secondFirst in result[first].Tracking[0]) { - resultXML = result[first].Tracking[0][secondFirst][0].$.name; - if (typeof resultXML === 'string' && resultXML.length === 0) { - console.warn('Target file for ' + folderName + ' has empty name, ' + - 'and may not function correctly. Delete and re-upload target for best results.'); - resultXML = null; - } - break; - } - break; - } - }); - return resultXML; - } catch (e) { - console.error('error reading xml file', e); - } - } else if (await fileExists(jsonFile)) { + if (await fileExists(jsonFile)) { try { let thisObject = JSON.parse(await fsProm.readFile(jsonFile, 'utf8')); if (thisObject.hasOwnProperty('objectId')) { @@ -230,7 +208,7 @@ async function getObjectIdFromTargetOrObjectFile(folderName) { } return null; } -exports.getObjectIdFromTargetOrObjectFile = getObjectIdFromTargetOrObjectFile; +exports.getObjectIdFromObjectFile = getObjectIdFromObjectFile; async function getAnchorIdFromObjectFile(folderName) { @@ -254,6 +232,99 @@ async function getAnchorIdFromObjectFile(folderName) { } exports.getAnchorIdFromObjectFile = getAnchorIdFromObjectFile; +/** + * Given a target folder, unzips the target.dat file within it and retrieves the targetId from config.info + * @param {string} targetFolderPath + * @returns {Promise} + */ +exports.getTargetIdFromTargetDat = async function getTargetIdFromTargetDat(targetFolderPath) { + return new Promise((resolve, reject) => { + // unzip the .dat file and read the unique targetId from the config.info file + let unzipperDat = new DecompressZip(path.join(targetFolderPath, 'target.dat')); + + unzipperDat.on('error', function (err) { + console.error('.dat Unzipper Error', err); + reject(err); + }); + + unzipperDat.on('extract', async function () { + let configFilePath = path.join(targetFolderPath, 'config.info'); + if (await fileExists(configFilePath)) { + // read the id stored within the config.info file (it's actually structured as XML) + let targetUniqueId = await getTargetIdFromConfigFile(configFilePath); + // TODO: cleanup config.info file instead of leaving it in the folder + resolve(targetUniqueId); + } else { + reject('config.info not found at ' + configFilePath); + } + }); + + unzipperDat.on('progress', function (_fileIndex, _fileCount) { + // console.log('Extracted dat file ' + (fileIndex + 1) + ' of ' + fileCount); + }); + + unzipperDat.extract({ + path: targetFolderPath, + filter: function (file) { + return file.type !== 'SymbolicLink' && file.filename.endsWith('info'); + } + }); + }); +}; + +/** + * Parses the file as XML and pulls out the targetId string + * @param {string} filePath + * @returns {Promise} + */ +async function getTargetIdFromConfigFile(filePath) { + if (!await fileExists(filePath)) { + return null; + } + + let contents; + try { + contents = await fsProm.readFile(filePath, 'utf8'); + } catch (err) { + console.error('Unable to read xml file for target ID', err); + return null; + } + + try { + return await queryXMLContents(contents, (xml) => { + // the file is structured like ... + // this gets the "AreaTarget"/"ImageTarget"/"ModelTarget" tag contents of the XML file + // and extracts the tag's properties, e.g. { version: "5.1", bbox: "...", targetId: "xzy": name: "_WORLD_test_xyz" } + return Object.entries(xml.QCARInfo.TargetSet[0]).find(entry => entry[0] !== '$')[1][0].$.targetId; + }); + } catch (err) { + console.error('Error parsing/querying XML contents', err); + return null; + } +} + +/** + * Parses the string as XML and searches the structured contents using the provided xmlQuery function + * @param {string} xmlContentsString - file contents + * @param {function} xmlQuery - the function that will be applied to the parsed contents to retrieve certain data + * @returns {Promise} + */ +async function queryXMLContents(xmlContentsString, xmlQuery) { + return new Promise(function (resolve, reject) { + xml2js.Parser().parseString(xmlContentsString, function (parseErr, result) { + try { + if (parseErr) { + throw parseErr; + } + resolve(xmlQuery(result)); + } catch (err) { + console.error('error parsing xml', err); + reject(err); + } + }); + }); +} + /** * * @param {string} folderName @@ -531,7 +602,7 @@ exports.updateObject = async function updateObject(objectName, objects) { var objectFolderList = await getObjectFolderList(); for (const objectFolder of objectFolderList) { - const tempFolderName = await getObjectIdFromTargetOrObjectFile(objectFolder); + const tempFolderName = await getObjectIdFromObjectFile(objectFolder); if (!tempFolderName) { console.warn(' object ' + objectFolder + ' has no marker yet'); diff --git a/libraries/webFrontend.js b/libraries/webFrontend.js index 117fbe4fa..efb32019f 100644 --- a/libraries/webFrontend.js +++ b/libraries/webFrontend.js @@ -121,7 +121,7 @@ exports.printFolder = async function printFolder(objects, objectsPath, _debug, o let tempKey; try { // gets the object id from the xml target file - tempKey = await utilities.getObjectIdFromTargetOrObjectFile(objectKey); + tempKey = await utilities.getObjectIdFromObjectFile(objectKey); } catch (e) { console.warn('printFolder getObjectId failed', e); } diff --git a/libraries/webInterface/gui/index.js b/libraries/webInterface/gui/index.js index be96cddc2..e4da73a8c 100644 --- a/libraries/webInterface/gui/index.js +++ b/libraries/webInterface/gui/index.js @@ -61,7 +61,7 @@ realityServer.getCommonContents = function () { return this.domObjects.querySelector('#commonContents'); }; -let remoteOperatorUrl = 'http://' + realityServer.states.ipAdress.interfaces[realityServer.states.ipAdress.activeInterface] + ':8081'; +let remoteOperatorUrl = 'https://' + realityServer.states.ipAdress.interfaces[realityServer.states.ipAdress.activeInterface] + ':8081'; let isRemoteOperatorSupported = false; realityServer.initialize = function () { @@ -462,7 +462,7 @@ realityServer.updateManageObjects = function (thisItem2) { thisObject.dom.querySelector('.active').classList.add('clickAble'); thisObject.dom.querySelector('.download').classList.add('clickAble'); - // let targetUrl = 'http://localhost:8080/obj/' + thisObject.name + '/target/target.jpg'; + // let targetUrl = 'https://localhost:8080/obj/' + thisObject.name + '/target/target.jpg'; // thisObject.dom.querySelector(".target").style.backgroundImage = 'url("' + targetUrl + '")'; // thisObject.dom.querySelector(".target").style.backgroundSize = 'cover'; @@ -512,7 +512,7 @@ realityServer.updateManageObjects = function (thisItem2) { // only add the icon if it exists if (thisObject.targetsExist.jpgExists) { let ipAddress = realityServer.states.ipAdress.interfaces[realityServer.states.ipAdress.activeInterface]; - thisObject.dom.querySelector('.objectTargetIcon').src = 'http://' + ipAddress + ':' + realityServer.states.serverPort + '/obj/' + thisObject.name + '/target/target.jpg'; + thisObject.dom.querySelector('.objectTargetIcon').src = 'https://' + ipAddress + ':' + realityServer.states.serverPort + '/obj/' + thisObject.name + '/target/target.jpg'; } } else { @@ -619,14 +619,14 @@ realityServer.updateManageObjects = function (thisItem2) { realityServer.switchClass(thisObject.dom.querySelector('.target'), 'yellow', 'green'); realityServer.switchClass(thisObject.dom.querySelector('.target'), 'targetWidthMedium', 'one'); - // let targetUrl = 'http://localhost:8080/obj/' + thisObject.name + '/target/target.jpg'; + // let targetUrl = 'https://localhost:8080/obj/' + thisObject.name + '/target/target.jpg'; // thisObject.dom.querySelector(".target").style.backgroundImage = 'url("' + targetUrl + '")'; // thisObject.dom.querySelector(".target").style.backgroundSize = 'cover'; let ipAddress = realityServer.states.ipAdress.interfaces[realityServer.states.ipAdress.activeInterface]; // add Image for Target if (thisObject.targetsExist.jpgExists) { - thisObject.dom.querySelector('.objectTargetIcon').src = 'http://' + ipAddress + ':' + realityServer.states.serverPort + '/obj/' + thisObject.name + '/target/target.jpg'; + thisObject.dom.querySelector('.objectTargetIcon').src = 'https://' + ipAddress + ':' + realityServer.states.serverPort + '/obj/' + thisObject.name + '/target/target.jpg'; } else if (thisObject.isAnchor) { thisObject.dom.querySelector('.objectTargetIcon').src = '../libraries/gui/resources/anchor.svg'; } @@ -727,7 +727,7 @@ realityServer.updateManageObjects = function (thisItem2) { setTooltipTextForElement(thisFullScreen.querySelector('.fullscreen'), 'Open the screen HMI configured by the vuforia-spatial-screens-addon.' + - ' (For this object: http://' + ipAddress + ': ' + thisObject.screenPort + ')'); + ' (For this object: https://' + ipAddress + ': ' + thisObject.screenPort + ')'); if (!thisObject.screenPort) { thisFullScreen.querySelector('.fullscreen').classList.remove('purple'); @@ -763,7 +763,7 @@ realityServer.updateManageObjects = function (thisItem2) { function addLinkToContent(buttonDiv, frameType) { // eslint-disable-line no-inner-declarations buttonDiv.addEventListener('click', function () { // put in a closure so it references don't mutate let ipAddress = realityServer.states.ipAdress.interfaces[realityServer.states.ipAdress.activeInterface]; - window.open('http://' + ipAddress + ':' + realityServer.states.serverPort + '/frames/' + frameType + '/index.html', '_blank'); // opens in new tab (instead of window.location.href = ) + window.open('https://' + ipAddress + ':' + realityServer.states.serverPort + '/frames/' + frameType + '/index.html', '_blank'); // opens in new tab (instead of window.location.href = ) }); } @@ -869,8 +869,8 @@ realityServer.updateManageFrames = function () { function addLinkToContent(buttonDiv, frameType) { // eslint-disable-line no-inner-declarations buttonDiv.addEventListener('click', function () { let ipAddress = realityServer.states.ipAdress.interfaces[realityServer.states.ipAdress.activeInterface]; - // window.location.href = 'http://' + ipAddress + ':8080/frames/active/' + frameType + '/index.html'; - window.open('http://' + ipAddress + ':' + realityServer.states.serverPort + '/frames/' + frameType + '/index.html', '_blank'); + // window.location.href = 'https://' + ipAddress + ':8080/frames/active/' + frameType + '/index.html'; + window.open('https://' + ipAddress + ':' + realityServer.states.serverPort + '/frames/' + frameType + '/index.html', '_blank'); }); } @@ -878,7 +878,7 @@ realityServer.updateManageFrames = function () { addLinkToContent(contentButton, frameKey); let ipAddress = realityServer.states.ipAdress.interfaces[realityServer.states.ipAdress.activeInterface]; - frameInfo.dom.querySelector('.frameIcon').src = 'http://' + ipAddress + ':' + realityServer.states.serverPort + '/frames/' + frameKey + '/icon.gif'; + frameInfo.dom.querySelector('.frameIcon').src = 'https://' + ipAddress + ':' + realityServer.states.serverPort + '/frames/' + frameKey + '/icon.gif'; addZipDownload(frameInfo.dom.querySelector('.download'), frameKey); @@ -922,7 +922,7 @@ realityServer.updateManageFrames = function () { realityServer.selectHardwareInterfaceSettings = function (interfaceName) { let ipAddress = realityServer.states.ipAdress.interfaces[realityServer.states.ipAdress.activeInterface]; - let pathToConfig = 'http://' + ipAddress + ':' + realityServer.states.serverPort + '/hardwareInterface/' + interfaceName + '/config.html'; + let pathToConfig = 'https://' + ipAddress + ':' + realityServer.states.serverPort + '/hardwareInterface/' + interfaceName + '/config.html'; let configFrame = document.querySelector('.configFrame'); configFrame.src = pathToConfig; @@ -2244,7 +2244,7 @@ realityServer.toggleFullScreen = function (item) { // if we have set up a screen port, open up that hardware interface application if (thisScreenPort) { - thisIframe.src = 'http://' + realityServer.states.ipAdress.interfaces[realityServer.states.ipAdress.activeInterface] + ':' + thisScreenPort; + thisIframe.src = 'https://' + realityServer.states.ipAdress.interfaces[realityServer.states.ipAdress.activeInterface] + ':' + thisScreenPort; } else { // otherwise just view the target image in fullscreen thisIframe.src = 'about:blank'; // Clear iframe before loading diff --git a/logger.js b/logger.js index e231d83b7..68ad01c25 100644 --- a/logger.js +++ b/logger.js @@ -40,7 +40,15 @@ const colorizedFormat = format.combine( const logger = createLogger({ level: process.env.LOG_LEVEL || 'debug', - format: isMobile ? monochromeFormat : colorizedFormat, + format: winston.format.combine( + isMobile ? monochromeFormat : colorizedFormat, + winston.format(info => { + if (info.message.includes('Possibly unsupported ZIP platform type')) { + return false; // Ignore logs from decompress-zip structures.js when decompressing target.dat + } + return info; + })() + ), transports: [new transports.Console()] }); diff --git a/models/ObjectModel.js b/models/ObjectModel.js index 022836d6c..28d320597 100644 --- a/models/ObjectModel.js +++ b/models/ObjectModel.js @@ -15,6 +15,8 @@ function ObjectModel(ip, version, protocol, objectId) { this.objectId = objectId; // The name for the object used for interfaces. this.name = ''; + // The UUID used internally by Vuforia for tracking. Keep null for objects without targets (e.g. human pose, avatar) + this.targetId = null; this.matrix = []; this.worldId = null; // matrix is relative to this world diff --git a/models/ObjectSocket.js b/models/ObjectSocket.js index dc2211933..abaf74f9b 100644 --- a/models/ObjectSocket.js +++ b/models/ObjectSocket.js @@ -10,7 +10,7 @@ function ObjectSocket(socket, socketPort, ip) { // keeps the own IP of an object this.ip = ip; // defines where to connect to - this.io = socket.connect('http://' + ip + ':' + socketPort, { + this.io = socket.connect('https://' + ip + ':' + socketPort, { // defines the timeout for a connection between objects and the reality editor. 'connect timeout': 5000, // try to reconnect diff --git a/package-lock.json b/package-lock.json index 57ceb3dca..81b621675 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "node-fetch": "^2.7.0", "node-persist": "^2.1.0", "request": "^2.*", - "simple-git": "^3.21.0", + "simple-git": "^3.22.0", "toolsocket": "github:ptcrealitylab/toolsocket", "winston": "^3.11.0", "ws": "^8.15.1", @@ -34,13 +34,13 @@ "yargs": "^17.7.2" }, "devDependencies": { - "eslint": "^8.55.0", + "eslint": "^8.56.0", "jest": "^29.7.0", "jest-cli": "^29.7.0", "jsdoc": "^4.0.2", "minami": "^1.2.3", "mock-fs": "^5.2.0", - "puppeteer": "^21.6.0" + "puppeteer": "^21.7.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -816,9 +816,9 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", - "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1784,9 +1784,9 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.0.tgz", - "integrity": "sha512-QwguOLy44YBGC8vuPP2nmpX4MUN2FzWbsnvZJtiCzecU3lHmVZkaC1tq6rToi9a200m8RzlVtDyxCS0UIDrxUg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.1.tgz", + "integrity": "sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA==", "dev": true, "dependencies": { "debug": "4.3.4", @@ -2453,9 +2453,9 @@ ] }, "node_modules/basic-ftp": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.3.tgz", - "integrity": "sha512-QHX8HLlncOLpy54mh+k/sWIFd0ThmRqwe9ZjELybGZK+tZ8rUb9VO0saKJUROTbE+KhzDUT7xziGpGrW8Kmd+g==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.4.tgz", + "integrity": "sha512-8PzkB0arJFV4jJWSGOYR+OEic6aeKMu/osRhBULN6RY0ykby6LKhbmuQ5ublvaas5BOwboah5D87nrHyuh8PPA==", "dev": true, "engines": { "node": ">=10.0.0" @@ -2783,9 +2783,9 @@ } }, "node_modules/chromium-bidi": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.1.tgz", - "integrity": "sha512-dcCqOgq9fHKExc2R4JZs/oKbOghWpUNFAJODS8WKRtLhp3avtIH5UDCBrutdqZdh3pARogH8y1ObXm87emwb3g==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.2.tgz", + "integrity": "sha512-PbVOSddxgKyj+JByqavWMNqWPCoCaT6XK5Z1EFe168sxnB/BM51LnZEPXSbFcFAJv/+u2B4XNTs9uXxy4GW3cQ==", "dev": true, "dependencies": { "mitt": "3.0.1", @@ -3571,15 +3571,15 @@ } }, "node_modules/eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", - "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.55.0", + "@eslint/js": "8.56.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -6980,15 +6980,15 @@ } }, "node_modules/puppeteer": { - "version": "21.6.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-21.6.0.tgz", - "integrity": "sha512-u6JhSF7xaPYZ2gd3tvhYI8MwVAjLc3Cazj7UWvMV95A07/y7cIjBwYUiMU9/jm4z0FSUORriLX/RZRaiASNWPw==", + "version": "21.7.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-21.7.0.tgz", + "integrity": "sha512-Yy+UUy0b9siJezbhHO/heYUoZQUwyqDK1yOQgblTt0l97tspvDVFkcW9toBlnSvSfkDmMI3Dx9cZL6R8bDArHA==", "dev": true, "hasInstallScript": true, "dependencies": { - "@puppeteer/browsers": "1.9.0", + "@puppeteer/browsers": "1.9.1", "cosmiconfig": "8.3.6", - "puppeteer-core": "21.6.0" + "puppeteer-core": "21.7.0" }, "bin": { "puppeteer": "lib/esm/puppeteer/node/cli.js" @@ -6998,17 +6998,17 @@ } }, "node_modules/puppeteer-core": { - "version": "21.6.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-21.6.0.tgz", - "integrity": "sha512-1vrzbp2E1JpBwtIIrriWkN+A0afUxkqRuFTC3uASc5ql6iuK9ppOdIU/CPGKwOyB4YFIQ16mRbK0PK19mbXnaQ==", + "version": "21.7.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-21.7.0.tgz", + "integrity": "sha512-elPYPozrgiM3phSy7VDUJCVWQ07SPnOm78fpSaaSNFoQx5sur/MqhTSro9Wz8lOEjqCykGC6WRkwxDgmqcy1dQ==", "dev": true, "dependencies": { - "@puppeteer/browsers": "1.9.0", - "chromium-bidi": "0.5.1", + "@puppeteer/browsers": "1.9.1", + "chromium-bidi": "0.5.2", "cross-fetch": "4.0.0", "debug": "4.3.4", "devtools-protocol": "0.0.1203626", - "ws": "8.14.2" + "ws": "8.16.0" }, "engines": { "node": ">=16.13.2" @@ -7037,27 +7037,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/puppeteer-core/node_modules/ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/pure-rand": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.3.tgz", @@ -7498,9 +7477,9 @@ "dev": true }, "node_modules/simple-git": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.21.0.tgz", - "integrity": "sha512-oTzw9248AF5bDTMk9MrxsRzEzivMlY+DWH0yWS4VYpMhNLhDWnN06pCtaUyPnqv/FpsdeNmRqmZugMABHRPdDA==", + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.22.0.tgz", + "integrity": "sha512-6JujwSs0ac82jkGjMHiCnTifvf1crOiY/+tfs/Pqih6iow7VrpNKRRNdWm6RtaXpvvv/JGNYhlUtLhGFqHF+Yw==", "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", @@ -8422,9 +8401,9 @@ } }, "node_modules/ws": { - "version": "8.15.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.15.1.tgz", - "integrity": "sha512-W5OZiCjXEmk0yZ66ZN82beM5Sz7l7coYxpRkzS+p9PP+ToQry8szKh+61eNktr7EA9DOwvFGhfC605jDHbP6QQ==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "engines": { "node": ">=10.0.0" }, @@ -9144,9 +9123,9 @@ } }, "@eslint/js": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", - "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", "dev": true }, "@humanwhocodes/config-array": { @@ -9883,9 +9862,9 @@ } }, "@puppeteer/browsers": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.0.tgz", - "integrity": "sha512-QwguOLy44YBGC8vuPP2nmpX4MUN2FzWbsnvZJtiCzecU3lHmVZkaC1tq6rToi9a200m8RzlVtDyxCS0UIDrxUg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.1.tgz", + "integrity": "sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA==", "dev": true, "requires": { "debug": "4.3.4", @@ -10432,9 +10411,9 @@ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "basic-ftp": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.3.tgz", - "integrity": "sha512-QHX8HLlncOLpy54mh+k/sWIFd0ThmRqwe9ZjELybGZK+tZ8rUb9VO0saKJUROTbE+KhzDUT7xziGpGrW8Kmd+g==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.4.tgz", + "integrity": "sha512-8PzkB0arJFV4jJWSGOYR+OEic6aeKMu/osRhBULN6RY0ykby6LKhbmuQ5ublvaas5BOwboah5D87nrHyuh8PPA==", "dev": true }, "bcrypt-pbkdf": { @@ -10656,9 +10635,9 @@ } }, "chromium-bidi": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.1.tgz", - "integrity": "sha512-dcCqOgq9fHKExc2R4JZs/oKbOghWpUNFAJODS8WKRtLhp3avtIH5UDCBrutdqZdh3pARogH8y1ObXm87emwb3g==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.2.tgz", + "integrity": "sha512-PbVOSddxgKyj+JByqavWMNqWPCoCaT6XK5Z1EFe168sxnB/BM51LnZEPXSbFcFAJv/+u2B4XNTs9uXxy4GW3cQ==", "dev": true, "requires": { "mitt": "3.0.1", @@ -11229,15 +11208,15 @@ } }, "eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", - "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.55.0", + "@eslint/js": "8.56.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -13841,28 +13820,28 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "puppeteer": { - "version": "21.6.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-21.6.0.tgz", - "integrity": "sha512-u6JhSF7xaPYZ2gd3tvhYI8MwVAjLc3Cazj7UWvMV95A07/y7cIjBwYUiMU9/jm4z0FSUORriLX/RZRaiASNWPw==", + "version": "21.7.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-21.7.0.tgz", + "integrity": "sha512-Yy+UUy0b9siJezbhHO/heYUoZQUwyqDK1yOQgblTt0l97tspvDVFkcW9toBlnSvSfkDmMI3Dx9cZL6R8bDArHA==", "dev": true, "requires": { - "@puppeteer/browsers": "1.9.0", + "@puppeteer/browsers": "1.9.1", "cosmiconfig": "8.3.6", - "puppeteer-core": "21.6.0" + "puppeteer-core": "21.7.0" } }, "puppeteer-core": { - "version": "21.6.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-21.6.0.tgz", - "integrity": "sha512-1vrzbp2E1JpBwtIIrriWkN+A0afUxkqRuFTC3uASc5ql6iuK9ppOdIU/CPGKwOyB4YFIQ16mRbK0PK19mbXnaQ==", + "version": "21.7.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-21.7.0.tgz", + "integrity": "sha512-elPYPozrgiM3phSy7VDUJCVWQ07SPnOm78fpSaaSNFoQx5sur/MqhTSro9Wz8lOEjqCykGC6WRkwxDgmqcy1dQ==", "dev": true, "requires": { - "@puppeteer/browsers": "1.9.0", - "chromium-bidi": "0.5.1", + "@puppeteer/browsers": "1.9.1", + "chromium-bidi": "0.5.2", "cross-fetch": "4.0.0", "debug": "4.3.4", "devtools-protocol": "0.0.1203626", - "ws": "8.14.2" + "ws": "8.16.0" }, "dependencies": { "debug": { @@ -13879,13 +13858,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true - }, - "ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", - "dev": true, - "requires": {} } } }, @@ -14208,9 +14180,9 @@ "dev": true }, "simple-git": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.21.0.tgz", - "integrity": "sha512-oTzw9248AF5bDTMk9MrxsRzEzivMlY+DWH0yWS4VYpMhNLhDWnN06pCtaUyPnqv/FpsdeNmRqmZugMABHRPdDA==", + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.22.0.tgz", + "integrity": "sha512-6JujwSs0ac82jkGjMHiCnTifvf1crOiY/+tfs/Pqih6iow7VrpNKRRNdWm6RtaXpvvv/JGNYhlUtLhGFqHF+Yw==", "requires": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", @@ -14911,9 +14883,9 @@ } }, "ws": { - "version": "8.15.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.15.1.tgz", - "integrity": "sha512-W5OZiCjXEmk0yZ66ZN82beM5Sz7l7coYxpRkzS+p9PP+ToQry8szKh+61eNktr7EA9DOwvFGhfC605jDHbP6QQ==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "requires": {} }, "xhr": { diff --git a/package.json b/package.json index 92492c7ca..f8edc77c4 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "node-fetch": "^2.7.0", "node-persist": "^2.1.0", "request": "^2.*", - "simple-git": "^3.21.0", + "simple-git": "^3.22.0", "toolsocket": "github:ptcrealitylab/toolsocket", "winston": "^3.11.0", "ws": "^8.15.1", @@ -33,13 +33,13 @@ "url": "https://github.com/ptcrealitylab/vuforia-spatial-edge-server" }, "devDependencies": { - "eslint": "^8.55.0", + "eslint": "^8.56.0", "jest": "^29.7.0", "jest-cli": "^29.7.0", "jsdoc": "^4.0.2", "minami": "^1.2.3", "mock-fs": "^5.2.0", - "puppeteer": "^21.6.0" + "puppeteer": "^21.7.0" }, "scripts": { "test": "npm run lint && jest --forceExit --runInBand", diff --git a/routers/spatial.js b/routers/spatial.js index 1dae51102..efbc5768f 100644 --- a/routers/spatial.js +++ b/routers/spatial.js @@ -34,7 +34,7 @@ router.get('/searchObjects', function (req, res) { * regardless of whether they are pinned or unpinned. * * Example usage: - * http://localhost:8080/spatial/searchFrames?maxDistance=2000&src=communication&publicData.title.includes=Machine&publicData.mentions.includes=@Ben + * https://localhost:8080/spatial/searchFrames?maxDistance=2000&src=communication&publicData.title.includes=Machine&publicData.mentions.includes=@Ben * * queryParams: * { maxDistance: '2000', diff --git a/scripts/ci.sh b/scripts/ci.sh index 14ec426fe..9a71e3313 100755 --- a/scripts/ci.sh +++ b/scripts/ci.sh @@ -1,17 +1,19 @@ #!/bin/bash -x +./scripts/ci_generate_certificate.sh + mkdir screenshots npm ci -git clone https://github.com/ptcrealitylab/vuforia-spatial-toolbox-userinterface +git clone --branch enable_https https://github.com/ptcrealitylab/vuforia-spatial-toolbox-userinterface git clone https://github.com/hobinjk-ptc/test-spatialToolbox spatialToolbox cd addons -git clone https://github.com/ptcrealitylab/pop-up-onboarding-addon +git clone --branch enable_https https://github.com/ptcrealitylab/pop-up-onboarding-addon git clone https://github.com/ptcrealitylab/vuforia-spatial-edge-agent-addon -git clone https://github.com/ptcrealitylab/vuforia-spatial-remote-operator-addon +git clone --branch enable_https https://github.com/ptcrealitylab/vuforia-spatial-remote-operator-addon for i in `ls ./`; do echo $i diff --git a/scripts/ci_generate_certificate.sh b/scripts/ci_generate_certificate.sh new file mode 100755 index 000000000..7ada77055 --- /dev/null +++ b/scripts/ci_generate_certificate.sh @@ -0,0 +1,3 @@ +#!/bin/bash -x + +openssl req -x509 -newkey rsa:4096 -nodes -keyout key.pem -sha256 -days 356 -out cert.pem -subj "/C=US/ST=Massachusetts/L=Boston/O=Ptc/OU=Reality lab/CN=localhost/emailAddress=test@ptc.com" diff --git a/server.js b/server.js index 6e5151d53..511d2fd4e 100644 --- a/server.js +++ b/server.js @@ -153,7 +153,7 @@ const nodePaths = addonFolders.map(folder => path.join(folder, 'nodes')); const blockPaths = addonFolders.map(folder => path.join(folder, 'blocks')); // All interfaces for different hardware such as Arduino Yun, PI, Philips Hue are stored in this folder. const hardwareInterfacePaths = addonFolders.map(folder => path.join(folder, 'interfaces')); -// The web service level on which objects are accessable. http://:8080 +// The web service level on which objects are accessable. https://:8080 const objectInterfaceFolder = '/'; /********************************************************************************************************************** @@ -313,9 +313,10 @@ if (!isLightweightMobile) { webServer.set('view engine', 'handlebars'); } -const options = { - key: fs.readFileSync('./key.pem'), - cert: fs.readFileSync('./cert.pem') + +let options = { + key: fs.readFileSync('key.pem'), + cert: fs.readFileSync('cert.pem') }; const httpServer = require('https').createServer(options, webServer); const http = httpServer.listen(serverPort, function () { @@ -735,7 +736,7 @@ async function loadObjects() { for (var i = 0; i < objectFolderList.length; i++) { const objectFolder = objectFolderList[i]; - const tempFolderName = await utilities.getObjectIdFromTargetOrObjectFile(objectFolder); + const tempFolderName = await utilities.getObjectIdFromObjectFile(objectFolder); if (tempFolderName !== null) { // fill objects with objects named by the folders in objects @@ -758,6 +759,9 @@ async function loadObjects() { const obj = objects[tempFolderName]; obj.ip = services.ip; // ip.address(); + // update the targetId if needed + obj.targetId = await utilities.getTargetIdFromTargetDat(path.join(objectsPath, objectFolderList[i], identityFolderName, 'target')); + migrateObjectValuesToFrames(obj, tempFolderName); if (obj.frames[tempFolderName]) { @@ -957,7 +961,7 @@ async function setAnchors() { if (objectKey.indexOf('_WORLD_') === -1) { let thisObjectKey = null; - let tempKey = await utilities.getObjectIdFromTargetOrObjectFile(objectKey); // gets the object id from the xml target file + let tempKey = await utilities.getObjectIdFromObjectFile(objectKey); // gets the object id from the xml target file if (tempKey) { thisObjectKey = tempKey; } else { @@ -1496,7 +1500,7 @@ async function getKnownSceneGraph(ip, port) { } // TODO: implement placeholder // 2. if not, make an HTTP GET request to the other server's /spatial/sceneGraph endpoint to get it - const url = 'http://' + ip + ':' + (port || 8080) + '/spatial/sceneGraph'; + const url = 'https://' + ip + ':' + (port || 8080) + '/spatial/sceneGraph'; let response = null; try { response = await utilities.httpGet(url); @@ -3241,47 +3245,37 @@ async function createObjectFromTarget(folderVar) { return; } - var objectIDXML = await utilities.getObjectIdFromTargetOrObjectFile(folderVar); - var objectSizeXML = await utilities.getTargetSizeFromTarget(folderVar); - if (!objectIDXML || objectIDXML.length <= 13) { - return; - } + // This isn't retrieved from the XML file anymore - we use the name the object was created with + let objectId = utilities.readObject(objectLookup, folderVar); + let objectSizeXML = await utilities.getTargetSizeFromTarget(folderVar); - objects[objectIDXML] = new ObjectModel(services.ip, version, protocol, objectIDXML); - objects[objectIDXML].port = serverPort; - objects[objectIDXML].name = folderVar; - objects[objectIDXML].targetSize = objectSizeXML; + let targetUniqueId = await utilities.getTargetIdFromTargetDat(path.join(objectsPath, folderVar, identityFolderName, 'target')); - if (objectIDXML.indexOf(worldObjectName) > -1) { // TODO: implement a more robust way to tell if it's a world object - objects[objectIDXML].isWorldObject = true; - objects[objectIDXML].type = 'world'; - objects[objectIDXML].timestamp = Date.now(); - } + objects[objectId] = new ObjectModel(services.ip, version, protocol, objectId); + objects[objectId].port = serverPort; + objects[objectId].name = folderVar; + objects[objectId].targetSize = objectSizeXML; try { const contents = await fsProm.readFile(objectsPath + '/' + folderVar + '/' + identityFolderName + '/object.json', 'utf8'); - objects[objectIDXML] = JSON.parse(contents); - objects[objectIDXML].objectId = objectIDXML; - objects[objectIDXML].ip = services.ip; //ip.address(); + objects[objectId] = JSON.parse(contents); + // objects[objectId].objectId = objectId; + objects[objectId].ip = services.ip; //ip.address(); } catch (e) { - objects[objectIDXML].ip = services.ip; //ip.address(); - console.warn('No saved data for: ' + objectIDXML, e); + objects[objectId].ip = services.ip; //ip.address(); + console.warn('No saved data for: ' + objectId, e); } - if (utilities.readObject(objectLookup, folderVar) !== objectIDXML) { - let oldObjectId = utilities.readObject(objectLookup, folderVar); - try { - objects[oldObjectId].deconstruct(); - } catch (e) { - console.warn('Object exists without proper prototype: ' + oldObjectId, e); - } - delete objects[oldObjectId]; - delete knownObjects[oldObjectId]; - delete objectLookup[oldObjectId]; - sceneGraph.removeElementAndChildren(oldObjectId); + if (objectId.indexOf(worldObjectName) > -1) { // TODO: implement a more robust way to tell if it's a world object + objects[objectId].isWorldObject = true; + objects[objectId].type = 'world'; + objects[objectId].timestamp = Date.now(); } - utilities.writeObject(objectLookup, folderVar, objectIDXML); - // entering the obejct in to the lookup table + + objects[objectId].targetId = targetUniqueId; + + // entering the object in to the lookup table + utilities.writeObject(objectLookup, folderVar, objectId); // ask the object to reinitialize //serialPort.write("ok\n"); @@ -3289,16 +3283,16 @@ async function createObjectFromTarget(folderVar) { hardwareAPI.reset(); - await utilities.writeObjectToFile(objects, objectIDXML, globalVariables.saveToDisk); + await utilities.writeObjectToFile(objects, objectId, globalVariables.saveToDisk); - if (!objects[objectIDXML]) { - console.error('Object deleted during createObjectFromTarget', objectIDXML); + if (!objects[objectId]) { + console.error('Object deleted during createObjectFromTarget', objectId); return; } - sceneGraph.addObjectAndChildren(objectIDXML, objects[objectIDXML]); + sceneGraph.addObjectAndChildren(objectId, objects[objectId]); - await objectBeatSender(beatPort, objectIDXML, objects[objectIDXML].ip); + await objectBeatSender(beatPort, objectId, objects[objectId].ip); } diff --git a/tests/addon-loading.test.js b/tests/addon-loading.test.js index bb3787afe..2137b7c70 100644 --- a/tests/addon-loading.test.js +++ b/tests/addon-loading.test.js @@ -11,6 +11,9 @@ const { } = require('./helpers.js'); const fetch = require('node-fetch'); +const https = require('https'); + +let httpsAgent = new https.Agent({rejectUnauthorized: false}); let server; beforeAll(async () => { @@ -24,7 +27,7 @@ afterAll(async () => { test('GET /availableFrames', async () => { await waitForObjects(); - const res = await fetch('http://localhost:8080/availableFrames'); + const res = await fetch('https://localhost:8080/availableFrames', {agent: httpsAgent}); const frames = await res.json(); // spot check one core frame expect(frames['switch']).toEqual({ @@ -41,7 +44,7 @@ test('GET /availableFrames', async () => { test('GET /availableLogicBlocks', async () => { await waitForObjects(); - const res = await fetch('http://localhost:8080/availableLogicBlocks'); + const res = await fetch('https://localhost:8080/availableLogicBlocks', {agent: httpsAgent}); const blocks = await res.json(); // spot check one core block expect(blocks.add).toBeTruthy(); diff --git a/tests/create-frame.test.js b/tests/create-frame.test.js index fad8eef6b..607ac9881 100644 --- a/tests/create-frame.test.js +++ b/tests/create-frame.test.js @@ -14,6 +14,11 @@ const { } = require('./helpers.js'); const fetch = require('node-fetch'); +const https = require('https'); + +let httpsAgent = new https.Agent({rejectUnauthorized: false}); + +const worldName = '_WORLD_instantScanPJ1cgyrm'; let server; beforeAll(async () => { @@ -27,7 +32,7 @@ afterAll(async () => { async function addFrame() { await waitForObjects(); - const res = await fetch('http://localhost:8080/object/_WORLD_instantScanPJ1cgyrm_T6ijgnpsk1c/addFrame/', { + const res = await fetch('https://localhost:8080/object/_WORLD_instantScanPJ1cgyrm_T6ijgnpsk1c/addFrame/', { headers: { 'content-type': 'application/json' }, @@ -83,22 +88,23 @@ async function addFrame() { lastEditor: 'cKzswlhy', }), method: 'POST', + agent: httpsAgent }); return await res.text(); } -async function getObject() { - const res = await fetch('http://localhost:8080/object/_WORLD_instantScanPJ1cgyrm_T6ijgnpsk1c'); +async function getObject(objectId) { + const res = await fetch(`https://localhost:8080/object/${objectId}`, {agent: httpsAgent}); return await res.json(); } -async function getFrame() { - const res = await fetch('http://localhost:8080/object/_WORLD_instantScanPJ1cgyrm_T6ijgnpsk1c/frame/_WORLD_instantScanPJ1cgyrm_T6ijgnpsk1cspatialDraw1mJx458y5jn9a'); +async function getFrame(objectId) { + const res = await fetch(`https://localhost:8080/object/${objectId}/frame/${objectId}spatialDraw1mJx458y5jn9a`, {agent: httpsAgent}); return await res.json(); } -async function moveFrame() { - const res = await fetch('http://localhost:8080/object/_WORLD_instantScanPJ1cgyrm_T6ijgnpsk1c/frame/_WORLD_instantScanPJ1cgyrm_T6ijgnpsk1cspatialDraw1mJx458y5jn9a/node/null/size/', { +async function moveFrame(objectId) { + const res = await fetch(`https://localhost:8080/object/${objectId}/frame/${objectId}spatialDraw1mJx458y5jn9a/node/null/size/`, { headers: { 'content-type': 'application/json' }, @@ -115,64 +121,88 @@ async function moveFrame() { lastEditor: 'cKzswlhy' }), method: 'POST', + agent: httpsAgent }); return await res.text(); } -const frameAddedRef = { - objectId: '_WORLD_instantScanPJ1cgyrm_T6ijgnpsk1c', - uuid: '_WORLD_instantScanPJ1cgyrm_T6ijgnpsk1cspatialDraw1mJx458y5jn9a', - name: 'spatialDraw1mJx458y5jn9a', - visualization: 'ar', - ar: { - x: 0, - y: 0, - scale: 1, - matrix: [ - 0.966, 0.015, 0.257, 0, - -0.256, -0.0676, 0.964, 0, - -0.0319, 0.997, 0.0614, 0, - -50.227, -193.471, -239.032, 1 - ] - }, - screen: { x: 0, y: 0, scale: 0.5 }, - visible: false, - visibleText: false, - visibleEditing: false, - developer: true, - links: {}, - nodes: {}, - location: 'global', - src: 'spatialDraw', - privateData: {}, - publicData: {}, - staticCopy: false, - distanceScale: 1, - groupID: null, - pinned: true +const getFrameAddedRef = (objectId) => { + return { + objectId, + uuid: `${objectId}spatialDraw1mJx458y5jn9a`, + name: 'spatialDraw1mJx458y5jn9a', + visualization: 'ar', + ar: { + x: 0, + y: 0, + scale: 1, + matrix: [ + 0.966, 0.015, 0.257, 0, + -0.256, -0.0676, 0.964, 0, + -0.0319, 0.997, 0.0614, 0, + -50.227, -193.471, -239.032, 1 + ] + }, + screen: { x: 0, y: 0, scale: 0.5 }, + visible: false, + visibleText: false, + visibleEditing: false, + developer: true, + links: {}, + nodes: {}, + location: 'global', + src: 'spatialDraw', + privateData: {}, + publicData: {}, + staticCopy: false, + distanceScale: 1, + groupID: null, + pinned: true + }; }; -const frameMovedRef = JSON.parse(JSON.stringify(frameAddedRef)); -frameMovedRef.ar.matrix = [ - 0.966, 0.015, 0.257, 0, - -0.256, -0.0675, 0.964, 0, - -0.031, 0.997, 0.061, 0, - -150, -293, -555, 1 -]; +const getFrameMovedRef = (objectId) => { + const frameMovedRef = JSON.parse(JSON.stringify(getFrameAddedRef(objectId))); + frameMovedRef.ar.matrix = [ + 0.966, 0.015, 0.257, 0, + -0.256, -0.0675, 0.964, 0, + -0.031, 0.997, 0.061, 0, + -150, -293, -555, 1 + ]; + return frameMovedRef; +}; test('new object creation', async () => { let objectsPath = require('../config.js').objectsPath; - await addFrame(); - - const worldAdded = await getObject(); - expect(worldAdded.frames).toEqual({ - _WORLD_instantScanPJ1cgyrm_T6ijgnpsk1cspatialDraw1mJx458y5jn9a: frameAddedRef, + const snapshotPre = filterSnapshot(snapshotDirectory(objectsPath), (filePath) => { + return filterToObjects(filePath) && filePath.includes(worldName); }); - const frameAdded = await getFrame(); + let objFsPre = null; + for (let key of Object.keys(snapshotPre)) { + if (key.endsWith('.identity/object.json')) { + objFsPre = snapshotPre[key]; + break; + } + } + const objectId = objFsPre.objectId; + expect(typeof objectId).toBe('string'); + expect(objectId).toMatch(new RegExp(`^${worldName}`)); + + const frameAddedRef = getFrameAddedRef(objectId); + await addFrame(objectId); + + const worldAdded = await getObject(objectId); + let expectedFrames = {}; + expectedFrames[`${objectId}spatialDraw1mJx458y5jn9a`] = frameAddedRef; + expect(worldAdded.frames).toEqual(expectedFrames); + + const frameAdded = await getFrame(objectId); expect(frameAdded).toEqual(frameAddedRef); - const snapshot = filterSnapshot(snapshotDirectory(objectsPath), filterToObjects); + const snapshot = filterSnapshot(snapshotDirectory(objectsPath), (filePath) => { + return filterToObjects(filePath) && filePath.includes(worldName); + }); let objFs = null; for (let key of Object.keys(snapshot)) { if (key.endsWith('.identity/object.json')) { @@ -180,20 +210,21 @@ test('new object creation', async () => { break; } } - expect(objFs.frames).toEqual({ - _WORLD_instantScanPJ1cgyrm_T6ijgnpsk1cspatialDraw1mJx458y5jn9a: frameAddedRef, - }); + expect(objFs.frames).toEqual(expectedFrames); - await moveFrame(); + const frameMovedRef = getFrameMovedRef(objectId); + await moveFrame(objectId); - const worldMoved = await getObject(); - expect(worldMoved.frames).toEqual({ - _WORLD_instantScanPJ1cgyrm_T6ijgnpsk1cspatialDraw1mJx458y5jn9a: frameMovedRef, - }); - const frameMoved = await getFrame(); + const worldMoved = await getObject(objectId); + let expectedFramesMoved = {}; + expectedFramesMoved[`${objectId}spatialDraw1mJx458y5jn9a`] = frameMovedRef; + expect(worldMoved.frames).toEqual(expectedFramesMoved); + const frameMoved = await getFrame(objectId); expect(frameMoved).toEqual(frameMovedRef); - const snapshotMoved = filterSnapshot(snapshotDirectory(objectsPath), filterToObjects); + const snapshotMoved = filterSnapshot(snapshotDirectory(objectsPath), (filePath) => { + return filterToObjects(filePath) && filePath.includes(worldName); + }); objFs = null; for (let key of Object.keys(snapshotMoved)) { if (key.endsWith('.identity/object.json')) { @@ -201,7 +232,5 @@ test('new object creation', async () => { break; } } - expect(objFs.frames).toEqual({ - _WORLD_instantScanPJ1cgyrm_T6ijgnpsk1cspatialDraw1mJx458y5jn9a: frameMovedRef, - }); + expect(objFs.frames).toEqual(expectedFramesMoved); }); diff --git a/tests/helpers.js b/tests/helpers.js index 0f64cec08..3fecfecd9 100644 --- a/tests/helpers.js +++ b/tests/helpers.js @@ -1,8 +1,11 @@ const fs = require('fs'); const path = require('path'); +const https = require('https'); const fetch = require('node-fetch'); +let httpsAgent = new https.Agent({rejectUnauthorized: false}); + function sleep(ms) { return new Promise((res) => { setTimeout(res, ms); @@ -60,7 +63,7 @@ exports.filterToTestObject = function filterToTestObject(key) { }; async function getAllObjects() { - const resAllObjects = await fetch(`http://localhost:8080/allObjects`); + const resAllObjects = await fetch(`https://localhost:8080/allObjects`, {agent: httpsAgent}); const allObjects = await resAllObjects.json(); return allObjects; } @@ -82,7 +85,8 @@ async function waitForObjects(lengthMin = 1) { if (Object.keys(allObjects).length >= lengthMin) { break; } - } catch (_) { + } catch (e) { + console.log(e); // way way too early as opposed to normal too early } await sleep(100); diff --git a/tests/object.test.js b/tests/object.test.js index b7d9c5418..117aeafe5 100644 --- a/tests/object.test.js +++ b/tests/object.test.js @@ -6,6 +6,9 @@ /* global test, beforeAll, afterAll, expect */ const fetch = require('node-fetch'); +const https = require('https'); + +let httpsAgent = new https.Agent({rejectUnauthorized: false}); const { filterSnapshot, @@ -33,13 +36,14 @@ test('new object creation', async () => { const allObjectsPre = await getTestObjects(); expect(allObjectsPre).toEqual([]); - const resNew = await fetch('http://localhost:8080/', { + const resNew = await fetch('https://localhost:8080/', { headers: { 'Content-type': 'application/x-www-form-urlencoded', }, 'body': 'action=new&name=fdsa&isWorld=null', 'method': 'POST', - 'mode': 'cors' + 'mode': 'cors', + agent: httpsAgent }); await resNew.text(); const allObjectsCreated = await getTestObjects(); @@ -86,12 +90,13 @@ test('new object creation', async () => { expect(fdsaFs.timestamp).toBe(null); expect(fdsaFs.port).toBe(8080); - await fetch('http://localhost:8080/', { + await fetch('https://localhost:8080/', { headers: { 'Content-type': 'application/x-www-form-urlencoded', }, body: 'action=delete&name=fdsa&frame=', method: 'POST', + agent: httpsAgent }); const allObjectsDeleted = await getTestObjects(); diff --git a/tests/server-remote-operator.test.js b/tests/server-remote-operator.test.js index 16ac9fa61..949805a08 100644 --- a/tests/server-remote-operator.test.js +++ b/tests/server-remote-operator.test.js @@ -8,6 +8,9 @@ const puppeteer = require('puppeteer'); const fetch = require('node-fetch'); +const https = require('https'); + +let httpsAgent = new https.Agent({rejectUnauthorized: false}); const {sleep, waitForObjects} = require('./helpers.js'); @@ -23,8 +26,9 @@ afterAll(async () => { test('server provides remote operator functionality', async () => { const browser = await puppeteer.launch({ - args: ['--no-sandbox', '--disable-setuid-sandbox'], + args: ['--no-sandbox', '--disable-setuid-sandbox', '--ignore-certificate-errors'], headless: 'new', + ignoreHTTPSErrors: true }); const page = await browser.newPage(); @@ -41,7 +45,7 @@ test('server provides remote operator functionality', async () => { await waitForObjects(); await page.goto( - 'http://localhost:8080/', + 'https://localhost:8080/', { timeout: 60 * 1000, }, @@ -60,7 +64,7 @@ test('server provides remote operator functionality', async () => { await page.goto( // `https://${localSettings.serverUrl}/stable/n/${localSettings.networkUUID}/s/${localSettings.networkSecret}/`, - 'http://localhost:8081', + 'https://localhost:8081/', { timeout: 60 * 1000, }, @@ -80,7 +84,7 @@ test('server provides remote operator functionality', async () => { }); try { - const res = await fetch(`http://localhost:8080/hardwareInterface/edgeAgent/settings`); + const res = await fetch(`https://localhost:8080/hardwareInterface/edgeAgent/settings`, {agent: httpsAgent}); const localSettings = await res.json(); await page.goto( diff --git a/tests/upload-target.test.js b/tests/upload-target.test.js index 41b4d2f09..59736bf16 100644 --- a/tests/upload-target.test.js +++ b/tests/upload-target.test.js @@ -16,6 +16,8 @@ const fsProm = require('fs/promises'); const path = require('path'); const fetch = require('node-fetch'); +const https = require('https'); +let httpsAgent = new https.Agent({rejectUnauthorized: false}); const FormData = require('form-data'); const objectsPath = require('../config.js').objectsPath; @@ -41,28 +43,33 @@ afterAll(async () => { test('target upload to /content/:objectName', async () => { await waitForObjects(); - const resNew = await fetch('http://localhost:8080/', { + const resNew = await fetch('https://localhost:8080/', { headers: { 'Content-type': 'application/x-www-form-urlencoded', }, 'body': `action=new&name=${worldName}&isWorld=true`, - 'method': 'POST' + 'method': 'POST', + agent: httpsAgent }); await resNew.text(); + const preUploadSnapshot = filterSnapshot(snapshotDirectory(objectsPath), (name) => name.includes(worldName)); + const preUploadObjJson = getValueWithKeySuffixed(preUploadSnapshot, '.identity/object.json'); + const form = new FormData(); form.append('target.zip', targetZipBuf, {filename: 'target.zip', name: 'target.zip', contentType: 'application/zip'}); - const res = await fetch(`http://localhost:8080/content/${worldName}`, { + const res = await fetch(`https://localhost:8080/content/${worldName}`, { method: 'POST', headers: { ...form.getHeaders(), type: 'targetUpload', }, body: form, + agent: httpsAgent }); const content = await res.json(); expect(content).toEqual({ - id: '_WORLD_instantScan6bK1sn5d_wdb4wue6bdm', + id: preUploadObjJson.objectId, name: worldName, initialized: false, jpgExists: false, @@ -78,9 +85,11 @@ test('target upload to /content/:objectName', async () => { delete objJson.ip; delete objJson.port; delete objJson.tcs; + delete objJson.timestamp; expect(objJson).toEqual({ - objectId: '_WORLD_instantScan6bK1sn5d_wdb4wue6bdm', + objectId: preUploadObjJson.objectId, name: '_WORLD_instantScan6bK1sn5d', + targetId: 'e3169799f314480da133849f0feb1676', matrix: [], worldId: null, isAnchor: false, @@ -100,7 +109,6 @@ test('target upload to /content/:objectName', async () => { targetSize: { width: 0.3, height: 0.3 }, isWorldObject: true, type: 'world', - timestamp: null, }); const tdt = getValueWithKeySuffixed(snapshot, 'target.3dt'); @@ -116,12 +124,13 @@ test('target upload to /content/:objectName', async () => { // Let the upload cleanup process finish before we delete await sleep(1000); - const resDelete = await fetch('http://localhost:8080/', { + const resDelete = await fetch('https://localhost:8080/', { headers: { 'Content-type': 'application/x-www-form-urlencoded', }, 'body': `action=delete&name=${worldName}&frame=`, - 'method': 'POST' + 'method': 'POST', + agent: httpsAgent }); await resDelete.text();