mirror of
https://github.com/openstf/stf
synced 2025-10-04 10:19:30 +02:00
931 lines
28 KiB
JavaScript
931 lines
28 KiB
JavaScript
/**
|
|
* 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
|
|
}
|