diff --git a/component.json b/component.json index 59917b36..ae6ba8b9 100644 --- a/component.json +++ b/component.json @@ -1,6 +1,6 @@ { "name": "lpwanserver", "registry": "lpwanserver", - "version": "1.2.0", + "version": "1.2.1", "build": "1" } diff --git a/docker/dev/Dockerfile b/docker/dev/Dockerfile index d4909938..a8de92ca 100644 --- a/docker/dev/Dockerfile +++ b/docker/dev/Dockerfile @@ -7,6 +7,7 @@ WORKDIR /usr/src/app COPY bin/rest bin/rest COPY config config COPY prisma/generated prisma/generated +COPY lib/prisma-cache lib/prisma-cache COPY restApp.js ./restApp.js COPY server.js ./server.js COPY public public diff --git a/package.json b/package.json index 53f4de1f..68a1738f 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "ajv": "^6.10.0", "async": "~2.6.1", "bluebird": "^3.5.5", - "body-parser": "^1.18.3", "chai": "^4.2.0", "chai-http": "^4.2.0", "connect-ensure-login": "~0.1.1", diff --git a/rest/models/ICompanyNetworkTypeLink.js b/rest/models/ICompanyNetworkTypeLink.js index ebbdb52c..00d07142 100644 --- a/rest/models/ICompanyNetworkTypeLink.js +++ b/rest/models/ICompanyNetworkTypeLink.js @@ -251,7 +251,8 @@ module.exports = class CompanyNetworkTypeLink { appLogger.log('creating ' + JSON.stringify(device)) let appIndex = nsAppId.indexOf(device.applicationID) appLogger.log('localAppId[' + appIndex + '] = ' + localAppId[appIndex]) - existingDevice = await this.modelAPI.devices.create(device.name, device.description, localAppId[appIndex]) + const devData = { ...R.pick(['name', 'description'], device), applicationId: localAppId[appIndex] } + existingDevice = await this.modelAPI.devices.create(devData) } let [ devNtls ] = await this.modelAPI.deviceNetworkTypeLinks.list({ deviceId: existingDevice.id }) @@ -265,8 +266,13 @@ module.exports = class CompanyNetworkTypeLink { let tempApp = await this.modelAPI.applications.load(localAppId[appIndex]) let coId = tempApp.company.id - - this.modelAPI.deviceNetworkTypeLinks.create(existingDevice.id, networkTypeId, localDpId[dpIndex], device, coId) + let devNtlData = { + deviceId: existingDevice.id, + networkTypeId, + deviceProfileId: localDpId[dpIndex], + networkSettings: device + } + this.modelAPI.deviceNetworkTypeLinks.create(devNtlData, { validateCompanyId: coId }) } } } diff --git a/rest/models/IDevice.js b/rest/models/IDevice.js index 3aa0bcc2..768cefb1 100644 --- a/rest/models/IDevice.js +++ b/rest/models/IDevice.js @@ -74,8 +74,8 @@ module.exports = class Device { return DB.list(renameQueryKeys(query), opts) } - create (name, description, applicationId, deviceModel) { - return DB.create({ name, description, applicationId, deviceModel }) + create (data) { + return DB.create(data) } update ({ id, ...data }) { @@ -151,19 +151,7 @@ module.exports = class Device { async receiveIpDeviceUplink (devEUI, data) { devEUI = normalizeDevEUI(devEUI) let nwkType = await this.modelAPI.networkTypes.loadByName('IP') - let devNtl - // Check cache for devNtl ID - let devNtlId = await redisClient.getAsync(`ip-devNtl-${devEUI}`) - if (devNtlId) { - devNtl = await this.modelAPI.deviceNetworkTypeLinks.load(devNtlId) - if (!devNtl) await redisClient.delAsync(`ip-devNtl-${devEUI}`) - } - else { - const devNTLQuery = { networkType: { id: nwkType.id }, networkSettings_contains: devEUI } - let [ devNtls ] = await this.modelAPI.deviceNetworkTypeLinks.list(devNTLQuery) - devNtl = devNtls[0] - if (devNtl) await redisClient.setAsync(`ip-devNtl-${devEUI}`, devNtl.id) - } + const devNtl = await this.modelAPI.deviceNetworkTypeLinks.findByDevEUI(devEUI, nwkType.id) if (!devNtl) return // Get device const device = await this.load(devNtl.device.id) @@ -194,4 +182,34 @@ module.exports = class Device { await redisClient.del(key) return msgs.map(JSON.parse) } + + async importDevices ({ applicationId, deviceProfileId, devices }) { + // ensure app exists + await this.modelAPI.applications.load(applicationId) + // Load device profile + const deviceProfile = await this.modelAPI.deviceProfiles.load(deviceProfileId) + const nwkType = await this.modelAPI.networkTypes.load(deviceProfile.networkType.id) + if (!nwkType.name === 'IP') { + throw httpError(400, 'Device import currently only supports IP devices.') + } + // Catch all errors and return array + return Promise.all(devices.map(async ({ name, description, deviceModel, devEUI }, row) => { + try { + if (!devEUI) { + throw new Error('devEUI required for each imported device.') + } + const device = await this.create({ name: name || devEUI, applicationId, description, deviceModel }) + await this.modelAPI.deviceNetworkTypeLinks.create({ + deviceId: device.id, + networkTypeId: deviceProfile.networkType.id, + deviceProfileId, + networkSettings: { devEUI } + }) + return { status: 'OK', deviceId: device.id, devEUI, row } + } + catch (err) { + return { status: 'ERROR', error: err.message, devEUI, row } + } + })) + } } diff --git a/rest/models/IDeviceNetworkTypeLink.js b/rest/models/IDeviceNetworkTypeLink.js index b268b6fe..97162d9c 100644 --- a/rest/models/IDeviceNetworkTypeLink.js +++ b/rest/models/IDeviceNetworkTypeLink.js @@ -3,6 +3,7 @@ const { prisma } = require('../lib/prisma') const { normalizeDevEUI, parseProp, stringifyProp } = require('../lib/utils') const { redisClient } = require('../lib/redis') const CacheFirstStrategy = require('../../lib/prisma-cache/src/cache-first-strategy') +const R = require('ramda') //* ***************************************************************************** // Fragments for how the data should be returned from Prisma. @@ -59,16 +60,15 @@ module.exports = class DeviceNetworkTypeLink { return [ records.map(parseNetworkSettings), totalCount ] } - async create (deviceId, networkTypeId, deviceProfileId, networkSettings, validateCompanyId, { remoteOrigin = false } = {}) { + async create (data, { validateCompanyId, remoteOrigin = false } = {}) { try { - await this.validateCompanyForDevice(validateCompanyId, deviceId) - if (networkSettings && networkSettings.devEUI) { - networkSettings.devEUI = normalizeDevEUI(networkSettings.devEUI) + if (validateCompanyId) await this.validateCompanyForDevice(validateCompanyId, data.deviceId) + if (data.networkSettings && data.networkSettings.devEUI) { + data = R.assocPath(['networkSettings', 'devEUI'], normalizeDevEUI(data.networkSettings.devEUI), data) } - const data = { deviceId, networkTypeId, deviceProfileId, networkSettings } const rec = await DB.create(stringifyNetworkSettings(data)) if (!remoteOrigin) { - var logs = await this.modelAPI.networkTypeAPI.addDevice(networkTypeId, deviceId, networkSettings) + var logs = await this.modelAPI.networkTypeAPI.addDevice(data.networkTypeId, data.deviceId, data.networkSettings) rec.remoteAccessLogs = logs } return rec @@ -136,4 +136,21 @@ module.exports = class DeviceNetworkTypeLink { const dnl = await this.load(dnlId) await this.validateCompanyForDevice(companyId, dnl.device.id) } + + async findByDevEUI (devEUI, networkTypeId) { + let devNtl + // Check cache for devNtl ID + let devNtlId = await redisClient.getAsync(`ip-devNtl-${devEUI}`) + if (devNtlId) { + devNtl = await this.load(devNtlId) + if (!devNtl) await redisClient.delAsync(`ip-devNtl-${devEUI}`) + } + else { + const devNTLQuery = { networkType: { id: networkTypeId }, networkSettings_contains: devEUI } + let [ devNtls ] = await this.list(devNTLQuery) + devNtl = devNtls[0] + if (devNtl) await redisClient.setAsync(`ip-devNtl-${devEUI}`, devNtl.id) + } + return devNtl + } } diff --git a/rest/networkProtocols/handlers/LoraOpenSource/LoraOpenSource.js b/rest/networkProtocols/handlers/LoraOpenSource/LoraOpenSource.js index 162496bc..4f77cb7d 100644 --- a/rest/networkProtocols/handlers/LoraOpenSource/LoraOpenSource.js +++ b/rest/networkProtocols/handlers/LoraOpenSource/LoraOpenSource.js @@ -528,7 +528,8 @@ module.exports = class LoraOpenSource extends NetworkProtocol { } else { appLogger.log('creating ' + remoteDevice.name) - localDevice = await this.modelAPI.devices.create(remoteDevice.name, remoteDevice.description, localAppId) + const devData = { ...R.pick(['name', 'description'], remoteDevice), applicationId: localAppId } + localDevice = await this.modelAPI.devices.create(devData) appLogger.log('Created ' + localDevice.name) } let [ devNtls ] = await this.modelAPI.deviceNetworkTypeLinks.list({ deviceId: localDevice.id, limit: 1 }) @@ -540,7 +541,13 @@ module.exports = class LoraOpenSource extends NetworkProtocol { let company = cos[0] appLogger.log('creating Network Link for ' + localDevice.name) let networkSettings = this.buildDeviceNetworkSettings(remoteDevice) - await this.modelAPI.deviceNetworkTypeLinks.create(localDevice.id, network.networkType.id, deviceProfile.id, networkSettings, company.id, { remoteOrigin: true }) + let devNtlData = { + deviceId: localDevice.id, + networkTypeId: network.networkType.id, + deviceProfileId: deviceProfile.id, + networkSettings + } + await this.modelAPI.deviceNetworkTypeLinks.create(devNtlData, { validateCompanyId: company.id, remoteOrigin: true }) } await this.modelAPI.protocolData.upsert(network, makeDeviceDataKey(localDevice.id, 'devNwkId'), remoteDevice.devEUI) return localDevice.id diff --git a/rest/networkProtocols/handlers/Loriot/Loriot.js b/rest/networkProtocols/handlers/Loriot/Loriot.js index a85a388e..a4472d98 100644 --- a/rest/networkProtocols/handlers/Loriot/Loriot.js +++ b/rest/networkProtocols/handlers/Loriot/Loriot.js @@ -190,7 +190,8 @@ module.exports = class Loriot extends NetworkProtocol { } else { appLogger.log('creating ' + remoteDevice.title) - localDevice = await modelAPI.devices.create(remoteDevice.title, remoteDevice.description, localAppId) + const devData = { name: remoteDevice.title, description: remoteDevice.description, applicationId: localAppId } + localDevice = await modelAPI.devices.create(devData) appLogger.log('Created ' + localDevice.name) } @@ -204,7 +205,13 @@ module.exports = class Loriot extends NetworkProtocol { appLogger.log(dp, 'info') let networkSettings = this.buildDeviceNetworkSettings(remoteDevice, dp.localDeviceProfile) appLogger.log(networkSettings) - const deviceNtl = await modelAPI.deviceNetworkTypeLinks.create(localDevice.id, network.networkType.id, dp.localDeviceProfile, networkSettings, company.id, { remoteOrigin: true }) + let devNtlData = { + deviceId: localDevice.id, + networkTypeId: network.networkType.id, + deviceProfileId: dp.localDeviceProfile, + networkSettings + } + const deviceNtl = await modelAPI.deviceNetworkTypeLinks.create(devNtlData, { validateCompanyId: company.id, remoteOrigin: true }) appLogger.log(deviceNtl) } await this.modelAPI.protocolData.upsert(network, makeDeviceDataKey(localDevice.id, 'devNwkId'), remoteDevice._id) diff --git a/rest/networkProtocols/handlers/TheThingsNetwork/v2/index.js b/rest/networkProtocols/handlers/TheThingsNetwork/v2/index.js index f529251c..685eaf2b 100644 --- a/rest/networkProtocols/handlers/TheThingsNetwork/v2/index.js +++ b/rest/networkProtocols/handlers/TheThingsNetwork/v2/index.js @@ -203,7 +203,12 @@ module.exports = class TheThingsNetworkV2 extends NetworkProtocol { } else { appLogger.log('creating ' + remoteDevice.lorawan_device.dev_eui) - existingDevice = await modelAPI.devices.create(remoteDevice.lorawan_device.dev_eui, remoteDevice.description, localAppId) + const devData = { + name: remoteDevice.lorawan_device.dev_eui, + description: remoteDevice.description, + applicationId: localAppId + } + existingDevice = await modelAPI.devices.create(devData) appLogger.log('Created ' + existingDevice.name) } @@ -220,7 +225,15 @@ module.exports = class TheThingsNetworkV2 extends NetworkProtocol { appLogger.log(dp, 'info') let normalizedDevice = this.normalizeDeviceData(remoteDevice, dp.localDeviceProfile) appLogger.log(normalizedDevice) - const existingDeviceNTL = await modelAPI.deviceNetworkTypeLinks.create(existingDevice.id, network.networkType.id, dp.localDeviceProfile, normalizedDevice, 2, { remoteOrigin: true }) + const [ cos ] = await this.modelAPI.companies.list({ limit: 1 }) + let company = cos[0] + let devNtlData = { + deviceId: existingDevice.id, + networkTypeId: network.networkType.id, + deviceProfileId: dp.localDeviceProfile, + networkSettings: normalizedDevice + } + const existingDeviceNTL = await modelAPI.deviceNetworkTypeLinks.create(devNtlData, { validateCompanyId: company.id, remoteOrigin: true }) appLogger.log(existingDeviceNTL) await this.modelAPI.protocolData.upsert(network, makeDeviceDataKey(existingDevice.id, 'devNwkId'), remoteDevice.dev_id) appLogger.log({ localDevice: existingDevice.id, remoteDevice: remoteDevice.dev_id }) diff --git a/rest/restApplicationNetworkTypeLinks.js b/rest/restApplicationNetworkTypeLinks.js index f8f550d4..16738359 100644 --- a/rest/restApplicationNetworkTypeLinks.js +++ b/rest/restApplicationNetworkTypeLinks.js @@ -49,7 +49,7 @@ exports.initialize = function (app, server) { * that the Application is being linked to. * @apiSuccess {String} object.records.networkSettings The settings in a * JSON string that correspond to the Network Type. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/applicationNetworkTypeLinks', [restServer.isLoggedIn, restServer.fetchCompany], @@ -102,7 +102,7 @@ exports.initialize = function (app, server) { * that the Application is being linked to. * @apiSuccess {String} object.networkSettings The settings in a * JSON string that correspond to the Network Type. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/applicationNetworkTypeLinks/:id', [restServer.isLoggedIn], function (req, res, next) { var id = req.params.id @@ -139,7 +139,7 @@ exports.initialize = function (app, server) { * "networkSettings": { ... }, * } * @apiSuccess {String} id The new Application Network Type Link's id. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.post('/api/applicationNetworkTypeLinks', [restServer.isLoggedIn, restServer.fetchCompany, @@ -199,7 +199,7 @@ exports.initialize = function (app, server) { * { * "networkSettings": { ... }, * } - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.put('/api/applicationNetworkTypeLinks/:id', [restServer.isLoggedIn, restServer.fetchCompany, @@ -268,7 +268,7 @@ exports.initialize = function (app, server) { * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The Application Network Type * Link's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.delete('/api/applicationNetworkTypeLinks/:id', [restServer.isLoggedIn, restServer.fetchCompany, diff --git a/rest/restApplications.js b/rest/restApplications.js index 56dddb14..8df022ea 100644 --- a/rest/restApplications.js +++ b/rest/restApplications.js @@ -55,7 +55,7 @@ exports.initialize = function (app, server) { * @apiSuccess {Boolean} object.records.running If the Application is * currently sending data received from the Networks to the baseUrl via * the Reporting Protocol. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/applications', [restServer.isLoggedIn, restServer.fetchCompany], @@ -134,7 +134,7 @@ exports.initialize = function (app, server) { * @apiSuccess {Boolean} object.running If the Application is * currently sending data received from the Networks to the baseUrl via * the Reporting Protocol. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/applications/:id', [restServer.isLoggedIn, restServer.fetchCompany], @@ -178,12 +178,12 @@ exports.initialize = function (app, server) { * { * "name": "GPS Pet Tracker", * "description": "Pet finder with occasional reporting", - * "companyId": 1, + * "companyId": J59j3Ddteoi8, * "baseUrl": "https://IoTStuff.com/incomingData/GPSPetTracker" - * "reportingProtocolId": 1 + * "reportingProtocolId": 6s3oi3j90ed9j * } * @apiSuccess {String} id The new Application's id. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.post('/api/applications', [restServer.isLoggedIn, restServer.fetchCompany, @@ -248,11 +248,11 @@ exports.initialize = function (app, server) { * { * "name": "GPS Pet Tracker", * "description": "Pet finder with occasional reporting" - * "companyId": 1, + * "companyId": J59j3Ddteoi8, * "baseUrl": "https://IoTStuff.com/incomingData/GPSPetTracker" - * "reportingProtocolId": 1 + * "reportingProtocolId": 6s3oi3j90ed9j * } - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.put('/api/applications/:id', [restServer.isLoggedIn, restServer.fetchCompany, @@ -339,7 +339,7 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The Application's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.delete('/api/applications/:id', [restServer.isLoggedIn, restServer.fetchCompany, @@ -389,7 +389,7 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The Application's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ // Yeah, yeah, this isn't a pure REST call. So sue me. Gets the job done. async function startApplication (req, res) { @@ -425,7 +425,7 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The Application's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ // Yeah, yeah, this isn't a pure REST call. So sue me. Gets the job done. async function stopApplication (req, res) { @@ -487,7 +487,7 @@ exports.initialize = function (app, server) { * { * any: any * } - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ async function uplinkHandler (req, res) { var appId = req.params.applicationId @@ -503,4 +503,53 @@ exports.initialize = function (app, server) { } } app.post('/api/ingest/:applicationId/:networkId', uplinkHandler) + + /** + * @apiDescription Upload a CSV file with devEUIs + * + * @api {post} /api/applications/:id/bulk-device-import Import Devices + * @apiGroup Applications + * @apiPermission System Admin, or Company Admin for this company. + * @apiHeader {String} Authorization The Create Session's returned token + * prepended with "Bearer " + * @apiParam (Request Body) {String} [deviceProfileId] The ID of the + * device profile for the devices being imported. + * @apiParam (Request Body) {Object[]} [devices] List of device import data. devEUI required. + * @apiExample {json} Example body: + * { + * "deviceProfileId": "gh4s56l0fewo0" + * "devices": [ + * { + * "name": "GPS Pet Tracker", + * "description": "Pet finder with occasional reporting", + * "devEUI": 33:DD:99:FF:22:11:CC:BB + * } + * ] + * } + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * [ + * { status: 'OK', deviceId: 'deviceId', devEUI, row: 0 }, + * { status: 'ERROR', error: 'reason', devEUI, row: 1 } + * ] + * @apiVersion 1.2.1 + */ + async function bulkDeviceImport (req, res) { + try { + let result = await modelAPI.devices.importDevices({ + ...req.body, + applicationId: req.params.id + }) + restServer.respond(res, 200, result, true) + } + catch (err) { + appLogger.log(`Device bulk import failed for application ${req.params.id}: ${err}`) + restServer.respond(res, err) + } + } + app.post( + '/api/applications/:id/import-devices', + [restServer.isLoggedIn, restServer.fetchCompany, restServer.isAdmin], + bulkDeviceImport + ) } diff --git a/rest/restCompanies.js b/rest/restCompanies.js index bc328f35..eeeaaf48 100644 --- a/rest/restCompanies.js +++ b/rest/restCompanies.js @@ -41,7 +41,7 @@ exports.initialize = function (app, server) { * @apiSuccess {String} object.records.id The Company's Id * @apiSuccess {String} object.records.name The Company's name * @apiSuccess {String} object.records.type "admin" or "vendor" - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/companies', [restServer.isLoggedIn, restServer.fetchCompany, @@ -92,7 +92,7 @@ exports.initialize = function (app, server) { * @apiSuccess {String} object.id The Company's Id * @apiSuccess {String} object.name The Company's name * @apiSuccess {String} object.type "admin" or "vendor" - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/companies/:id', [restServer.isLoggedIn, restServer.fetchCompany], @@ -128,7 +128,7 @@ exports.initialize = function (app, server) { * "type": "vendor" * } * @apiSuccess {String} id The new Company's id. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.post('/api/companies', [restServer.isLoggedIn, restServer.fetchCompany, @@ -174,7 +174,7 @@ exports.initialize = function (app, server) { * "name": "IoT Stuff, Inc.", * "type": "vendor" * } - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.put('/api/companies/:id', [restServer.isLoggedIn, restServer.fetchCompany, @@ -237,7 +237,7 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The Company's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.delete('/api/companies/:id', [restServer.isLoggedIn, restServer.fetchCompany, diff --git a/rest/restCompanyNetworkTypeLinks.js b/rest/restCompanyNetworkTypeLinks.js index eb4c2e27..9bc616c0 100644 --- a/rest/restCompanyNetworkTypeLinks.js +++ b/rest/restCompanyNetworkTypeLinks.js @@ -46,7 +46,7 @@ exports.initialize = function (app, server) { * that the Company is being linked to. * @apiSuccess {String} object.records.networkSettings The settings in a * JSON string that correspond to the Network Type. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/companyNetworkTypeLinks', [ restServer.isLoggedIn, restServer.fetchCompany ], @@ -98,7 +98,7 @@ exports.initialize = function (app, server) { * that the Company is being linked to. * @apiSuccess {String} object.networkSettings The settings in a * JSON string that correspond to the Network Type. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/companyNetworkTypeLinks/:id', [restServer.isLoggedIn], function (req, res) { @@ -134,7 +134,7 @@ exports.initialize = function (app, server) { * "networkSettings": "{ ... }", * } * @apiSuccess {String} id The new Company Network Type Link's id. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.post('/api/companyNetworkTypeLinks', [restServer.isLoggedIn, restServer.fetchCompany, @@ -208,7 +208,7 @@ exports.initialize = function (app, server) { * { * "networkSettings": "{ ... }", * } - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.put('/api/companyNetworkTypeLinks/:id', [restServer.isLoggedIn, restServer.fetchCompany, @@ -279,7 +279,7 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The Company Network Type Link's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.delete('/api/companyNetworkTypeLinks/:id', [restServer.isLoggedIn, restServer.fetchCompany, diff --git a/rest/restDeviceNetworkTypeLinks.js b/rest/restDeviceNetworkTypeLinks.js index c8440d2f..818d3dc6 100644 --- a/rest/restDeviceNetworkTypeLinks.js +++ b/rest/restDeviceNetworkTypeLinks.js @@ -1,6 +1,7 @@ var appLogger = require('./lib/appLogger.js') var restServer var modelAPI +const R = require('ramda') const { formatRelationshipsOut } = require('./lib/prisma') @@ -48,7 +49,7 @@ exports.initialize = function (app, server) { * that the Device is being linked to. * @apiSuccess {String} object.records.networkSettings The settings in a * JSON string that correspond to the Network Type. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/deviceNetworkTypeLinks', [restServer.isLoggedIn, restServer.fetchCompany], function (req, res) { @@ -98,9 +99,9 @@ exports.initialize = function (app, server) { * that the Device is being linked to. * @apiSuccess {String} object.networkSettings The settings in a * JSON string that correspond to the Network Type. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ - app.get('/api/deviceNetworkTypeLinks/:id', [restServer.isLoggedIn], function (req, res, next) { + app.get('/api/deviceNetworkTypeLinks/:id', [restServer.isLoggedIn], function (req, res) { var id = req.params.id modelAPI.deviceNetworkTypeLinks.load(id).then(function (np) { restServer.respondJson(res, null, formatRelationshipsOut(np)) @@ -133,12 +134,12 @@ exports.initialize = function (app, server) { * "networkSettings": "{ ... }", * } * @apiSuccess {String} id The new Device Network Type Link's id. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.post('/api/deviceNetworkTypeLinks', [restServer.isLoggedIn, restServer.fetchCompany, restServer.isAdmin], - function (req, res, next) { + function (req, res) { var rec = req.body // You can't specify an id. if (rec.id) { @@ -164,16 +165,8 @@ exports.initialize = function (app, server) { } // Do the add. - modelAPI.deviceNetworkTypeLinks.create( - rec.deviceId, - rec.networkTypeId, - rec.deviceProfileId, - rec.networkSettings, - companyId).then(function (rec) { - var send = {} - send.id = rec.id - send.remoteAccessLogs = rec.remoteAccessLogs - restServer.respondJson(res, 200, send) + modelAPI.deviceNetworkTypeLinks.create(rec, { validateCompanyId: companyId }).then(function (rec) { + restServer.respondJson(res, 200, R.pick(['id', 'remoteAccessLogs'], rec)) }) .catch(function (err) { restServer.respond(res, err) @@ -198,12 +191,12 @@ exports.initialize = function (app, server) { * { * "networkSettings": "{ ... }", * } - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.put('/api/deviceNetworkTypeLinks/:id', [restServer.isLoggedIn, restServer.fetchCompany, restServer.isAdmin], - function (req, res, next) { + function (req, res) { // We're not going to allow changing the device or the network. // Neither operation makes much sense. if (req.body.deviceId || req.body.networkTypeId) { @@ -270,12 +263,12 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The Device Network Type Link's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.delete('/api/deviceNetworkTypeLinks/:id', [restServer.isLoggedIn, restServer.fetchCompany, restServer.isAdmin], - function (req, res, next) { + function (req, res) { var id = req.params.id // If not an admin company, the deviceId better be associated // with the user's company @@ -303,13 +296,13 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The Device Network Type Link's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.post('/api/deviceNetworkTypeLinks/:id/push', [restServer.isLoggedIn, restServer.fetchCompany, restServer.isAdmin, modelAPI.devices.fetchDeviceApplication], - function (req, res, next) { + function (req, res) { var id = req.params.id // If the caller is a global admin, or the device is part of the company // admin's company, we can delete.req.application.company.id diff --git a/rest/restDeviceProfiles.js b/rest/restDeviceProfiles.js index 17cbb9a3..40af68e2 100644 --- a/rest/restDeviceProfiles.js +++ b/rest/restDeviceProfiles.js @@ -126,7 +126,7 @@ exports.initialize = function (app, server) { * structure that has the settings for the Network Type. This is * expected to match the Network Protocol's expected data used to * set up the device on the remote Network(s). - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/deviceProfiles/:id', [ restServer.isLoggedIn, restServer.fetchCompany ], @@ -174,12 +174,12 @@ exports.initialize = function (app, server) { * { * "name": "GPS Tracker", * "description": "GPS device for remote reporting", - * "companyId": 1, + * "companyId": J59j3Ddteoi8, * "networkTypeId": 1, * "networkSettings": {...} * } * @apiSuccess {String} id The new Device Profile's id. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.post('/api/deviceProfiles', [ restServer.isLoggedIn, restServer.fetchCompany, @@ -253,11 +253,11 @@ exports.initialize = function (app, server) { * { * "name": "GPS Tracker", * "description": "GPS tracker with remote reporting", - * "companyId": 1, + * "companyId": J59j3Ddteoi8, * "networkTypeId": 1, * "networkSettings": {...} * } - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.put('/api/deviceProfiles/:id', [restServer.isLoggedIn, restServer.fetchCompany, @@ -339,7 +339,7 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The Device Profile's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.delete('/api/deviceProfiles/:id', [ restServer.isLoggedIn, restServer.fetchCompany, @@ -370,7 +370,7 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The Device Profile's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.post('/api/deviceProfiles/:id/push', [restServer.isLoggedIn, restServer.fetchCompany, diff --git a/rest/restDevices.js b/rest/restDevices.js index 90350d9c..c9c051e3 100644 --- a/rest/restDevices.js +++ b/rest/restDevices.js @@ -4,6 +4,7 @@ var modelAPI const { getCertificateCn, getHttpRequestPreferedWaitMs, normalizeDevEUI } = require('./lib/utils') const { formatRelationshipsOut } = require('./lib/prisma') const { redisSub } = require('./lib/redis') +const R = require('ramda') exports.initialize = function (app, server) { restServer = server @@ -49,7 +50,7 @@ exports.initialize = function (app, server) { * information * @apiSuccess {String} object.records.applicationId The Id of the * Application that this Device belongs to. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/devices', [restServer.isLoggedIn, restServer.fetchCompany], async function (req, res) { @@ -116,7 +117,7 @@ exports.initialize = function (app, server) { * information * @apiSuccess {String} object.records.applicationId The Id of the * Application that this Device belongs to. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/devices/:id', [restServer.isLoggedIn, restServer.fetchCompany, @@ -156,7 +157,7 @@ exports.initialize = function (app, server) { * "applicationId": 1 * } * @apiSuccess {String} id The new Device's id. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.post('/api/devices', [restServer.isLoggedIn, restServer.fetchCompany, @@ -183,13 +184,8 @@ exports.initialize = function (app, server) { } else { // OK, add it. - modelAPI.devices.create(rec.name, - rec.description, - rec.applicationId, - rec.deviceModel).then(function (rec) { - var send = {} - send.id = rec.id - restServer.respondJson(res, 200, send) // TODO: Shouldn't this id be in the header per POST convention? + modelAPI.devices.create(rec).then(function (rec) { + restServer.respondJson(res, 200, R.pick(['id'], rec)) // TODO: Shouldn't this id be in the header per POST convention? }) .catch(function (err) { appLogger.log('Failed to create device ' + JSON.stringify(rec) + ': ' + err) @@ -223,7 +219,7 @@ exports.initialize = function (app, server) { * "deviceModel": "Bark 1", * "applicationId": 1 * } - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.put('/api/devices/:id', [restServer.isLoggedIn, restServer.fetchCompany, @@ -332,7 +328,7 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The Device's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.delete('/api/devices/:id', [restServer.isLoggedIn, restServer.fetchCompany, @@ -374,7 +370,7 @@ exports.initialize = function (app, server) { * fPort: 1, * jsonData: { ... } * } - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ async function unicastDownlinkPostHandler (req, res) { const { id } = req.params @@ -408,7 +404,7 @@ exports.initialize = function (app, server) { * @apiPermission TLS Client Certificate, with devEUI in Subject.CN * @apiHeader {String} prefer Request a time in seconds for server to * hold request open, waiting for downlinks. e.g. prefer: wait=30 - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ async function listIpDeviceDownlinks (req, res) { const devEUI = getCertificateCn(req.connection.getPeerCertificate()) @@ -464,7 +460,7 @@ exports.initialize = function (app, server) { * { * any: 'any' * } - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ async function ipDeviceUplinkHandler (req, res) { const devEUI = getCertificateCn(req.connection.getPeerCertificate()) diff --git a/rest/restNetworkProtocols.js b/rest/restNetworkProtocols.js index 4203394e..68de7616 100644 --- a/rest/restNetworkProtocols.js +++ b/rest/restNetworkProtocols.js @@ -44,7 +44,7 @@ exports.initialize = function (app, server) { * node code that communicates with a remote Network. * @apiSuccess {String} object.records.networkTypeId The id of the Network * Type that the Network Protocol uses for data input. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/networkProtocols', [restServer.isLoggedIn], @@ -154,7 +154,7 @@ exports.initialize = function (app, server) { * node code that communicates with a remote Network. * @apiSuccess {String} object.networkTypeId The id of the Network * Type that the Network Protocol uses for data input. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/networkProtocols/:id', [restServer.isLoggedIn], function (req, res) { @@ -188,7 +188,7 @@ exports.initialize = function (app, server) { * "networkTypeId": 1 * } * @apiSuccess {String} id The new Network Protocol's id. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.post('/api/networkProtocols', [restServer.isLoggedIn, restServer.fetchCompany, @@ -247,7 +247,7 @@ exports.initialize = function (app, server) { * "protocolHandler": "LoRaOpenSource.js", * "networkTypeId": 1 * } - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.put('/api/networkProtocols/:id', [restServer.isLoggedIn, restServer.fetchCompany, @@ -313,7 +313,7 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The Network Protocol's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.delete('/api/networkProtocols/:id', [restServer.isLoggedIn, restServer.fetchCompany, @@ -342,7 +342,7 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiSuccess {Array} array of protocol handlers available. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/networkProtocolHandlers/', [restServer.isLoggedIn], async function (req, res) { diff --git a/rest/restNetworkProviders.js b/rest/restNetworkProviders.js index f7444dd1..48e4fe09 100644 --- a/rest/restNetworkProviders.js +++ b/rest/restNetworkProviders.js @@ -39,7 +39,7 @@ exports.initialize = function (app, server) { * records. * @apiSuccess {String} object.records.id The Network Provider's Id * @apiSuccess {String} object.records.name The name of the Network Provider - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/networkProviders', [restServer.isLoggedIn], function (req, res) { var options = {} @@ -79,7 +79,7 @@ exports.initialize = function (app, server) { * @apiSuccess {Object} object * @apiSuccess {String} object.id The Network Provider's Id * @apiSuccess {String} object.name The name of the Network Provider - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/networkProviders/:id', [restServer.isLoggedIn], function (req, res) { @@ -151,7 +151,7 @@ exports.initialize = function (app, server) { * { * "name": "CableLabs" * } - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.put('/api/networkProviders/:id', [restServer.isLoggedIn, restServer.fetchCompany, @@ -204,7 +204,7 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The Network Provider's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.delete('/api/networkProviders/:id', [restServer.isLoggedIn, restServer.fetchCompany, diff --git a/rest/restNetworkTypes.js b/rest/restNetworkTypes.js index 29d09efe..717c826a 100644 --- a/rest/restNetworkTypes.js +++ b/rest/restNetworkTypes.js @@ -39,7 +39,7 @@ exports.initialize = function (app, server) { * records. * @apiSuccess {String} object.records.id The Network Type's Id * @apiSuccess {String} object.records.name The name of the Network Type - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/networkTypes', [restServer.isLoggedIn], function (req, res) { modelAPI.networkTypes.list({}, { includeTotal: true }).then(([ records, totalCount ]) => { @@ -63,7 +63,7 @@ exports.initialize = function (app, server) { * @apiSuccess {Object} object * @apiSuccess {String} object.id The Network Type's Id * @apiSuccess {String} object.name The name of the Network Type - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/networkTypes/:id', [restServer.isLoggedIn], function (req, res) { @@ -135,7 +135,7 @@ exports.initialize = function (app, server) { * { * "name": "NB-IoT" * } - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.put('/api/networkTypes/:id', [restServer.isLoggedIn, restServer.fetchCompany, @@ -188,7 +188,7 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The Network Type's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.delete('/api/networkTypes/:id', [restServer.isLoggedIn, restServer.fetchCompany, diff --git a/rest/restNetworks.js b/rest/restNetworks.js index f39c2f6f..c2196b4a 100644 --- a/rest/restNetworks.js +++ b/rest/restNetworks.js @@ -57,7 +57,7 @@ exports.initialize = function (app, server) { * @apiSuccess {Object} object.records.securityData The data used to grant * secure access to the Network's server API. (Only returned to * System Admins.) - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/networks', [restServer.isLoggedIn, restServer.fetchCompany], @@ -244,7 +244,7 @@ exports.initialize = function (app, server) { * @apiSuccess {Object} object.securityData The data used to grant * secure access to the Network's server API. (Only returned to System * Admins.) - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/networks/:id', [restServer.isLoggedIn, restServer.fetchCompany], @@ -313,7 +313,7 @@ exports.initialize = function (app, server) { * } * } * @apiSuccess {String} id The new Network's id. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.post('/api/networks', [restServer.isLoggedIn, restServer.fetchCompany, restServer.isAdminCompany], @@ -510,7 +510,7 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The Network's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.delete('/api/networks/:id', [restServer.isLoggedIn, restServer.fetchCompany, diff --git a/rest/restPasswordPolicies.js b/rest/restPasswordPolicies.js index 42553e90..40c455e2 100644 --- a/rest/restPasswordPolicies.js +++ b/rest/restPasswordPolicies.js @@ -32,7 +32,7 @@ exports.initialize = function (app, server) { * password for it to be considered valid. * @apiSuccess {Boolean} [object.global] True indicates a system-wide rule, * which can only be changed by a System Admin. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/passwordPolicies/company/:companyId', [restServer.isLoggedIn, @@ -79,7 +79,7 @@ exports.initialize = function (app, server) { * password for it to be considered valid. * @apiSuccess {String} companyId The company who owns this Password Policy * or null if a global policy. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/passwordPolicies/:id', [restServer.isLoggedIn, restServer.fetchCompany], @@ -135,7 +135,7 @@ exports.initialize = function (app, server) { * "companyId": 3 * } * @apiSuccess {String} id The new Password Policy's id. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.post('/api/passwordPolicies', [restServer.isLoggedIn, restServer.fetchCompany, @@ -203,7 +203,7 @@ exports.initialize = function (app, server) { * "ruleText": "Must contain a digit", * "ruleRegexp": "[0-9]" * } - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.put('/api/passwordPolicies/:id', [restServer.isLoggedIn, restServer.fetchCompany, @@ -287,7 +287,7 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The Password Policy's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.delete('/api/passwordPolicies/:id', [restServer.isLoggedIn, restServer.fetchCompany, diff --git a/rest/restReportingProtocols.js b/rest/restReportingProtocols.js index 54299f47..dd46998c 100644 --- a/rest/restReportingProtocols.js +++ b/rest/restReportingProtocols.js @@ -25,7 +25,7 @@ exports.initialize = function (app, server) { * @apiSuccess {String} object.name The name of the Reporting Protocol * @apiSuccess {String} object.protocolHandler The Reporting * Protocol code that communicates with an Application vendor's server. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/reportingProtocols', [restServer.isLoggedIn], function (req, res) { modelAPI.reportingProtocols.list({}, { includeTotal: true }).then(function ([ records, totalCount ]) { @@ -51,7 +51,7 @@ exports.initialize = function (app, server) { * @apiSuccess {String} object.name The name of the Reporting Protocol * @apiSuccess {String} object.protocolHandler The Reporting Protocol * code that communicates with an Application vendor's server. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/reportingProtocols/:id', [restServer.isLoggedIn], function (req, res) { modelAPI.reportingProtocols.load(req.params.id).then(function (rp) { @@ -80,7 +80,7 @@ exports.initialize = function (app, server) { * "protocolHandler": "Post.js" * } * @apiSuccess {String} id The new Network Protocol's id. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.post('/api/reportingProtocols', [restServer.isLoggedIn, restServer.fetchCompany, @@ -130,7 +130,7 @@ exports.initialize = function (app, server) { * "name": "POST", * "protocolHandler": "Post.js" * } - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.put('/api/reportingProtocols/:id', [restServer.isLoggedIn, restServer.fetchCompany, @@ -189,7 +189,7 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The Reporting Protocol's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.delete('/api/reportingProtocols/:id', [restServer.isLoggedIn, restServer.fetchCompany, @@ -214,7 +214,7 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiSuccess {Array} array of protocol handlers available. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/reportingProtocolHandlers/', [restServer.isLoggedIn], async function (req, res) { diff --git a/rest/restSessions.js b/rest/restSessions.js index 3189b3a7..b969634e 100644 --- a/rest/restSessions.js +++ b/rest/restSessions.js @@ -27,7 +27,7 @@ exports.initialize = function (app, server) { * "login_password": "secretshhh" * } * @apiSuccess (200) {string} token The JWT token for the user. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.post('/api/sessions', function (req, res, next) { modelAPI.sessions.authorize(req, res, next).then( @@ -47,7 +47,7 @@ exports.initialize = function (app, server) { * @api {delete} /api/sessions Delete Session * @apiGroup Sessions * @apiDescription Deletes the session. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.delete('/api/sessions', [ restServer.isLoggedIn ], function (req, res, next) { modelAPI.sessions.delete(req, res, next).then(function () { diff --git a/rest/restUsers.js b/rest/restUsers.js index 2587a577..833ef8f1 100644 --- a/rest/restUsers.js +++ b/rest/restUsers.js @@ -61,7 +61,7 @@ exports.initialize = function (app, server) { * the system. * @apiSuccess {String} object.records.companyId The Id of the Company * that the User belongs to. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/users', [restServer.isLoggedIn, restServer.fetchCompany, @@ -125,7 +125,7 @@ exports.initialize = function (app, server) { * the system. * @apiSuccess {String} object.companyId The Id of the Company * that the User belongs to. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/users/me', [restServer.isLoggedIn], function (req, res) { restServer.respondJson(res, null, formatRelationshipsOut(req.user)) @@ -150,7 +150,7 @@ exports.initialize = function (app, server) { * the system. * @apiSuccess {String} object.companyId The Id of the Company * that the User belongs to. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.get('/api/users/:id', [restServer.isLoggedIn, restServer.fetchCompany], function (req, res) { modelAPI.users.load(req.params.id).then(function (user) { @@ -194,7 +194,7 @@ exports.initialize = function (app, server) { * "companyId": 3 * } * @apiSuccess {String} id The new User's id. - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.post('/api/users', [restServer.isLoggedIn, restServer.fetchCompany, @@ -260,7 +260,7 @@ exports.initialize = function (app, server) { * "role": "user", * "companyId": 4 * } - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.put('/api/users/:id', [restServer.isLoggedIn, restServer.fetchCompany], @@ -381,7 +381,7 @@ exports.initialize = function (app, server) { * @apiHeader {String} Authorization The Create Session's returned token * prepended with "Bearer " * @apiParam (URL Parameters) {String} id The User's id - * @apiVersion 1.2.0 + * @apiVersion 1.2.1 */ app.delete('/api/users/:id', [restServer.isLoggedIn, restServer.fetchCompany, diff --git a/restApp.js b/restApp.js index be86143c..4b2083b7 100644 --- a/restApp.js +++ b/restApp.js @@ -9,7 +9,6 @@ var express = require('express') var cors = require('cors') // var path = require('path'); var cookieParser = require('cookie-parser') -var bodyParser = require('body-parser') // server var RestServer = require('./rest/restServer') var appLogger = require('./rest/lib/appLogger') @@ -76,8 +75,7 @@ module.exports = async function createApp () { appLogger.initRESTCallLogger(app) // Add the body parser. - app.use(bodyParser.json()) - app.use(bodyParser.urlencoded({ extended: false })) + app.use(express.json()) // Add a cookie parser. app.use(cookieParser()) diff --git a/test/e2e/specs/device-import.js b/test/e2e/specs/device-import.js new file mode 100644 index 00000000..dde5d8d1 --- /dev/null +++ b/test/e2e/specs/device-import.js @@ -0,0 +1,96 @@ +const assert = require('assert') +const { prisma } = require('../../../prisma/generated/prisma-client') +const request = require('request-promise') +const fs = require('fs') +const path = require('path') + +const { + LPWANSERVER_URL +} = process.env + +const caFile = path.join(__dirname, '../../../certs/ca-crt.pem') +const ca = fs.readFileSync(caFile, { encoding: 'utf8' }) +const requestOpts = { resolveWithFullResponse: true, json: true } + +describe.only('Bulk device import', () => { + let deviceProfile + let application + + async function setupData () { + let ipNwkType = await prisma.networkType({ name: 'IP' }) + let postReportingProtocol = (await prisma.reportingProtocols())[0] + let company = (await prisma.companies())[0] + deviceProfile = await prisma.createDeviceProfile({ + company: { connect: { id: company.id } }, + networkType: { connect: { id: ipNwkType.id } }, + name: 'device-import-test-dev-prof', + networkSettings: `{}` + }) + application = await prisma.createApplication({ + name: 'device-import-test-app', + enabled: false, + reportingProtocol: { connect: { id: postReportingProtocol.id } } + }) + } + + before(setupData) + + describe('Bulk upload IP devices', () => { + let accessToken + + it('Get auth token for LPWAN Server', async () => { + const opts = { + method: 'POST', + url: `${LPWANSERVER_URL}/api/sessions`, + body: { login_username: 'admin', login_password: 'password' }, + ca, + json: true + } + accessToken = await request(opts) + }) + + it('Uploads a payload with a list of devices to import', async () => { + const opts = { + method: 'POST', + headers: { Authorization: `Bearer ${accessToken}` }, + url: `${LPWANSERVER_URL}/api/applications/${application.id}/import-devices`, + ...requestOpts, + ca, + body: { + deviceProfileId: deviceProfile.id, + devices: [ + { devEUI: '19:7A:56:16:B8:28:17:CD' }, + { devEUI: '19:7A:56:16:B8:28:43:21' }, + { devEUI: '19:7A:56:16:B8:28:EF:12' } + ] + } + } + + const res = await request(opts) + console.log(res.body) + assert.strictEqual(res.statusCode, 200) + }) + + it('Device import fails if no devEUI', async () => { + const opts = { + method: 'POST', + headers: { Authorization: `Bearer ${accessToken}` }, + url: `${LPWANSERVER_URL}/api/applications/${application.id}/import-devices`, + ...requestOpts, + ca, + body: { + deviceProfileId: deviceProfile.id, + devices: [ + { devEUI: '19:7A:56:16:B8:28:17:FE' }, + { name: 'invalid' } + ] + } + } + + const res = await request(opts) + console.log(res.body) + const invalid = res.body.filter(x => x.status === 'ERROR') + assert.strictEqual(invalid.length, 1) + }) + }) +}) diff --git a/test/e2e/specs/ip-device-messaging.js b/test/e2e/specs/ip-device-messaging.js index 4f862831..193c926d 100644 --- a/test/e2e/specs/ip-device-messaging.js +++ b/test/e2e/specs/ip-device-messaging.js @@ -95,7 +95,6 @@ describe.only('E2E Test for IP Device Uplink/Downlink Device Messaging', () => { json: true } accessToken = await request(opts) - console.log(accessToken) }) it('Send a downlink request to LPWAN Server', async () => { @@ -113,7 +112,7 @@ describe.only('E2E Test for IP Device Uplink/Downlink Device Messaging', () => { }) it('Long poll for downlinks', async function () { - this.timeout(140000) + this.timeout(10000) let downlink2 = { jsonData: { msgId: 3 }, fCnt: 0, fPort: 1 } setTimeout(() => sendDownlink(accessToken, device.id, downlink2), 3000) diff --git a/test2.0/unit/models/IDevice-test.js b/test2.0/unit/models/IDevice-test.js index 6bf32bbd..7b5c49da 100644 --- a/test2.0/unit/models/IDevice-test.js +++ b/test2.0/unit/models/IDevice-test.js @@ -34,7 +34,13 @@ describe('Unit Tests for ' + testName, () => { const apps = await prisma.applications() let testModule = new TestModule(modelAPIMock) should.exist(testModule) - const actual = await testModule.create('test', 'test application', apps[0].id, 'AR1') + const devData = { + name: 'test', + description: 'test application', + applicationId: apps[0].id, + deviceModel: 'AR1' + } + const actual = await testModule.create(devData) assertDeviceProps(actual) deviceId = actual.id })