mirror of
https://github.com/openstf/stf
synced 2025-10-05 10:39:25 +02:00
add groups feature
This commit is contained in:
parent
6fd750dad5
commit
7f5dc4c152
119 changed files with 12416 additions and 402 deletions
|
@ -1,79 +1,527 @@
|
|||
/**
|
||||
* Copyright © 2019 contains code contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
|
||||
**/
|
||||
|
||||
var _ = require('lodash')
|
||||
var Promise = require('bluebird')
|
||||
|
||||
var dbapi = require('../../../db/api')
|
||||
var logger = require('../../../util/logger')
|
||||
var datautil = require('../../../util/datautil')
|
||||
|
||||
var log = logger.createLogger('api:controllers:devices')
|
||||
|
||||
module.exports = {
|
||||
getDevices: getDevices
|
||||
, getDeviceBySerial: getDeviceBySerial
|
||||
const apiutil = require('../../../util/apiutil')
|
||||
const lockutil = require('../../../util/lockutil')
|
||||
const util = require('util')
|
||||
const uuid = require('uuid')
|
||||
const wire = require('../../../wire')
|
||||
const wireutil = require('../../../wire/util')
|
||||
const wirerouter = require('../../../wire/router')
|
||||
|
||||
/* ------------------------------------ PRIVATE FUNCTIONS ------------------------------- */
|
||||
|
||||
function filterGenericDevices(req, res, devices) {
|
||||
apiutil.respond(res, 200, 'Devices Information', {
|
||||
devices: devices.map(function(device) {
|
||||
return apiutil.filterDevice(req, device)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function getDevices(req, res) {
|
||||
var fields = req.swagger.params.fields.value
|
||||
function getGenericDevices(req, res, loadDevices) {
|
||||
loadDevices(req.user.groups.subscribed).then(function(devices) {
|
||||
filterGenericDevices(req, res, devices)
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to load device list: ', err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
dbapi.loadDevices()
|
||||
.then(function(cursor) {
|
||||
return Promise.promisify(cursor.toArray, cursor)()
|
||||
.then(function(list) {
|
||||
var deviceList = []
|
||||
|
||||
list.forEach(function(device) {
|
||||
datautil.normalize(device, req.user)
|
||||
var responseDevice = device
|
||||
|
||||
if (fields) {
|
||||
responseDevice = _.pick(device, fields.split(','))
|
||||
}
|
||||
deviceList.push(responseDevice)
|
||||
})
|
||||
|
||||
res.json({
|
||||
success: true
|
||||
, devices: deviceList
|
||||
})
|
||||
})
|
||||
function getDeviceFilteredGroups(serial, fields, bookingOnly) {
|
||||
return dbapi.getDeviceGroups(serial).then(function(groups) {
|
||||
return Promise.map(groups, function(group) {
|
||||
return !bookingOnly || !apiutil.isOriginGroup(group.class) ?
|
||||
group :
|
||||
'filtered'
|
||||
})
|
||||
.catch(function(err) {
|
||||
log.error('Failed to load device list: ', err.stack)
|
||||
res.status(500).json({
|
||||
success: false
|
||||
.then(function(groups) {
|
||||
return _.without(groups, 'filtered').map(function(group) {
|
||||
if (fields) {
|
||||
return _.pick(apiutil.publishGroup(group), fields.split(','))
|
||||
}
|
||||
return apiutil.publishGroup(group)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function extractStandardizableDevices(devices) {
|
||||
return dbapi.getTransientGroups().then(function(groups) {
|
||||
return Promise.map(devices, function(device) {
|
||||
return Promise.map(groups, function(group) {
|
||||
if (group.devices.indexOf(device.serial) > -1) {
|
||||
return Promise.reject('booked')
|
||||
}
|
||||
return true
|
||||
})
|
||||
.then(function() {
|
||||
return device
|
||||
})
|
||||
.catch(function(err) {
|
||||
if (err !== 'booked') {
|
||||
throw err
|
||||
}
|
||||
return err
|
||||
})
|
||||
})
|
||||
.then(function(devices) {
|
||||
return _.without(devices, 'booked')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function getStandardizableDevices(req, res) {
|
||||
dbapi.loadDevicesByOrigin(req.user.groups.subscribed).then(function(devices) {
|
||||
extractStandardizableDevices(devices).then(function(devices) {
|
||||
filterGenericDevices(req, res, devices)
|
||||
})
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to load device list: ', err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function removeDevice(serial, req, res) {
|
||||
const presentState = req.swagger.params.present.value
|
||||
const bookingState = req.swagger.params.booked.value
|
||||
const notesState = req.swagger.params.annotated.value
|
||||
const controllingState = req.swagger.params.controlled.value
|
||||
const anyPresentState = typeof presentState === 'undefined'
|
||||
const anyBookingState = typeof bookingState === 'undefined'
|
||||
const anyNotesState = typeof notesState === 'undefined'
|
||||
const anyControllingState = typeof controllingState === 'undefined'
|
||||
const lock = {}
|
||||
|
||||
function deleteGroupDevice(email, id) {
|
||||
const lock = {}
|
||||
|
||||
return dbapi.lockGroupByOwner(email, id).then(function(stats) {
|
||||
if (!stats.replaced) {
|
||||
return apiutil.lightComputeStats(res, stats)
|
||||
}
|
||||
const group = lock.group = stats.changes[0].new_val
|
||||
|
||||
if (group.devices.indexOf(serial) > -1) {
|
||||
return apiutil.isOriginGroup(group.class) ?
|
||||
dbapi.removeOriginGroupDevice(group, serial) :
|
||||
dbapi.removeGroupDevices(group, [serial])
|
||||
}
|
||||
return group
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockGroup(lock)
|
||||
})
|
||||
}
|
||||
|
||||
function deleteDeviceInDatabase() {
|
||||
function wrappedDeleteDeviceInDatabase() {
|
||||
const result = {
|
||||
status: false
|
||||
, data: 'not deleted'
|
||||
}
|
||||
|
||||
return dbapi.loadDeviceBySerial(serial).then(function(device) {
|
||||
if (device && device.group.id === device.group.origin) {
|
||||
return deleteGroupDevice(device.group.owner.email, device.group.id)
|
||||
.then(function(group) {
|
||||
if (group !== 'not found') {
|
||||
return dbapi.deleteDevice(serial).then(function() {
|
||||
result.status = true
|
||||
result.data = 'deleted'
|
||||
})
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
return false
|
||||
})
|
||||
.then(function() {
|
||||
return result
|
||||
})
|
||||
}
|
||||
return apiutil.setIntervalWrapper(
|
||||
wrappedDeleteDeviceInDatabase
|
||||
, 10
|
||||
, Math.random() * 500 + 50)
|
||||
}
|
||||
|
||||
return dbapi.lockDeviceByOrigin(req.user.groups.subscribed, serial).then(function(stats) {
|
||||
if (!stats.replaced) {
|
||||
return apiutil.lightComputeStats(res, stats)
|
||||
}
|
||||
const device = lock.device = stats.changes[0].new_val
|
||||
|
||||
if (!anyPresentState && device.present !== presentState ||
|
||||
!anyControllingState && (device.owner === null) === controllingState ||
|
||||
!anyNotesState &&
|
||||
(typeof device.notes !== 'undefined' && device.notes !== '') !== notesState ||
|
||||
!anyBookingState && (device.group.id !== device.group.origin && !bookingState ||
|
||||
device.group.class === apiutil.STANDARD && bookingState)) {
|
||||
return 'unchanged'
|
||||
}
|
||||
if (device.group.class === apiutil.STANDARD) {
|
||||
return deleteDeviceInDatabase()
|
||||
}
|
||||
return dbapi.getDeviceTransientGroups(serial).then(function(groups) {
|
||||
if (groups.length && !anyBookingState && !bookingState) {
|
||||
return 'unchanged'
|
||||
}
|
||||
return Promise.each(groups, function(group) {
|
||||
return deleteGroupDevice(group.owner.email, group.id)
|
||||
})
|
||||
.then(function() {
|
||||
if (!groups.length && !anyBookingState && bookingState) {
|
||||
return 'unchanged'
|
||||
}
|
||||
return deleteDeviceInDatabase()
|
||||
})
|
||||
})
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockDevice(lock)
|
||||
})
|
||||
}
|
||||
|
||||
/* ------------------------------------ PUBLIC FUNCTIONS ------------------------------- */
|
||||
|
||||
function getDevices(req, res) {
|
||||
const target = req.swagger.params.target.value
|
||||
|
||||
switch(target) {
|
||||
case apiutil.BOOKABLE:
|
||||
getGenericDevices(req, res, dbapi.loadBookableDevices)
|
||||
break
|
||||
case apiutil.ORIGIN:
|
||||
getGenericDevices(req, res, dbapi.loadDevicesByOrigin)
|
||||
break
|
||||
case apiutil.STANDARD:
|
||||
getGenericDevices(req, res, dbapi.loadStandardDevices)
|
||||
break
|
||||
case apiutil.STANDARDIZABLE:
|
||||
getStandardizableDevices(req, res)
|
||||
break
|
||||
default:
|
||||
getGenericDevices(req, res, dbapi.loadDevices)
|
||||
}
|
||||
}
|
||||
|
||||
function getDeviceBySerial(req, res) {
|
||||
var serial = req.swagger.params.serial.value
|
||||
var fields = req.swagger.params.fields.value
|
||||
|
||||
dbapi.loadDevice(serial)
|
||||
.then(function(device) {
|
||||
if (!device) {
|
||||
return res.status(404).json({
|
||||
success: false
|
||||
, description: 'Device not found'
|
||||
dbapi.loadDevice(req.user.groups.subscribed, serial)
|
||||
.then(function(cursor) {
|
||||
cursor.next(function(err, device) {
|
||||
if (err) {
|
||||
return res.status(404).json({
|
||||
success: false
|
||||
, description: 'Device not found'
|
||||
})
|
||||
}
|
||||
let responseDevice = apiutil.publishDevice(device, req.user)
|
||||
|
||||
if (fields) {
|
||||
responseDevice = _.pick(device, fields.split(','))
|
||||
}
|
||||
res.json({
|
||||
success: true
|
||||
, device: responseDevice
|
||||
})
|
||||
}
|
||||
|
||||
datautil.normalize(device, req.user)
|
||||
var responseDevice = device
|
||||
|
||||
if (fields) {
|
||||
responseDevice = _.pick(device, fields.split(','))
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true
|
||||
, device: responseDevice
|
||||
})
|
||||
})
|
||||
.catch(function(err) {
|
||||
log.error('Failed to load device "%s": ', req.params.serial, err.stack)
|
||||
log.error('Failed to load device "%s": ', serial, err.stack)
|
||||
res.status(500).json({
|
||||
success: false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function getDeviceGroups(req, res) {
|
||||
const serial = req.swagger.params.serial.value
|
||||
const fields = req.swagger.params.fields.value
|
||||
|
||||
dbapi.loadDevice(req.user.groups.subscribed, serial).then(function(cursor) {
|
||||
return cursor.toArray()
|
||||
})
|
||||
.then(function(devices) {
|
||||
if (!devices.length) {
|
||||
apiutil.respond(res, 404, 'Not Found (device)')
|
||||
}
|
||||
else {
|
||||
getDeviceFilteredGroups(serial, fields, false)
|
||||
.then(function(groups) {
|
||||
return apiutil.respond(res, 200, 'Groups Information', {groups: groups})
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to get device groups: ', err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function getDeviceBookings(req, res) {
|
||||
const serial = req.swagger.params.serial.value
|
||||
const fields = req.swagger.params.fields.value
|
||||
|
||||
dbapi.loadDevice(req.user.groups.subscribed, serial).then(function(cursor) {
|
||||
return cursor.toArray()
|
||||
})
|
||||
.then(function(devices) {
|
||||
if (!devices.length) {
|
||||
apiutil.respond(res, 404, 'Not Found (device)')
|
||||
}
|
||||
else {
|
||||
getDeviceFilteredGroups(serial, fields, true)
|
||||
.then(function(bookings) {
|
||||
apiutil.respond(res, 200, 'Bookings Information', {bookings: bookings})
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to get device bookings: ', err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function addOriginGroupDevices(req, res) {
|
||||
const serials = apiutil.getBodyParameter(req.body, 'serials')
|
||||
const fields = apiutil.getQueryParameter(req.swagger.params.fields)
|
||||
const target = apiutil.getQueryParameter(req.swagger.params.redirected) ? 'device' : 'devices'
|
||||
const lock = {}
|
||||
|
||||
function askUpdateDeviceOriginGroup(group, serial) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
const signature = util.format('%s', uuid.v4()).replace(/-/g, '')
|
||||
let messageListener
|
||||
const responseTimer = setTimeout(function() {
|
||||
req.options.channelRouter.removeListener(wireutil.global, messageListener)
|
||||
apiutil.respond(res, 504, 'Gateway Time-out')
|
||||
reject('timeout')
|
||||
}, 5000)
|
||||
|
||||
messageListener = wirerouter()
|
||||
.on(wire.DeviceOriginGroupMessage, function(channel, message) {
|
||||
if (message.signature === signature) {
|
||||
clearTimeout(responseTimer)
|
||||
req.options.channelRouter.removeListener(wireutil.global, messageListener)
|
||||
dbapi.loadDeviceBySerial(serial).then(function(device) {
|
||||
if (fields) {
|
||||
resolve(_.pick(apiutil.publishDevice(device, req.user), fields.split(',')))
|
||||
}
|
||||
else {
|
||||
resolve(apiutil.publishDevice(device, req.user))
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
.handler()
|
||||
|
||||
req.options.channelRouter.on(wireutil.global, messageListener)
|
||||
return dbapi.askUpdateDeviceOriginGroup(serial, group, signature)
|
||||
})
|
||||
}
|
||||
|
||||
function updateDeviceOriginGroup(group, serial) {
|
||||
const lock = {}
|
||||
|
||||
return dbapi.lockDeviceByOrigin(req.user.groups.subscribed, serial).then(function(stats) {
|
||||
if (!stats.replaced) {
|
||||
return apiutil.lightComputeStats(res, stats)
|
||||
}
|
||||
lock.device = stats.changes[0].new_val
|
||||
|
||||
return dbapi.isUpdateDeviceOriginGroupAllowed(serial, group)
|
||||
.then(function(updatingAllowed) {
|
||||
if (!updatingAllowed) {
|
||||
apiutil.respond(res, 403, 'Forbidden (device is currently booked)')
|
||||
return Promise.reject('booked')
|
||||
}
|
||||
return askUpdateDeviceOriginGroup(group, serial)
|
||||
})
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockDevice(lock)
|
||||
})
|
||||
}
|
||||
|
||||
function updateDevicesOriginGroup(group, serials) {
|
||||
let results = []
|
||||
|
||||
return Promise.each(serials, function(serial) {
|
||||
return updateDeviceOriginGroup(group, serial).then(function(result) {
|
||||
results.push(result)
|
||||
})
|
||||
})
|
||||
.then(function() {
|
||||
const result = target === 'device' ? {device: {}} : {devices: []}
|
||||
|
||||
results = _.without(results, 'unchanged')
|
||||
if (!results.length) {
|
||||
return apiutil.respond(res, 200, `Unchanged (${target})`, result)
|
||||
}
|
||||
results = _.without(results, 'not found')
|
||||
if (!results.length) {
|
||||
return apiutil.respond(res, 404, `Not Found (${target})`)
|
||||
}
|
||||
if (target === 'device') {
|
||||
result.device = results[0]
|
||||
}
|
||||
else {
|
||||
result.devices = results
|
||||
}
|
||||
return apiutil.respond(res, 200, `Updated (${target})`, result)
|
||||
})
|
||||
.catch(function(err) {
|
||||
if (err !== 'booked' && err !== 'timeout' && err !== 'busy') {
|
||||
throw err
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return lockutil.lockGroup(req, res, lock).then(function(lockingSuccessed) {
|
||||
if (lockingSuccessed) {
|
||||
const group = lock.group
|
||||
|
||||
if (!apiutil.isOriginGroup(group.class)) {
|
||||
return apiutil.respond(res, 400, 'Bad Request (this group cannot act as an origin one)')
|
||||
}
|
||||
if (typeof serials !== 'undefined') {
|
||||
return updateDevicesOriginGroup(
|
||||
group
|
||||
, _.without(serials.split(','), '').filter(function(serial) {
|
||||
return group.devices.indexOf(serial) < 0
|
||||
})
|
||||
)
|
||||
}
|
||||
return dbapi.loadDevicesByOrigin(req.user.groups.subscribed).then(function(devices) {
|
||||
if (group.class === apiutil.BOOKABLE) {
|
||||
return devices
|
||||
}
|
||||
return extractStandardizableDevices(devices)
|
||||
})
|
||||
.then(function(devices) {
|
||||
const serials = []
|
||||
|
||||
devices.forEach(function(device) {
|
||||
if (group.devices.indexOf(device.serial) < 0) {
|
||||
serials.push(device.serial)
|
||||
}
|
||||
})
|
||||
return updateDevicesOriginGroup(group, serials)
|
||||
})
|
||||
}
|
||||
return false
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, `Failed to update ${target} origin group: `, err.stack)
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockGroup(lock)
|
||||
})
|
||||
}
|
||||
|
||||
function addOriginGroupDevice(req, res) {
|
||||
apiutil.redirectApiWrapper('serial', addOriginGroupDevices, req, res)
|
||||
}
|
||||
|
||||
function removeOriginGroupDevices(req, res) {
|
||||
const lock = {}
|
||||
|
||||
return lockutil.lockGroup(req, res, lock).then(function(lockingSuccessed) {
|
||||
if (lockingSuccessed) {
|
||||
const group = lock.group
|
||||
|
||||
if (!apiutil.checkBodyParameter(req.body, 'serials')) {
|
||||
req.body = {serials: group.devices.join()}
|
||||
}
|
||||
return dbapi.getRootGroup().then(function(group) {
|
||||
req.swagger.params.id = {value: group.id}
|
||||
return addOriginGroupDevices(req, res)
|
||||
})
|
||||
}
|
||||
return false
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockGroup(lock)
|
||||
})
|
||||
}
|
||||
|
||||
function removeOriginGroupDevice(req, res) {
|
||||
apiutil.redirectApiWrapper('serial', removeOriginGroupDevices, req, res)
|
||||
}
|
||||
|
||||
function deleteDevices(req, res) {
|
||||
const serials = apiutil.getBodyParameter(req.body, 'serials')
|
||||
const target = apiutil.getQueryParameter(req.swagger.params.redirected) ? 'device' : 'devices'
|
||||
|
||||
function removeDevices(serials) {
|
||||
let results = []
|
||||
|
||||
return Promise.each(serials, function(serial) {
|
||||
return removeDevice(serial, req, res).then(function(result) {
|
||||
if (result === 'not deleted') {
|
||||
apiutil.respond(res, 503, 'Server too busy [code: 2], please try again later')
|
||||
return Promise.reject('busy')
|
||||
}
|
||||
return results.push(result)
|
||||
})
|
||||
})
|
||||
.then(function() {
|
||||
results = _.without(results, 'unchanged')
|
||||
if (!results.length) {
|
||||
return apiutil.respond(res, 200, `Unchanged (${target})`)
|
||||
}
|
||||
if (!_.without(results, 'not found').length) {
|
||||
return apiutil.respond(res, 404, `Not Found (${target})`)
|
||||
}
|
||||
return apiutil.respond(res, 200, `Deleted (${target})`)
|
||||
})
|
||||
.catch(function(err) {
|
||||
if (err !== 'busy') {
|
||||
throw err
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
(function() {
|
||||
if (typeof serials === 'undefined') {
|
||||
return dbapi.loadDevicesByOrigin(req.user.groups.subscribed).then(function(devices) {
|
||||
return removeDevices(devices.map(function(device) {
|
||||
return device.serial
|
||||
}))
|
||||
})
|
||||
}
|
||||
else {
|
||||
return removeDevices(_.without(serials.split(','), ''))
|
||||
}
|
||||
})()
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, `Failed to delete ${target}: `, err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function deleteDevice(req, res) {
|
||||
apiutil.redirectApiWrapper('serial', deleteDevices, req, res)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getDevices: getDevices
|
||||
, getDeviceBySerial: getDeviceBySerial
|
||||
, getDeviceGroups: getDeviceGroups
|
||||
, getDeviceBookings: getDeviceBookings
|
||||
, addOriginGroupDevice: addOriginGroupDevice
|
||||
, addOriginGroupDevices: addOriginGroupDevices
|
||||
, removeOriginGroupDevice: removeOriginGroupDevice
|
||||
, removeOriginGroupDevices: removeOriginGroupDevices
|
||||
, deleteDevice: deleteDevice
|
||||
, deleteDevices: deleteDevices
|
||||
}
|
||||
|
|
931
lib/units/api/controllers/groups.js
Normal file
931
lib/units/api/controllers/groups.js
Normal file
|
@ -0,0 +1,931 @@
|
|||
/**
|
||||
* Copyright © 2019 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
|
||||
**/
|
||||
|
||||
const _ = require('lodash')
|
||||
const dbapi = require('../../../db/api')
|
||||
const apiutil = require('../../../util/apiutil')
|
||||
const lockutil = require('../../../util/lockutil')
|
||||
const util = require('util')
|
||||
const uuid = require('uuid')
|
||||
const Promise = require('bluebird')
|
||||
const usersapi = require('./users')
|
||||
|
||||
/* ---------------------------------- PRIVATE FUNCTIONS --------------------------------- */
|
||||
|
||||
function groupApiWrapper(email, fn, req, res) {
|
||||
dbapi.loadUser(email).then(function(user) {
|
||||
if (!user) {
|
||||
apiutil.respond(res, 404, 'Not Found (user)')
|
||||
}
|
||||
else {
|
||||
req.user = user
|
||||
fn(req, res)
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to wrap "%s": ', fn.name, err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function getDevice(req, serial) {
|
||||
return dbapi.loadDeviceBySerial(serial).then(function(device) {
|
||||
if (!device) {
|
||||
throw new Error(`Device not found: ${serial}`)
|
||||
}
|
||||
return apiutil.filterDevice(req, device)
|
||||
})
|
||||
}
|
||||
|
||||
function checkConflicts(id, devices, dates) {
|
||||
function computeConflicts(conflicts, liteGroup, otherGroup) {
|
||||
if (otherGroup.id !== liteGroup.id) {
|
||||
const devices = _.intersection(liteGroup.devices, otherGroup.devices)
|
||||
|
||||
if (devices.length) {
|
||||
for (let liteGroupDate of liteGroup.dates) {
|
||||
for (let otherGroupDate of otherGroup.dates) {
|
||||
if (liteGroupDate.start < otherGroupDate.stop &&
|
||||
liteGroupDate.stop > otherGroupDate.start) {
|
||||
conflicts.push({
|
||||
devices: devices
|
||||
, date: {
|
||||
start: new Date(
|
||||
Math.max(liteGroupDate.start.getTime()
|
||||
, otherGroupDate.start.getTime()))
|
||||
, stop: new Date(
|
||||
Math.min(liteGroupDate.stop.getTime()
|
||||
, otherGroupDate.stop.getTime()))
|
||||
}
|
||||
, group: otherGroup.name
|
||||
, owner: otherGroup.owner
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dbapi.getTransientGroups().then(function(groups) {
|
||||
const conflicts = []
|
||||
|
||||
groups.forEach(function(otherGroup) {
|
||||
computeConflicts(
|
||||
conflicts
|
||||
, {id: id, devices: devices, dates: dates}
|
||||
, otherGroup)
|
||||
})
|
||||
return conflicts
|
||||
})
|
||||
}
|
||||
|
||||
function checkSchedule(res, oldGroup, _class, email, repetitions, privilege, start, stop) {
|
||||
if (oldGroup && oldGroup.devices.length &&
|
||||
(apiutil.isOriginGroup(_class) && !apiutil.isOriginGroup(oldGroup.class) ||
|
||||
apiutil.isOriginGroup(oldGroup.class) && !apiutil.isOriginGroup(_class))) {
|
||||
return Promise.resolve(apiutil.respond(res, 403,
|
||||
'Forbidden (unauthorized class while device list is not empty)'))
|
||||
}
|
||||
if (apiutil.isAdminGroup(_class) && privilege === apiutil.USER) {
|
||||
return Promise.resolve(apiutil.respond(res, 403, 'Forbidden (unauthorized class)'))
|
||||
}
|
||||
if (isNaN(start.getTime())) {
|
||||
return Promise.resolve(apiutil.respond(res, 400, 'Bad Request (Invalid startTime format)'))
|
||||
}
|
||||
if (isNaN(stop.getTime())) {
|
||||
return Promise.resolve(apiutil.respond(res, 400, 'Bad Request (Invalid stopTime format)'))
|
||||
}
|
||||
if (start >= stop) {
|
||||
return Promise.resolve(
|
||||
apiutil.respond(res, 400, 'Bad Request (Invalid life time: startTime >= stopTime)'))
|
||||
}
|
||||
if ((stop - start) > apiutil.CLASS_DURATION[_class]) {
|
||||
return Promise.resolve(apiutil.respond(res, 400,
|
||||
'Bad Request (Invalid Life time & class combination: life time > class duration)'
|
||||
))
|
||||
}
|
||||
switch(_class) {
|
||||
case apiutil.BOOKABLE:
|
||||
case apiutil.STANDARD:
|
||||
case apiutil.ONCE:
|
||||
if (repetitions !== 0) {
|
||||
return Promise.resolve(
|
||||
apiutil.respond(res, 400, 'Bad Request (Invalid class & repetitions combination)'))
|
||||
}
|
||||
break
|
||||
default:
|
||||
if (repetitions === 0) {
|
||||
return Promise.resolve(
|
||||
apiutil.respond(res, 400, 'Bad Request (Invalid class & repetitions combination)'))
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return dbapi.loadUser(email).then(function(owner) {
|
||||
if (repetitions > owner.groups.quotas.repetitions) {
|
||||
return apiutil.respond(res, 400, 'Bad Request (Invalid repetitions value)')
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
/* ---------------------------------- PUBLIC FUNCTIONS ------------------------------------- */
|
||||
|
||||
function addGroupDevices(req, res) {
|
||||
const id = req.swagger.params.id.value
|
||||
const serials = apiutil.getBodyParameter(req.body, 'serials')
|
||||
const target = apiutil.getQueryParameter(req.swagger.params.redirected) ? 'device' : 'devices'
|
||||
const lock = {}
|
||||
let email = null
|
||||
|
||||
function addGroupDevice(group, serial) {
|
||||
const lock = {}
|
||||
|
||||
return dbapi.lockBookableDevice(req.user.groups.subscribed, serial).then(function(stats) {
|
||||
if (!stats.replaced) {
|
||||
return apiutil.lightComputeStats(res, stats)
|
||||
}
|
||||
lock.device = stats.changes[0].new_val
|
||||
|
||||
return dbapi.lockGroup(lock.device.group.origin).then(function(stats) {
|
||||
if (!stats.replaced) {
|
||||
return apiutil.lightComputeStats(res, stats)
|
||||
}
|
||||
lock.group = {id: lock.device.group.origin}
|
||||
|
||||
return checkConflicts(id, [serial], group.dates).then(function(conflicts) {
|
||||
return conflicts.length ?
|
||||
Promise.reject(conflicts) :
|
||||
dbapi.addGroupDevices(group, [serial])
|
||||
})
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockGroup(lock)
|
||||
})
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockDevice(lock)
|
||||
})
|
||||
}
|
||||
|
||||
function _addGroupDevices(lockedGroup, serials) {
|
||||
let results = []
|
||||
let group = lockedGroup
|
||||
|
||||
return Promise.each(serials, function(serial) {
|
||||
return addGroupDevice(group, serial).then(function(result) {
|
||||
results.push(result)
|
||||
if (result.hasOwnProperty('id')) {
|
||||
group = result
|
||||
}
|
||||
})
|
||||
})
|
||||
.then(function() {
|
||||
results = _.without(results, 'unchanged')
|
||||
if (!results.length) {
|
||||
apiutil.respond(res, 200, `Unchanged (group ${target})`, {group: {}})
|
||||
}
|
||||
else {
|
||||
results = _.without(results, 'not found')
|
||||
if (!results.length) {
|
||||
apiutil.respond(res, 404, `Not Found (group ${target})`)
|
||||
}
|
||||
else {
|
||||
apiutil.respond(res, 200, `Added (group ${target})`
|
||||
, {group: apiutil.publishGroup(results[results.length - 1])})
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
if (err === 'quota is reached') {
|
||||
apiutil.respond(res, 403, 'Forbidden (groups duration quota is reached)')
|
||||
}
|
||||
else if (Array.isArray(err)) {
|
||||
apiutil.respond(res, 409, 'Conflicts Information', {conflicts: err})
|
||||
}
|
||||
else if (err !== 'busy') {
|
||||
throw err
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
lockutil.lockGroup(req, res, lock).then(function(lockingSuccessed) {
|
||||
if (lockingSuccessed) {
|
||||
let group = lock.group
|
||||
|
||||
if (req.user.privilege === apiutil.ADMIN && req.user.email !== group.owner.email) {
|
||||
email = group.owner.email
|
||||
return false
|
||||
}
|
||||
if (apiutil.isOriginGroup(group.class)) {
|
||||
return apiutil.respond(res, 400, 'Bad Request (use admin API for bookable/standard groups)')
|
||||
}
|
||||
|
||||
return (function() {
|
||||
if (typeof serials === 'undefined') {
|
||||
return dbapi.loadBookableDevices(req.user.groups.subscribed).then(function(devices) {
|
||||
const serials = []
|
||||
|
||||
devices.forEach(function(device) {
|
||||
if (group.devices.indexOf(device.serial) < 0) {
|
||||
serials.push(device.serial)
|
||||
}
|
||||
})
|
||||
return _addGroupDevices(group, serials)
|
||||
})
|
||||
}
|
||||
else {
|
||||
return _addGroupDevices(
|
||||
group
|
||||
, _.difference(
|
||||
_.without(serials.split(','), '')
|
||||
, group.devices)
|
||||
)
|
||||
}
|
||||
})()
|
||||
}
|
||||
return false
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, `Failed to add group ${target}: `, err.stack)
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockGroup(lock)
|
||||
if (email) {
|
||||
groupApiWrapper(email, addGroupDevices, req, res)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function addGroupDevice(req, res) {
|
||||
apiutil.redirectApiWrapper('serial', addGroupDevices, req, res)
|
||||
}
|
||||
|
||||
function removeGroupDevices(req, res) {
|
||||
const serials = apiutil.getBodyParameter(req.body, 'serials')
|
||||
const target = apiutil.getQueryParameter(req.swagger.params.redirected) ? 'device' : 'devices'
|
||||
const lock = {}
|
||||
|
||||
lockutil.lockGroup(req, res, lock).then(function(lockingSuccessed) {
|
||||
if (lockingSuccessed) {
|
||||
const group = lock.group
|
||||
|
||||
if (apiutil.isOriginGroup(group.class)) {
|
||||
return apiutil.respond(res, 400, 'Bad Request (use admin API for bookable/standard groups)')
|
||||
}
|
||||
let serialsToRemove = group.devices
|
||||
|
||||
if (typeof serials !== 'undefined') {
|
||||
serialsToRemove = _.without(serials.split(','), '')
|
||||
}
|
||||
if (!serialsToRemove.length) {
|
||||
return apiutil.respond(res, 200, `Unchanged (group ${target})`, {group: {}})
|
||||
}
|
||||
serialsToRemove = _.intersection(serialsToRemove, group.devices)
|
||||
if (!serialsToRemove.length) {
|
||||
return apiutil.respond(res, 404, `Not Found (group ${target})`)
|
||||
}
|
||||
return dbapi.removeGroupDevices(group, serialsToRemove).then(function(group) {
|
||||
apiutil.respond(res, 200, `Removed (group ${target})`, {group: apiutil.publishGroup(group)})
|
||||
})
|
||||
}
|
||||
return false
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, `Failed to remove group ${target}: `, err.stack)
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockGroup(lock)
|
||||
})
|
||||
}
|
||||
|
||||
function removeGroupDevice(req, res) {
|
||||
apiutil.redirectApiWrapper('serial', removeGroupDevices, req, res)
|
||||
}
|
||||
|
||||
function getGroupDevice(req, res) {
|
||||
const id = req.swagger.params.id.value
|
||||
const serial = req.swagger.params.serial.value
|
||||
|
||||
dbapi.getUserGroup(req.user.email, id).then(function(group) {
|
||||
if (!group) {
|
||||
apiutil.respond(res, 404, 'Not Found (group)')
|
||||
}
|
||||
else if (group.devices.indexOf(serial) < 0) {
|
||||
apiutil.respond(res, 404, 'Not Found (device)')
|
||||
}
|
||||
else {
|
||||
getDevice(req, serial).then(function(device) {
|
||||
apiutil.respond(res, 200, 'Device Information', {device: device})
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to get group device: ', err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function getGroupUser(req, res) {
|
||||
const id = req.swagger.params.id.value
|
||||
const email = req.swagger.params.email.value
|
||||
|
||||
dbapi.getUserGroup(req.user.email, id).then(function(group) {
|
||||
if (!group) {
|
||||
apiutil.respond(res, 404, 'Not Found (group)')
|
||||
}
|
||||
else if (group.users.indexOf(email) < 0) {
|
||||
apiutil.respond(res, 404, 'Not Found (user)')
|
||||
}
|
||||
else {
|
||||
usersapi.getUserByEmail(req, res)
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to get group user: ', err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function getGroupUsers(req, res) {
|
||||
const id = req.swagger.params.id.value
|
||||
|
||||
dbapi.getUserGroup(req.user.email, id).then(function(group) {
|
||||
if (!group) {
|
||||
apiutil.respond(res, 404, 'Not Found (group)')
|
||||
}
|
||||
else {
|
||||
Promise.map(group.users, function(email) {
|
||||
return usersapi.getUserInfo(req, email).then(function(user) {
|
||||
return user || Promise.reject(`Group user not found: ${email}`)
|
||||
})
|
||||
})
|
||||
.then(function(users) {
|
||||
apiutil.respond(res, 200, 'Users Information', {users: users})
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to get group users: ', err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function removeGroupUsers(req, res) {
|
||||
const id = req.swagger.params.id.value
|
||||
const emails = apiutil.getBodyParameter(req.body, 'emails')
|
||||
const target = apiutil.getQueryParameter(req.swagger.params.redirected) ? 'user' : 'users'
|
||||
const lock = {}
|
||||
|
||||
function removeGroupUser(email, group, rootGroup) {
|
||||
if (group.users.indexOf(email) < 0) {
|
||||
return Promise.resolve('not found')
|
||||
}
|
||||
if (email === rootGroup.owner.email || email === group.owner.email) {
|
||||
return Promise.resolve('forbidden')
|
||||
}
|
||||
const lock = {}
|
||||
|
||||
return dbapi.lockUser(email).then(function(stats) {
|
||||
if (!stats.replaced) {
|
||||
return apiutil.lightComputeStats(res, stats)
|
||||
}
|
||||
lock.user = stats.changes[0].new_val
|
||||
|
||||
return dbapi.isRemoveGroupUserAllowed(email, group)
|
||||
.then(function(isAllowed) {
|
||||
return isAllowed ? dbapi.removeGroupUser(id, email) : 'forbidden'
|
||||
})
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockUser(lock)
|
||||
})
|
||||
}
|
||||
|
||||
lockutil.lockGroup(req, res, lock).then(function(lockingSuccessed) {
|
||||
if (lockingSuccessed) {
|
||||
const group = lock.group
|
||||
|
||||
return dbapi.getRootGroup().then(function(rootGroup) {
|
||||
let emailsToRemove = group.users
|
||||
let results = []
|
||||
|
||||
if (typeof emails !== 'undefined') {
|
||||
emailsToRemove = _.without(emails.split(','), '')
|
||||
}
|
||||
return Promise.each(emailsToRemove, function(email) {
|
||||
return removeGroupUser(email, group, rootGroup).then(function(result) {
|
||||
results.push(result)
|
||||
})
|
||||
})
|
||||
.then(function() {
|
||||
if (!results.length) {
|
||||
return apiutil.respond(res, 200, `Unchanged (group ${target})`, {group: {}})
|
||||
}
|
||||
results = _.without(results, 'not found')
|
||||
if (!results.length) {
|
||||
return apiutil.respond(res, 404, `Not Found (group ${target})`)
|
||||
}
|
||||
if (!_.without(results, 'forbidden').length) {
|
||||
return apiutil.respond(res, 403, `Forbidden (group ${target})`)
|
||||
}
|
||||
return dbapi.getGroup(id).then(function(group) {
|
||||
apiutil.respond(res, 200, `Removed (group ${target})`, {
|
||||
group: apiutil.publishGroup(group)})
|
||||
})
|
||||
})
|
||||
})
|
||||
.catch(function(err) {
|
||||
if (err !== 'busy') {
|
||||
throw err
|
||||
}
|
||||
})
|
||||
}
|
||||
return false
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, `Failed to remove group ${target}: `, err.stack)
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockGroup(lock)
|
||||
})
|
||||
}
|
||||
|
||||
function removeGroupUser(req, res) {
|
||||
apiutil.redirectApiWrapper('email', removeGroupUsers, req, res)
|
||||
}
|
||||
|
||||
function addGroupUsers(req, res) {
|
||||
const id = req.swagger.params.id.value
|
||||
const emails = apiutil.getBodyParameter(req.body, 'emails')
|
||||
const target = apiutil.getQueryParameter(req.swagger.params.redirected) ? 'user' : 'users'
|
||||
const lock = {}
|
||||
|
||||
function addGroupUser(email) {
|
||||
const lock = {}
|
||||
|
||||
return dbapi.lockUser(email).then(function(stats) {
|
||||
if (!stats.replaced) {
|
||||
return apiutil.lightComputeStats(res, stats)
|
||||
}
|
||||
lock.user = stats.changes[0].new_val
|
||||
|
||||
return dbapi.addGroupUser(id, email)
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockUser(lock)
|
||||
})
|
||||
}
|
||||
|
||||
function _addGroupUsers(emails) {
|
||||
let results = []
|
||||
|
||||
return Promise.each(emails, function(email) {
|
||||
return addGroupUser(email).then(function(result) {
|
||||
results.push(result)
|
||||
})
|
||||
})
|
||||
.then(function() {
|
||||
results = _.without(results, 'unchanged')
|
||||
if (!results.length) {
|
||||
return apiutil.respond(res, 200, `Unchanged (group ${target})`, {group: {}})
|
||||
}
|
||||
if (!_.without(results, 'not found').length) {
|
||||
return apiutil.respond(res, 404, `Not Found (group ${target})`)
|
||||
}
|
||||
return dbapi.getGroup(id).then(function(group) {
|
||||
apiutil.respond(res, 200, `Added (group ${target})`, {group: apiutil.publishGroup(group)})
|
||||
})
|
||||
})
|
||||
.catch(function(err) {
|
||||
if (err !== 'busy') {
|
||||
throw err
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
lockutil.lockGroup(req, res, lock).then(function(lockingSuccessed) {
|
||||
if (!lockingSuccessed) {
|
||||
return false
|
||||
}
|
||||
const group = lock.group
|
||||
|
||||
return (function() {
|
||||
if (typeof emails === 'undefined') {
|
||||
return dbapi.getUsers().then(function(users) {
|
||||
const emails = []
|
||||
|
||||
users.forEach(function(user) {
|
||||
if (group.users.indexOf(user.email) < 0) {
|
||||
emails.push(user.email)
|
||||
}
|
||||
})
|
||||
return _addGroupUsers(emails)
|
||||
})
|
||||
}
|
||||
else {
|
||||
return _addGroupUsers(
|
||||
_.difference(
|
||||
_.without(emails.split(','), '')
|
||||
, group.users)
|
||||
)
|
||||
}
|
||||
})()
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, `Failed to add group ${target}: `, err.stack)
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockGroup(lock)
|
||||
})
|
||||
}
|
||||
|
||||
function addGroupUser(req, res) {
|
||||
apiutil.redirectApiWrapper('email', addGroupUsers, req, res)
|
||||
}
|
||||
|
||||
function getGroup(req, res) {
|
||||
const id = req.swagger.params.id.value
|
||||
const fields = req.swagger.params.fields.value
|
||||
|
||||
dbapi.getUserGroup(req.user.email, id).then(function(group) {
|
||||
if (!group) {
|
||||
apiutil.respond(res, 404, 'Not Found (group)')
|
||||
return
|
||||
}
|
||||
let publishedGroup = apiutil.publishGroup(group)
|
||||
|
||||
if (fields) {
|
||||
publishedGroup = _.pick(publishedGroup, fields.split(','))
|
||||
}
|
||||
apiutil.respond(res, 200, 'Group Information', {group: publishedGroup})
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to get group: ', err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function getGroups(req, res) {
|
||||
const fields = req.swagger.params.fields.value
|
||||
const owner = req.swagger.params.owner.value
|
||||
let getGenericGroups
|
||||
|
||||
switch(owner) {
|
||||
case true:
|
||||
getGenericGroups = dbapi.getOwnerGroups
|
||||
break
|
||||
case false:
|
||||
getGenericGroups = dbapi.getOnlyUserGroups
|
||||
break
|
||||
default:
|
||||
getGenericGroups = dbapi.getUserGroups
|
||||
}
|
||||
getGenericGroups(req.user.email).then(function(groups) {
|
||||
return apiutil.respond(res, 200, 'Groups Information', {
|
||||
groups: groups.map(function(group) {
|
||||
if (fields) {
|
||||
return _.pick(apiutil.publishGroup(group), fields.split(','))
|
||||
}
|
||||
return apiutil.publishGroup(group)
|
||||
})
|
||||
})
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to get groups: ', err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function createGroup(req, res) {
|
||||
const _class = typeof req.body.class === 'undefined' ? apiutil.ONCE : req.body.class
|
||||
const repetitions =
|
||||
apiutil.isOriginGroup(_class) || typeof req.body.repetitions === 'undefined' ?
|
||||
0 :
|
||||
req.body.repetitions
|
||||
const now = Date.now()
|
||||
const start =
|
||||
apiutil.isOriginGroup(_class) ?
|
||||
new Date(now) :
|
||||
new Date(req.body.startTime || now)
|
||||
const stop =
|
||||
apiutil.isOriginGroup(_class) ?
|
||||
new Date(now + apiutil.ONE_YEAR) :
|
||||
new Date(req.body.stopTime || now + apiutil.ONE_HOUR)
|
||||
|
||||
checkSchedule(res, null, _class, req.user.email, repetitions, req.user.privilege,
|
||||
start, stop).then(function(checkingSuccessed) {
|
||||
if (!checkingSuccessed) {
|
||||
return
|
||||
}
|
||||
const name =
|
||||
typeof req.body.name === 'undefined' ?
|
||||
'New_' + util.format('%s', uuid.v4()).replace(/-/g, '') :
|
||||
req.body.name
|
||||
const state =
|
||||
apiutil.isOriginGroup(_class) || typeof req.body.state === 'undefined' ?
|
||||
apiutil.READY :
|
||||
req.body.state
|
||||
const isActive = state === apiutil.READY && apiutil.isOriginGroup(_class)
|
||||
const duration = 0
|
||||
const dates = apiutil.computeGroupDates({start: start, stop: stop}, _class, repetitions)
|
||||
|
||||
dbapi.createUserGroup({
|
||||
name: name
|
||||
, owner: {
|
||||
email: req.user.email
|
||||
, name: req.user.name
|
||||
}
|
||||
, privilege: req.user.privilege
|
||||
, class: _class
|
||||
, repetitions: repetitions
|
||||
, isActive: isActive
|
||||
, dates: dates
|
||||
, duration: duration
|
||||
, state: state
|
||||
})
|
||||
.then(function(group) {
|
||||
if (group) {
|
||||
apiutil.respond(res, 201, 'Created', {group: apiutil.publishGroup(group)})
|
||||
}
|
||||
else {
|
||||
apiutil.respond(res, 403, 'Forbidden (groups number quota is reached)')
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to create group: ', err.stack)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function deleteGroups(req, res) {
|
||||
const ids = apiutil.getBodyParameter(req.body, 'ids')
|
||||
const target = apiutil.getQueryParameter(req.swagger.params.redirected) ? 'group' : 'groups'
|
||||
|
||||
function removeGroup(id) {
|
||||
const lock = {}
|
||||
|
||||
return dbapi.lockGroupByOwner(req.user.email, id).then(function(stats) {
|
||||
if (!stats.replaced) {
|
||||
return apiutil.lightComputeStats(res, stats)
|
||||
}
|
||||
const group = lock.group = stats.changes[0].new_val
|
||||
|
||||
if (group.privilege === apiutil.ROOT) {
|
||||
return 'forbidden'
|
||||
}
|
||||
if (group.class === apiutil.BOOKABLE) {
|
||||
return Promise.each(group.devices, function(serial) {
|
||||
return dbapi.isDeviceBooked(serial)
|
||||
.then(function(isBooked) {
|
||||
return isBooked ? Promise.reject('booked') : true
|
||||
})
|
||||
})
|
||||
.then(function() {
|
||||
return dbapi.deleteUserGroup(id)
|
||||
})
|
||||
.catch(function(err) {
|
||||
if (err !== 'booked') {
|
||||
throw err
|
||||
}
|
||||
return 'forbidden'
|
||||
})
|
||||
}
|
||||
else {
|
||||
return dbapi.deleteUserGroup(id)
|
||||
}
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockGroup(lock)
|
||||
})
|
||||
}
|
||||
|
||||
function removeGroups(ids) {
|
||||
let results = []
|
||||
|
||||
return Promise.each(ids, function(id) {
|
||||
return removeGroup(id).then(function(result) {
|
||||
results.push(result)
|
||||
})
|
||||
})
|
||||
.then(function() {
|
||||
if (!results.length) {
|
||||
return apiutil.respond(res, 200, `Unchanged (${target})`)
|
||||
}
|
||||
results = _.without(results, 'not found')
|
||||
if (!results.length) {
|
||||
return apiutil.respond(res, 404, `Not Found (${target})`)
|
||||
}
|
||||
results = _.without(results, 'forbidden')
|
||||
if (!results.length) {
|
||||
return apiutil.respond(res, 403, `Forbidden (${target})`)
|
||||
}
|
||||
return apiutil.respond(res, 200, `Deleted (${target})`)
|
||||
})
|
||||
.catch(function(err) {
|
||||
if (err !== 'busy') {
|
||||
throw err
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
(function() {
|
||||
if (typeof ids === 'undefined') {
|
||||
return dbapi.getOwnerGroups(req.user.email).then(function(groups) {
|
||||
const ids = []
|
||||
|
||||
groups.forEach(function(group) {
|
||||
if (group.privilege !== apiutil.ROOT) {
|
||||
ids.push(group.id)
|
||||
}
|
||||
})
|
||||
return removeGroups(ids)
|
||||
})
|
||||
}
|
||||
else {
|
||||
return removeGroups(_.without(ids.split(','), ''))
|
||||
}
|
||||
})()
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, `Failed to delete ${target}: `, err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function deleteGroup(req, res) {
|
||||
apiutil.redirectApiWrapper('id', deleteGroups, req, res)
|
||||
}
|
||||
|
||||
function updateGroup(req, res) {
|
||||
const id = req.swagger.params.id.value
|
||||
const lock = {}
|
||||
|
||||
function updateUserGroup(group, data) {
|
||||
return dbapi.updateUserGroup(group, data)
|
||||
.then(function(group) {
|
||||
if (group) {
|
||||
apiutil.respond(res, 200, 'Updated (group)', {group: apiutil.publishGroup(group)})
|
||||
}
|
||||
else {
|
||||
apiutil.respond(res, 403, 'Forbidden (groups duration quota is reached)')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
lockutil.lockGroup(req, res, lock).then(function(lockingSuccessed) {
|
||||
if (!lockingSuccessed) {
|
||||
return false
|
||||
}
|
||||
const group = lock.group
|
||||
const _class = typeof req.body.class === 'undefined' ? group.class : req.body.class
|
||||
const name = typeof req.body.name === 'undefined' ? group.name : req.body.name
|
||||
const repetitions =
|
||||
typeof req.body.repetitions === 'undefined' ?
|
||||
group.repetitions :
|
||||
req.body.repetitions
|
||||
const start = new Date(req.body.startTime || group.dates[0].start)
|
||||
const stop = new Date(req.body.stopTime || group.dates[0].stop)
|
||||
let state, isActive
|
||||
|
||||
if (apiutil.isOriginGroup(_class)) {
|
||||
state = apiutil.READY
|
||||
isActive = true
|
||||
}
|
||||
else {
|
||||
state = typeof req.body.state === 'undefined' ? apiutil.PENDING : req.body.state
|
||||
isActive = false
|
||||
}
|
||||
|
||||
if (group.state === apiutil.READY && state === apiutil.PENDING) {
|
||||
return apiutil.respond(res, 403, 'Forbidden (group is ready)')
|
||||
}
|
||||
|
||||
return checkSchedule(res, group, _class, group.owner.email, repetitions, group.privilege,
|
||||
start, stop).then(function(checkingSuccessed) {
|
||||
if (!checkingSuccessed) {
|
||||
return false
|
||||
}
|
||||
if (name === group.name &&
|
||||
start.toISOString() === group.dates[0].start.toISOString() &&
|
||||
stop.toISOString() === group.dates[0].stop.toISOString() &&
|
||||
state === group.state &&
|
||||
_class === group.class &&
|
||||
repetitions === group.repetitions) {
|
||||
return apiutil.respond(res, 200, 'Unchanged (group)', {group: {}})
|
||||
}
|
||||
const duration = group.devices.length * (stop - start) * (repetitions + 1)
|
||||
const dates = apiutil.computeGroupDates({start: start, stop: stop}, _class, repetitions)
|
||||
|
||||
if (start < group.dates[0].start ||
|
||||
stop > group.dates[0].stop ||
|
||||
repetitions > group.repetitions ||
|
||||
_class !== group.class) {
|
||||
return checkConflicts(id, group.devices, dates)
|
||||
.then(function(conflicts) {
|
||||
if (!conflicts.length) {
|
||||
return updateUserGroup(group, {
|
||||
name: name
|
||||
, state: state
|
||||
, class: _class
|
||||
, isActive: isActive
|
||||
, repetitions: repetitions
|
||||
, dates: dates
|
||||
, duration: duration
|
||||
})
|
||||
}
|
||||
return apiutil.respond(res, 409, 'Conflicts Information', {conflicts: conflicts})
|
||||
})
|
||||
}
|
||||
return updateUserGroup(group, {
|
||||
name: name
|
||||
, state: state
|
||||
, class: _class
|
||||
, isActive: isActive
|
||||
, repetitions: repetitions
|
||||
, dates: dates
|
||||
, duration: duration
|
||||
})
|
||||
})
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to update group: ', err.stack)
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockGroup(lock)
|
||||
})
|
||||
}
|
||||
|
||||
function getGroupDevices(req, res) {
|
||||
const id = req.swagger.params.id.value
|
||||
const bookable = req.swagger.params.bookable.value
|
||||
|
||||
dbapi.getUserGroup(req.user.email, id).then(function(group) {
|
||||
if (!group) {
|
||||
apiutil.respond(res, 404, 'Not Found (group)')
|
||||
return
|
||||
}
|
||||
if (bookable) {
|
||||
if (apiutil.isOriginGroup(group.class)) {
|
||||
apiutil.respond(res, 400, 'Bad Request (group is not transient)')
|
||||
return
|
||||
}
|
||||
if (req.user.privilege === apiutil.ADMIN && req.user.email !== group.owner.email) {
|
||||
groupApiWrapper(group.owner.email, getGroupDevices, req, res)
|
||||
return
|
||||
}
|
||||
dbapi.loadBookableDevices(req.user.groups.subscribed).then(function(devices) {
|
||||
Promise.map(devices, function(device) {
|
||||
return device.serial
|
||||
})
|
||||
.then(function(serials) {
|
||||
return checkConflicts(group.id, serials, group.dates)
|
||||
.then(function(conflicts) {
|
||||
let bookableSerials = serials
|
||||
|
||||
conflicts.forEach(function(conflict) {
|
||||
bookableSerials = _.difference(bookableSerials, conflict.devices)
|
||||
})
|
||||
return bookableSerials
|
||||
})
|
||||
})
|
||||
.then(function(bookableSerials) {
|
||||
const deviceList = []
|
||||
|
||||
devices.forEach(function(device) {
|
||||
if (bookableSerials.indexOf(device.serial) > -1) {
|
||||
deviceList.push(apiutil.filterDevice(req, device))
|
||||
}
|
||||
})
|
||||
apiutil.respond(res, 200, 'Devices Information', {devices: deviceList})
|
||||
})
|
||||
})
|
||||
}
|
||||
else {
|
||||
Promise.map(group.devices, function(serial) {
|
||||
return getDevice(req, serial)
|
||||
})
|
||||
.then(function(devices) {
|
||||
apiutil.respond(res, 200, 'Devices Information', {devices: devices})
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to get group devices: ', err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createGroup: createGroup
|
||||
, updateGroup: updateGroup
|
||||
, deleteGroup: deleteGroup
|
||||
, deleteGroups: deleteGroups
|
||||
, getGroup: getGroup
|
||||
, getGroups: getGroups
|
||||
, getGroupUser: getGroupUser
|
||||
, getGroupUsers: getGroupUsers
|
||||
, addGroupUser: addGroupUser
|
||||
, addGroupUsers: addGroupUsers
|
||||
, removeGroupUser: removeGroupUser
|
||||
, removeGroupUsers: removeGroupUsers
|
||||
, getGroupDevice: getGroupDevice
|
||||
, getGroupDevices: getGroupDevices
|
||||
, addGroupDevice: addGroupDevice
|
||||
, addGroupDevices: addGroupDevices
|
||||
, removeGroupDevice: removeGroupDevice
|
||||
, removeGroupDevices: removeGroupDevices
|
||||
}
|
|
@ -1,3 +1,7 @@
|
|||
/**
|
||||
* Copyright © 2019 contains code contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
|
||||
**/
|
||||
|
||||
var util = require('util')
|
||||
|
||||
var _ = require('lodash')
|
||||
|
@ -12,20 +16,121 @@ var wire = require('../../../wire')
|
|||
var wireutil = require('../../../wire/util')
|
||||
var wirerouter = require('../../../wire/router')
|
||||
|
||||
const apiutil = require('../../../util/apiutil')
|
||||
const jwtutil = require('../../../util/jwtutil')
|
||||
|
||||
var log = logger.createLogger('api:controllers:user')
|
||||
|
||||
module.exports = {
|
||||
getUser: getUser
|
||||
, getUserDevices: getUserDevices
|
||||
, addUserDevice: addUserDevice
|
||||
, getUserDeviceBySerial: getUserDeviceBySerial
|
||||
, deleteUserDeviceBySerial: deleteUserDeviceBySerial
|
||||
, remoteConnectUserDeviceBySerial: remoteConnectUserDeviceBySerial
|
||||
, remoteDisconnectUserDeviceBySerial: remoteDisconnectUserDeviceBySerial
|
||||
, getUserAccessTokens: getUserAccessTokens
|
||||
function getAccessToken(req, res) {
|
||||
const id = req.swagger.params.id.value
|
||||
|
||||
dbapi.loadAccessToken(id).then(function(token) {
|
||||
if (!token || token.email !== req.user.email) {
|
||||
apiutil.respond(res, 404, 'Not Found (access token)')
|
||||
}
|
||||
else {
|
||||
apiutil.respond(res, 200, 'Access Token Information', {
|
||||
token: apiutil.publishAccessToken(token)
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to delete access token "%s": ', id, err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function getAccessTokens(req, res) {
|
||||
dbapi.loadAccessTokens(req.user.email).then(function(cursor) {
|
||||
Promise.promisify(cursor.toArray, cursor)().then(function(tokens) {
|
||||
const tokenList = []
|
||||
|
||||
tokens.forEach(function(token) {
|
||||
tokenList.push(apiutil.publishAccessToken(token))
|
||||
})
|
||||
apiutil.respond(res, 200, 'Access Tokens Information', {tokens: tokenList})
|
||||
})
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to get access tokens: ', err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function createAccessToken(req, res) {
|
||||
const title = req.swagger.params.title.value
|
||||
const jwt = jwtutil.encode({
|
||||
payload: {
|
||||
email: req.user.email
|
||||
, name: req.user.name
|
||||
}
|
||||
, secret: req.options.secret
|
||||
})
|
||||
const id = util.format('%s-%s', uuid.v4(), uuid.v4()).replace(/-/g, '')
|
||||
|
||||
dbapi.saveUserAccessToken(req.user.email, {
|
||||
title: title
|
||||
, id: id
|
||||
, jwt: jwt
|
||||
})
|
||||
.then(function(stats) {
|
||||
req.options.pushdev.send([
|
||||
req.user.group
|
||||
, wireutil.envelope(new wire.UpdateAccessTokenMessage())
|
||||
])
|
||||
apiutil.respond(res, 201, 'Created (access token)',
|
||||
{token: apiutil.publishAccessToken(stats.changes[0].new_val)})
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to create access token "%s": ', title, err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function deleteAccessTokens(req, res) {
|
||||
dbapi.removeUserAccessTokens(req.user.email).then(function(stats) {
|
||||
if (!stats.deleted) {
|
||||
apiutil.respond(res, 200, 'Unchanged (access tokens)')
|
||||
}
|
||||
else {
|
||||
req.options.pushdev.send([
|
||||
req.user.group
|
||||
, wireutil.envelope(new wire.UpdateAccessTokenMessage())
|
||||
])
|
||||
apiutil.respond(res, 200, 'Deleted (access tokens)')
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to delete access tokens: ', err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function deleteAccessToken(req, res) {
|
||||
const id = req.swagger.params.id.value
|
||||
|
||||
dbapi.loadAccessToken(id).then(function(token) {
|
||||
if (!token || token.email !== req.user.email) {
|
||||
apiutil.respond(res, 404, 'Not Found (access token)')
|
||||
}
|
||||
else {
|
||||
dbapi.removeAccessToken(id).then(function(stats) {
|
||||
if (!stats.deleted) {
|
||||
apiutil.respond(res, 404, 'Not Found (access token)')
|
||||
}
|
||||
else {
|
||||
req.options.pushdev.send([
|
||||
req.user.group
|
||||
, wireutil.envelope(new wire.UpdateAccessTokenMessage())
|
||||
])
|
||||
apiutil.respond(res, 200, 'Deleted (access token)')
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to delete access token "%s": ', id, err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function getUser(req, res) {
|
||||
// delete req.user.groups.lock
|
||||
res.json({
|
||||
success: true
|
||||
, user: req.user
|
||||
|
@ -52,6 +157,7 @@ function getUserDevices(req, res) {
|
|||
|
||||
res.json({
|
||||
success: true
|
||||
, description: 'Controlled devices information'
|
||||
, devices: deviceList
|
||||
})
|
||||
})
|
||||
|
@ -60,6 +166,7 @@ function getUserDevices(req, res) {
|
|||
log.error('Failed to load device list: ', err.stack)
|
||||
res.status(500).json({
|
||||
success: false
|
||||
, description: 'Internal Server Error'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -68,113 +175,121 @@ function getUserDeviceBySerial(req, res) {
|
|||
var serial = req.swagger.params.serial.value
|
||||
var fields = req.swagger.params.fields.value
|
||||
|
||||
dbapi.loadDevice(serial)
|
||||
.then(function(device) {
|
||||
if (!device) {
|
||||
return res.status(404).json({
|
||||
success: false
|
||||
, description: 'Device not found'
|
||||
dbapi.loadDevice(req.user.groups.subscribed, serial)
|
||||
.then(function(cursor) {
|
||||
cursor.next(function(err, device) {
|
||||
if (err) {
|
||||
return res.status(404).json({
|
||||
success: false
|
||||
, description: 'Device not found'
|
||||
})
|
||||
}
|
||||
|
||||
datautil.normalize(device, req.user)
|
||||
if (!deviceutil.isOwnedByUser(device, req.user)) {
|
||||
return res.status(403).json({
|
||||
success: false
|
||||
, description: 'Device is not owned by you'
|
||||
})
|
||||
}
|
||||
|
||||
var responseDevice = device
|
||||
if (fields) {
|
||||
responseDevice = _.pick(device, fields.split(','))
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true
|
||||
, description: 'Controlled device information'
|
||||
, device: responseDevice
|
||||
})
|
||||
}
|
||||
|
||||
datautil.normalize(device, req.user)
|
||||
if (!deviceutil.isOwnedByUser(device, req.user)) {
|
||||
return res.status(403).json({
|
||||
success: false
|
||||
, description: 'Device is not owned by you'
|
||||
})
|
||||
}
|
||||
|
||||
var responseDevice = device
|
||||
if (fields) {
|
||||
responseDevice = _.pick(device, fields.split(','))
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true
|
||||
, device: responseDevice
|
||||
})
|
||||
})
|
||||
.catch(function(err) {
|
||||
log.error('Failed to load device "%s": ', req.params.serial, err.stack)
|
||||
res.status(500).json({
|
||||
success: false
|
||||
, description: 'Internal Server Error'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function addUserDevice(req, res) {
|
||||
var serial = req.body.serial
|
||||
var timeout = req.body.timeout || null
|
||||
var serial = req.hasOwnProperty('body') ? req.body.serial : req.swagger.params.serial.value
|
||||
var timeout = req.hasOwnProperty('body') ? req.body.timeout ||
|
||||
null : req.swagger.params.timeout.value || null
|
||||
|
||||
dbapi.loadDevice(serial)
|
||||
.then(function(device) {
|
||||
if (!device) {
|
||||
return res.status(404).json({
|
||||
success: false
|
||||
, description: 'Device not found'
|
||||
})
|
||||
}
|
||||
|
||||
datautil.normalize(device, req.user)
|
||||
if (!deviceutil.isAddable(device, req.user)) {
|
||||
return res.status(403).json({
|
||||
success: false
|
||||
, description: 'Device is being used or not available'
|
||||
})
|
||||
}
|
||||
|
||||
// Timer will be called if no JoinGroupMessage is received till 5 seconds
|
||||
var responseTimer = setTimeout(function() {
|
||||
req.options.channelRouter.removeListener(wireutil.global, messageListener)
|
||||
return res.status(504).json({
|
||||
dbapi.loadDevice(req.user.groups.subscribed, serial)
|
||||
.then(function(cursor) {
|
||||
cursor.next(function(err, device) {
|
||||
if (err) {
|
||||
return res.status(404).json({
|
||||
success: false
|
||||
, description: 'Device is not responding'
|
||||
})
|
||||
}, 5000)
|
||||
, description: 'Device not found'
|
||||
})
|
||||
}
|
||||
|
||||
var messageListener = wirerouter()
|
||||
.on(wire.JoinGroupMessage, function(channel, message) {
|
||||
if (message.serial === serial && message.owner.email === req.user.email) {
|
||||
clearTimeout(responseTimer)
|
||||
req.options.channelRouter.removeListener(wireutil.global, messageListener)
|
||||
datautil.normalize(device, req.user)
|
||||
if (!deviceutil.isAddable(device, req.user)) {
|
||||
return res.status(403).json({
|
||||
success: false
|
||||
, description: 'Device is being used or not available'
|
||||
})
|
||||
}
|
||||
|
||||
return res.json({
|
||||
success: true
|
||||
, description: 'Device successfully added'
|
||||
})
|
||||
}
|
||||
})
|
||||
.handler()
|
||||
// Timer will be called if no JoinGroupMessage is received till 5 seconds
|
||||
var responseTimer = setTimeout(function() {
|
||||
req.options.channelRouter.removeListener(wireutil.global, messageListener)
|
||||
return res.status(504).json({
|
||||
success: false
|
||||
, description: 'Device is not responding'
|
||||
})
|
||||
}, 5000)
|
||||
|
||||
req.options.channelRouter.on(wireutil.global, messageListener)
|
||||
var usage = 'automation'
|
||||
var messageListener = wirerouter()
|
||||
.on(wire.JoinGroupMessage, function(channel, message) {
|
||||
if (message.serial === serial && message.owner.email === req.user.email) {
|
||||
clearTimeout(responseTimer)
|
||||
req.options.channelRouter.removeListener(wireutil.global, messageListener)
|
||||
|
||||
req.options.push.send([
|
||||
device.channel
|
||||
, wireutil.envelope(
|
||||
new wire.GroupMessage(
|
||||
new wire.OwnerMessage(
|
||||
req.user.email
|
||||
, req.user.name
|
||||
, req.user.group
|
||||
)
|
||||
, timeout
|
||||
, wireutil.toDeviceRequirements({
|
||||
serial: {
|
||||
value: serial
|
||||
, match: 'exact'
|
||||
return res.json({
|
||||
success: true
|
||||
, description: 'Device successfully added'
|
||||
})
|
||||
}
|
||||
})
|
||||
, usage
|
||||
.handler()
|
||||
|
||||
req.options.channelRouter.on(wireutil.global, messageListener)
|
||||
var usage = 'automation'
|
||||
|
||||
req.options.push.send([
|
||||
device.channel
|
||||
, wireutil.envelope(
|
||||
new wire.GroupMessage(
|
||||
new wire.OwnerMessage(
|
||||
req.user.email
|
||||
, req.user.name
|
||||
, req.user.group
|
||||
)
|
||||
, timeout
|
||||
, wireutil.toDeviceRequirements({
|
||||
serial: {
|
||||
value: serial
|
||||
, match: 'exact'
|
||||
}
|
||||
})
|
||||
, usage
|
||||
)
|
||||
)
|
||||
)
|
||||
])
|
||||
])
|
||||
})
|
||||
})
|
||||
.catch(function(err) {
|
||||
log.error('Failed to load device "%s": ', req.params.serial, err.stack)
|
||||
res.status(500).json({
|
||||
success: false
|
||||
, description: 'Internal Server Error'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -182,66 +297,70 @@ function addUserDevice(req, res) {
|
|||
function deleteUserDeviceBySerial(req, res) {
|
||||
var serial = req.swagger.params.serial.value
|
||||
|
||||
dbapi.loadDevice(serial)
|
||||
.then(function(device) {
|
||||
if (!device) {
|
||||
return res.status(404).json({
|
||||
success: false
|
||||
, description: 'Device not found'
|
||||
})
|
||||
}
|
||||
|
||||
datautil.normalize(device, req.user)
|
||||
if (!deviceutil.isOwnedByUser(device, req.user)) {
|
||||
return res.status(403).json({
|
||||
success: false
|
||||
, description: 'You cannot release this device. Not owned by you'
|
||||
})
|
||||
}
|
||||
|
||||
// Timer will be called if no JoinGroupMessage is received till 5 seconds
|
||||
var responseTimer = setTimeout(function() {
|
||||
req.options.channelRouter.removeListener(wireutil.global, messageListener)
|
||||
return res.status(504).json({
|
||||
dbapi.loadDevice(req.user.groups.subscribed, serial)
|
||||
.then(function(cursor) {
|
||||
cursor.next(function(err, device) {
|
||||
if (err) {
|
||||
return res.status(404).json({
|
||||
success: false
|
||||
, description: 'Device is not responding'
|
||||
})
|
||||
}, 5000)
|
||||
, description: 'Device not found'
|
||||
})
|
||||
}
|
||||
|
||||
var messageListener = wirerouter()
|
||||
.on(wire.LeaveGroupMessage, function(channel, message) {
|
||||
if (message.serial === serial && message.owner.email === req.user.email) {
|
||||
clearTimeout(responseTimer)
|
||||
req.options.channelRouter.removeListener(wireutil.global, messageListener)
|
||||
datautil.normalize(device, req.user)
|
||||
if (!deviceutil.isOwnedByUser(device, req.user)) {
|
||||
return res.status(403).json({
|
||||
success: false
|
||||
, description: 'You cannot release this device. Not owned by you'
|
||||
})
|
||||
}
|
||||
|
||||
return res.json({
|
||||
success: true
|
||||
, description: 'Device successfully removed'
|
||||
})
|
||||
}
|
||||
})
|
||||
.handler()
|
||||
// Timer will be called if no JoinGroupMessage is received till 5 seconds
|
||||
var responseTimer = setTimeout(function() {
|
||||
req.options.channelRouter.removeListener(wireutil.global, messageListener)
|
||||
return res.status(504).json({
|
||||
success: false
|
||||
, description: 'Device is not responding'
|
||||
})
|
||||
}, 5000)
|
||||
|
||||
req.options.channelRouter.on(wireutil.global, messageListener)
|
||||
var messageListener = wirerouter()
|
||||
.on(wire.LeaveGroupMessage, function(channel, message) {
|
||||
if (message.serial === serial &&
|
||||
(message.owner.email === req.user.email || req.user.privilege === 'admin')) {
|
||||
clearTimeout(responseTimer)
|
||||
req.options.channelRouter.removeListener(wireutil.global, messageListener)
|
||||
|
||||
req.options.push.send([
|
||||
device.channel
|
||||
, wireutil.envelope(
|
||||
new wire.UngroupMessage(
|
||||
wireutil.toDeviceRequirements({
|
||||
serial: {
|
||||
value: serial
|
||||
, match: 'exact'
|
||||
}
|
||||
})
|
||||
return res.json({
|
||||
success: true
|
||||
, description: 'Device successfully removed'
|
||||
})
|
||||
}
|
||||
})
|
||||
.handler()
|
||||
|
||||
req.options.channelRouter.on(wireutil.global, messageListener)
|
||||
|
||||
req.options.push.send([
|
||||
device.channel
|
||||
, wireutil.envelope(
|
||||
new wire.UngroupMessage(
|
||||
wireutil.toDeviceRequirements({
|
||||
serial: {
|
||||
value: serial
|
||||
, match: 'exact'
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
])
|
||||
])
|
||||
})
|
||||
})
|
||||
.catch(function(err) {
|
||||
log.error('Failed to load device "%s": ', req.params.serial, err.stack)
|
||||
res.status(500).json({
|
||||
success: false
|
||||
, description: 'Internal Server Error'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -249,65 +368,68 @@ function deleteUserDeviceBySerial(req, res) {
|
|||
function remoteConnectUserDeviceBySerial(req, res) {
|
||||
var serial = req.swagger.params.serial.value
|
||||
|
||||
dbapi.loadDevice(serial)
|
||||
.then(function(device) {
|
||||
if (!device) {
|
||||
return res.status(404).json({
|
||||
success: false
|
||||
, description: 'Device not found'
|
||||
})
|
||||
}
|
||||
|
||||
datautil.normalize(device, req.user)
|
||||
if (!deviceutil.isOwnedByUser(device, req.user)) {
|
||||
return res.status(403).json({
|
||||
success: false
|
||||
, description: 'Device is not owned by you or is not available'
|
||||
})
|
||||
}
|
||||
|
||||
var responseChannel = 'txn_' + uuid.v4()
|
||||
req.options.sub.subscribe(responseChannel)
|
||||
|
||||
// Timer will be called if no JoinGroupMessage is received till 5 seconds
|
||||
var timer = setTimeout(function() {
|
||||
req.options.channelRouter.removeListener(responseChannel, messageListener)
|
||||
req.options.sub.unsubscribe(responseChannel)
|
||||
return res.status(504).json({
|
||||
dbapi.loadDevice(req.user.groups.subscribed, serial)
|
||||
.then(function(cursor) {
|
||||
cursor.next(function(err, device) {
|
||||
if (err) {
|
||||
return res.status(404).json({
|
||||
success: false
|
||||
, description: 'Device is not responding'
|
||||
})
|
||||
}, 5000)
|
||||
, description: 'Device not found'
|
||||
})
|
||||
}
|
||||
|
||||
var messageListener = wirerouter()
|
||||
.on(wire.ConnectStartedMessage, function(channel, message) {
|
||||
if (message.serial === serial) {
|
||||
clearTimeout(timer)
|
||||
req.options.sub.unsubscribe(responseChannel)
|
||||
req.options.channelRouter.removeListener(responseChannel, messageListener)
|
||||
datautil.normalize(device, req.user)
|
||||
if (!deviceutil.isOwnedByUser(device, req.user)) {
|
||||
return res.status(403).json({
|
||||
success: false
|
||||
, description: 'Device is not owned by you or is not available'
|
||||
})
|
||||
}
|
||||
|
||||
return res.json({
|
||||
success: true
|
||||
, remoteConnectUrl: message.url
|
||||
})
|
||||
}
|
||||
})
|
||||
.handler()
|
||||
var responseChannel = 'txn_' + uuid.v4()
|
||||
req.options.sub.subscribe(responseChannel)
|
||||
|
||||
req.options.channelRouter.on(responseChannel, messageListener)
|
||||
// Timer will be called if no JoinGroupMessage is received till 5 seconds
|
||||
var timer = setTimeout(function() {
|
||||
req.options.channelRouter.removeListener(responseChannel, messageListener)
|
||||
req.options.sub.unsubscribe(responseChannel)
|
||||
return res.status(504).json({
|
||||
success: false
|
||||
, description: 'Device is not responding'
|
||||
})
|
||||
}, 5000)
|
||||
|
||||
req.options.push.send([
|
||||
device.channel
|
||||
, wireutil.transaction(
|
||||
responseChannel
|
||||
, new wire.ConnectStartMessage()
|
||||
)
|
||||
])
|
||||
var messageListener = wirerouter()
|
||||
.on(wire.ConnectStartedMessage, function(channel, message) {
|
||||
if (message.serial === serial) {
|
||||
clearTimeout(timer)
|
||||
req.options.sub.unsubscribe(responseChannel)
|
||||
req.options.channelRouter.removeListener(responseChannel, messageListener)
|
||||
return res.json({
|
||||
success: true
|
||||
, description: 'Remote connection is enabled'
|
||||
, remoteConnectUrl: message.url
|
||||
})
|
||||
}
|
||||
})
|
||||
.handler()
|
||||
|
||||
req.options.channelRouter.on(responseChannel, messageListener)
|
||||
|
||||
req.options.push.send([
|
||||
device.channel
|
||||
, wireutil.transaction(
|
||||
responseChannel
|
||||
, new wire.ConnectStartMessage()
|
||||
)
|
||||
])
|
||||
})
|
||||
})
|
||||
.catch(function(err) {
|
||||
log.error('Failed to load device "%s": ', req.params.serial, err.stack)
|
||||
res.status(500).json({
|
||||
success: false
|
||||
, description: 'Internal Server Error'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -315,65 +437,67 @@ function remoteConnectUserDeviceBySerial(req, res) {
|
|||
function remoteDisconnectUserDeviceBySerial(req, res) {
|
||||
var serial = req.swagger.params.serial.value
|
||||
|
||||
dbapi.loadDevice(serial)
|
||||
.then(function(device) {
|
||||
if (!device) {
|
||||
return res.status(404).json({
|
||||
success: false
|
||||
, description: 'Device not found'
|
||||
})
|
||||
}
|
||||
dbapi.loadDevice(req.user.groups.subscribed, serial)
|
||||
.then(function(cursor) {
|
||||
cursor.next(function(err, device) {
|
||||
if (err) {
|
||||
return res.status(404).json({
|
||||
success: false
|
||||
, description: 'Device not found'
|
||||
})
|
||||
}
|
||||
|
||||
datautil.normalize(device, req.user)
|
||||
if (!deviceutil.isOwnedByUser(device, req.user)) {
|
||||
return res.status(403).json({
|
||||
success: false
|
||||
, description: 'Device is not owned by you or is not available'
|
||||
})
|
||||
}
|
||||
datautil.normalize(device, req.user)
|
||||
if (!deviceutil.isOwnedByUser(device, req.user)) {
|
||||
return res.status(403).json({
|
||||
success: false
|
||||
, description: 'Device is not owned by you or is not available'
|
||||
})
|
||||
}
|
||||
|
||||
var responseChannel = 'txn_' + uuid.v4()
|
||||
req.options.sub.subscribe(responseChannel)
|
||||
var responseChannel = 'txn_' + uuid.v4()
|
||||
req.options.sub.subscribe(responseChannel)
|
||||
|
||||
// Timer will be called if no JoinGroupMessage is received till 5 seconds
|
||||
var timer = setTimeout(function() {
|
||||
req.options.channelRouter.removeListener(responseChannel, messageListener)
|
||||
req.options.sub.unsubscribe(responseChannel)
|
||||
return res.status(504).json({
|
||||
// Timer will be called if no JoinGroupMessage is received till 5 seconds
|
||||
var timer = setTimeout(function() {
|
||||
req.options.channelRouter.removeListener(responseChannel, messageListener)
|
||||
req.options.sub.unsubscribe(responseChannel)
|
||||
return res.status(504).json({
|
||||
success: false
|
||||
, description: 'Device is not responding'
|
||||
})
|
||||
}, 5000)
|
||||
})
|
||||
}, 5000)
|
||||
|
||||
var messageListener = wirerouter()
|
||||
.on(wire.ConnectStoppedMessage, function(channel, message) {
|
||||
if (message.serial === serial) {
|
||||
clearTimeout(timer)
|
||||
req.options.sub.unsubscribe(responseChannel)
|
||||
req.options.channelRouter.removeListener(responseChannel, messageListener)
|
||||
var messageListener = wirerouter()
|
||||
.on(wire.ConnectStoppedMessage, function(channel, message) {
|
||||
if (message.serial === serial) {
|
||||
clearTimeout(timer)
|
||||
req.options.sub.unsubscribe(responseChannel)
|
||||
req.options.channelRouter.removeListener(responseChannel, messageListener)
|
||||
return res.json({
|
||||
success: true
|
||||
, description: 'Device remote disconnected successfully'
|
||||
})
|
||||
}
|
||||
})
|
||||
.handler()
|
||||
|
||||
return res.json({
|
||||
success: true
|
||||
, description: 'Device remote disconnected successfully'
|
||||
})
|
||||
}
|
||||
})
|
||||
.handler()
|
||||
req.options.channelRouter.on(responseChannel, messageListener)
|
||||
|
||||
req.options.channelRouter.on(responseChannel, messageListener)
|
||||
|
||||
req.options.push.send([
|
||||
device.channel
|
||||
, wireutil.transaction(
|
||||
responseChannel
|
||||
, new wire.ConnectStopMessage()
|
||||
)
|
||||
])
|
||||
req.options.push.send([
|
||||
device.channel
|
||||
, wireutil.transaction(
|
||||
responseChannel
|
||||
, new wire.ConnectStopMessage()
|
||||
)
|
||||
])
|
||||
})
|
||||
})
|
||||
.catch(function(err) {
|
||||
log.error('Failed to load device "%s": ', req.params.serial, err.stack)
|
||||
res.status(500).json({
|
||||
success: false
|
||||
, description: 'Internal Server Error'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -400,3 +524,20 @@ function getUserAccessTokens(req, res) {
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getUser: getUser
|
||||
, getUserDevices: getUserDevices
|
||||
, addUserDevice: addUserDevice
|
||||
, addUserDeviceV2: addUserDevice
|
||||
, getUserDeviceBySerial: getUserDeviceBySerial
|
||||
, deleteUserDeviceBySerial: deleteUserDeviceBySerial
|
||||
, remoteConnectUserDeviceBySerial: remoteConnectUserDeviceBySerial
|
||||
, remoteDisconnectUserDeviceBySerial: remoteDisconnectUserDeviceBySerial
|
||||
, getUserAccessTokens: getUserAccessTokens
|
||||
, getAccessTokens: getAccessTokens
|
||||
, getAccessToken: getAccessToken
|
||||
, createAccessToken: createAccessToken
|
||||
, deleteAccessToken: deleteAccessToken
|
||||
, deleteAccessTokens: deleteAccessTokens
|
||||
}
|
||||
|
|
398
lib/units/api/controllers/users.js
Normal file
398
lib/units/api/controllers/users.js
Normal file
|
@ -0,0 +1,398 @@
|
|||
/**
|
||||
* Copyright © 2019 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
|
||||
**/
|
||||
|
||||
const dbapi = require('../../../db/api')
|
||||
const _ = require('lodash')
|
||||
const apiutil = require('../../../util/apiutil')
|
||||
const lockutil = require('../../../util/lockutil')
|
||||
const Promise = require('bluebird')
|
||||
const wire = require('../../../wire')
|
||||
const wireutil = require('../../../wire/util')
|
||||
const userapi = require('./user')
|
||||
|
||||
/* --------------------------------- PRIVATE FUNCTIONS --------------------------------------- */
|
||||
|
||||
function userApiWrapper(fn, req, res) {
|
||||
const email = req.swagger.params.email.value
|
||||
|
||||
dbapi.loadUser(email).then(function(user) {
|
||||
if (!user) {
|
||||
apiutil.respond(res, 404, 'Not Found (user)')
|
||||
}
|
||||
else {
|
||||
req.user = user
|
||||
fn(req, res)
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to wrap "%s": ', fn.name, err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function getPublishedUser(user, userEmail, adminEmail, fields) {
|
||||
let publishedUser = apiutil.publishUser(user)
|
||||
if (userEmail !== adminEmail) {
|
||||
publishedUser = _.pick(user, 'email', 'name', 'privilege')
|
||||
}
|
||||
if (fields) {
|
||||
publishedUser = _.pick(publishedUser, fields.split(','))
|
||||
}
|
||||
return publishedUser
|
||||
}
|
||||
|
||||
function removeUser(email, req, res) {
|
||||
const groupOwnerState = req.swagger.params.groupOwner.value
|
||||
const anyGroupOwnerState = typeof groupOwnerState === 'undefined'
|
||||
const lock = {}
|
||||
|
||||
function removeGroupUser(owner, id) {
|
||||
const lock = {}
|
||||
|
||||
return dbapi.lockGroupByOwner(owner, id).then(function(stats) {
|
||||
if (!stats.replaced) {
|
||||
return apiutil.lightComputeStats(res, stats)
|
||||
}
|
||||
lock.group = stats.changes[0].new_val
|
||||
|
||||
return owner === email ?
|
||||
dbapi.deleteUserGroup(id) :
|
||||
dbapi.removeGroupUser(id, email)
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockGroup(lock)
|
||||
})
|
||||
}
|
||||
|
||||
function deleteUserInDatabase(channel) {
|
||||
return dbapi.removeUserAccessTokens(email).then(function() {
|
||||
return dbapi.deleteUser(email).then(function() {
|
||||
req.options.pushdev.send([
|
||||
channel
|
||||
, wireutil.envelope(new wire.DeleteUserMessage(
|
||||
email
|
||||
))
|
||||
])
|
||||
return 'deleted'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function computeUserGroupOwnership(groups) {
|
||||
if (anyGroupOwnerState) {
|
||||
return Promise.resolve(true)
|
||||
}
|
||||
return Promise.map(groups, function(group) {
|
||||
if (!groupOwnerState && group.owner.email === email) {
|
||||
return Promise.reject('filtered')
|
||||
}
|
||||
return !groupOwnerState || group.owner.email === email
|
||||
})
|
||||
.then(function(results) {
|
||||
return _.without(results, false).length > 0
|
||||
})
|
||||
.catch(function(err) {
|
||||
if (err === 'filtered') {
|
||||
return false
|
||||
}
|
||||
throw err
|
||||
})
|
||||
}
|
||||
|
||||
if (req.user.email === email) {
|
||||
return Promise.resolve('forbidden')
|
||||
}
|
||||
return dbapi.lockUser(email).then(function(stats) {
|
||||
if (!stats.replaced) {
|
||||
return apiutil.lightComputeStats(res, stats)
|
||||
}
|
||||
const user = lock.user = stats.changes[0].new_val
|
||||
|
||||
return dbapi.getGroupsByUser(user.email).then(function(groups) {
|
||||
return computeUserGroupOwnership(groups).then(function(doContinue) {
|
||||
if (!doContinue) {
|
||||
return 'unchanged'
|
||||
}
|
||||
return Promise.each(groups, function(group) {
|
||||
return removeGroupUser(group.owner.email, group.id)
|
||||
})
|
||||
.then(function() {
|
||||
return deleteUserInDatabase(user.group)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockUser(lock)
|
||||
})
|
||||
}
|
||||
|
||||
/* --------------------------------- PUBLIC FUNCTIONS --------------------------------------- */
|
||||
|
||||
function getUserInfo(req, email) {
|
||||
const fields = req.swagger.params.fields.value
|
||||
|
||||
return dbapi.loadUser(email).then(function(user) {
|
||||
if (user) {
|
||||
return dbapi.getRootGroup().then(function(group) {
|
||||
return getPublishedUser(user, req.user.email, group.owner.email, fields)
|
||||
})
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
function updateUserGroupsQuotas(req, res) {
|
||||
const email = req.swagger.params.email.value
|
||||
const duration =
|
||||
typeof req.swagger.params.duration.value !== 'undefined' ?
|
||||
req.swagger.params.duration.value :
|
||||
null
|
||||
const number =
|
||||
typeof req.swagger.params.number.value !== 'undefined' ?
|
||||
req.swagger.params.number.value :
|
||||
null
|
||||
const repetitions =
|
||||
typeof req.swagger.params.repetitions.value !== 'undefined' ?
|
||||
req.swagger.params.repetitions.value :
|
||||
null
|
||||
const lock = {}
|
||||
|
||||
lockutil.lockUser(email, res, lock).then(function(lockingSuccessed) {
|
||||
if (lockingSuccessed) {
|
||||
return dbapi.updateUserGroupsQuotas(email, duration, number, repetitions)
|
||||
.then(function(stats) {
|
||||
if (stats.replaced) {
|
||||
return apiutil.respond(res, 200, 'Updated (user quotas)', {
|
||||
user: apiutil.publishUser(stats.changes[0].new_val)
|
||||
})
|
||||
}
|
||||
if ((duration === null || duration === lock.user.groups.quotas.allocated.duration) &&
|
||||
(number === null || number === lock.user.groups.quotas.allocated.number) &&
|
||||
(repetitions === null || repetitions === lock.user.groups.quotas.repetitions)
|
||||
) {
|
||||
return apiutil.respond(res, 200, 'Unchanged (user quotas)', {user: {}})
|
||||
}
|
||||
return apiutil.respond(
|
||||
res
|
||||
, 400
|
||||
, 'Bad Request (quotas must be >= actual consumed resources)')
|
||||
})
|
||||
}
|
||||
return false
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to update user groups quotas: ', err.stack)
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockUser(lock)
|
||||
})
|
||||
}
|
||||
|
||||
function updateDefaultUserGroupsQuotas(req, res) {
|
||||
const duration =
|
||||
typeof req.swagger.params.duration.value !== 'undefined' ?
|
||||
req.swagger.params.duration.value :
|
||||
null
|
||||
const number =
|
||||
typeof req.swagger.params.number.value !== 'undefined' ?
|
||||
req.swagger.params.number.value :
|
||||
null
|
||||
const repetitions =
|
||||
typeof req.swagger.params.repetitions.value !== 'undefined' ?
|
||||
req.swagger.params.repetitions.value :
|
||||
null
|
||||
const lock = {}
|
||||
|
||||
lockutil.lockUser(req.user.email, res, lock).then(function(lockingSuccessed) {
|
||||
if (lockingSuccessed) {
|
||||
return dbapi.updateDefaultUserGroupsQuotas(req.user.email, duration, number, repetitions)
|
||||
.then(function(stats) {
|
||||
if (stats.replaced) {
|
||||
return apiutil.respond(res, 200, 'Updated (user default quotas)', {
|
||||
user: apiutil.publishUser(stats.changes[0].new_val)
|
||||
})
|
||||
}
|
||||
return apiutil.respond(res, 200, 'Unchanged (user default quotas)', {user: {}})
|
||||
})
|
||||
}
|
||||
return false
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to update default user groups quotas: ', err.stack)
|
||||
})
|
||||
.finally(function() {
|
||||
lockutil.unlockUser(lock)
|
||||
})
|
||||
}
|
||||
|
||||
function getUserByEmail(req, res) {
|
||||
const email = req.swagger.params.email.value
|
||||
|
||||
getUserInfo(req, email).then(function(user) {
|
||||
if (user) {
|
||||
apiutil.respond(res, 200, 'User Information', {user: user})
|
||||
}
|
||||
else {
|
||||
apiutil.respond(res, 404, 'Not Found (user)')
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to get user: ', err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function getUsers(req, res) {
|
||||
const fields = req.swagger.params.fields.value
|
||||
|
||||
dbapi.getUsers().then(function(users) {
|
||||
return dbapi.getRootGroup().then(function(group) {
|
||||
apiutil.respond(res, 200, 'Users Information', {
|
||||
users: users.map(function(user) {
|
||||
return getPublishedUser(user, req.user.email, group.owner.email, fields)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to get users: ', err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function createUser(req, res) {
|
||||
const email = req.swagger.params.email.value
|
||||
const name = req.swagger.params.name.value
|
||||
|
||||
dbapi.createUser(email, name, req.user.ip).then(function(stats) {
|
||||
if (!stats.inserted) {
|
||||
apiutil.respond(res, 403, 'Forbidden (user already exists)')
|
||||
}
|
||||
else {
|
||||
apiutil.respond(res, 201, 'Created (user)', {
|
||||
user: apiutil.publishUser(stats.changes[0].new_val)
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to create user: ', err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function deleteUsers(req, res) {
|
||||
const emails = apiutil.getBodyParameter(req.body, 'emails')
|
||||
const target = apiutil.getQueryParameter(req.swagger.params.redirected) ? 'user' : 'users'
|
||||
|
||||
function removeUsers(emails) {
|
||||
let results = []
|
||||
|
||||
return Promise.each(emails, function(email) {
|
||||
return removeUser(email, req, res).then(function(result) {
|
||||
results.push(result)
|
||||
})
|
||||
})
|
||||
.then(function() {
|
||||
results = _.without(results, 'unchanged')
|
||||
if (!results.length) {
|
||||
return apiutil.respond(res, 200, `Unchanged (${target})`)
|
||||
}
|
||||
results = _.without(results, 'not found')
|
||||
if (!results.length) {
|
||||
return apiutil.respond(res, 404, `Not Found (${target})`)
|
||||
}
|
||||
results = _.without(results, 'forbidden')
|
||||
if (!results.length) {
|
||||
apiutil.respond(res, 403, `Forbidden (${target})`)
|
||||
}
|
||||
return apiutil.respond(res, 200, `Deleted (${target})`)
|
||||
})
|
||||
.catch(function(err) {
|
||||
if (err !== 'busy') {
|
||||
throw err
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
(function() {
|
||||
if (typeof emails === 'undefined') {
|
||||
return dbapi.getEmails().then(function(emails) {
|
||||
return removeUsers(emails)
|
||||
})
|
||||
}
|
||||
else {
|
||||
return removeUsers(_.without(emails.split(','), ''))
|
||||
}
|
||||
})()
|
||||
.catch(function(err) {
|
||||
apiutil.internalError(res, 'Failed to delete ${target}: ', err.stack)
|
||||
})
|
||||
}
|
||||
|
||||
function deleteUser(req, res) {
|
||||
apiutil.redirectApiWrapper('email', deleteUsers, req, res)
|
||||
}
|
||||
|
||||
function createUserAccessToken(req, res) {
|
||||
userApiWrapper(userapi.createAccessToken, req, res)
|
||||
}
|
||||
|
||||
function deleteUserAccessToken(req, res) {
|
||||
userApiWrapper(userapi.deleteAccessToken, req, res)
|
||||
}
|
||||
|
||||
function deleteUserAccessTokens(req, res) {
|
||||
userApiWrapper(userapi.deleteAccessTokens, req, res)
|
||||
}
|
||||
|
||||
function getUserAccessToken(req, res) {
|
||||
userApiWrapper(userapi.getAccessToken, req, res)
|
||||
}
|
||||
|
||||
function getUserAccessTokens(req, res) {
|
||||
userApiWrapper(userapi.getAccessTokens, req, res)
|
||||
}
|
||||
|
||||
function getUserDevices(req, res) {
|
||||
userApiWrapper(userapi.getUserDevices, req, res)
|
||||
}
|
||||
|
||||
function getUserDevice(req, res) {
|
||||
userApiWrapper(userapi.getUserDeviceBySerial, req, res)
|
||||
}
|
||||
|
||||
function addUserDevice(req, res) {
|
||||
userApiWrapper(userapi.addUserDevice, req, res)
|
||||
}
|
||||
|
||||
function deleteUserDevice(req, res) {
|
||||
userApiWrapper(userapi.deleteUserDeviceBySerial, req, res)
|
||||
}
|
||||
|
||||
function remoteConnectUserDevice(req, res) {
|
||||
userApiWrapper(userapi.remoteConnectUserDeviceBySerial, req, res)
|
||||
}
|
||||
|
||||
function remoteDisconnectUserDevice(req, res) {
|
||||
userApiWrapper(userapi.remoteDisconnectUserDeviceBySerial, req, res)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
updateUserGroupsQuotas: updateUserGroupsQuotas
|
||||
, updateDefaultUserGroupsQuotas: updateDefaultUserGroupsQuotas
|
||||
, getUsers: getUsers
|
||||
, getUserByEmail: getUserByEmail
|
||||
, getUserInfo: getUserInfo
|
||||
, createUser: createUser
|
||||
, deleteUser: deleteUser
|
||||
, deleteUsers: deleteUsers
|
||||
, createUserAccessToken: createUserAccessToken
|
||||
, deleteUserAccessToken: deleteUserAccessToken
|
||||
, deleteUserAccessTokens: deleteUserAccessTokens
|
||||
, getUserAccessTokensV2: getUserAccessTokens
|
||||
, getUserAccessToken: getUserAccessToken
|
||||
, getUserDevicesV2: getUserDevices
|
||||
, getUserDevice: getUserDevice
|
||||
, addUserDeviceV3: addUserDevice
|
||||
, deleteUserDevice: deleteUserDevice
|
||||
, remoteConnectUserDevice: remoteConnectUserDevice
|
||||
, remoteDisconnectUserDevice: remoteDisconnectUserDevice
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue