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
257
lib/util/apiutil.js
Normal file
257
lib/util/apiutil.js
Normal file
|
@ -0,0 +1,257 @@
|
|||
/**
|
||||
* Copyright © 2019 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
|
||||
**/
|
||||
|
||||
const Promise = require('bluebird')
|
||||
const _ = require('lodash')
|
||||
const logger = require('./logger')
|
||||
const datautil = require('./datautil')
|
||||
|
||||
const apiutil = Object.create(null)
|
||||
const log = logger.createLogger('api:controllers:apiutil')
|
||||
|
||||
apiutil.PENDING = 'pending'
|
||||
apiutil.READY = 'ready'
|
||||
apiutil.WAITING = 'waiting'
|
||||
|
||||
apiutil.BOOKABLE = 'bookable'
|
||||
apiutil.STANDARD = 'standard'
|
||||
apiutil.ONCE = 'once'
|
||||
apiutil.DEBUG = 'debug'
|
||||
apiutil.ORIGIN = 'origin'
|
||||
apiutil.STANDARDIZABLE = 'standardizable'
|
||||
|
||||
apiutil.ROOT = 'root'
|
||||
apiutil.ADMIN = 'admin'
|
||||
apiutil.USER = 'user'
|
||||
|
||||
apiutil.FIVE_MN = 300 * 1000
|
||||
apiutil.ONE_HOUR = 3600 * 1000
|
||||
apiutil.ONE_DAY = 24 * apiutil.ONE_HOUR
|
||||
apiutil.ONE_WEEK = 7 * apiutil.ONE_DAY
|
||||
apiutil.ONE_MONTH = 30 * apiutil.ONE_DAY
|
||||
apiutil.ONE_QUATER = 3 * apiutil.ONE_MONTH
|
||||
apiutil.ONE_HALF_YEAR = 6 * apiutil.ONE_MONTH
|
||||
apiutil.ONE_YEAR = 365 * apiutil.ONE_DAY
|
||||
|
||||
apiutil.MAX_USER_GROUPS_NUMBER = 5
|
||||
apiutil.MAX_USER_GROUPS_DURATION = 15 * apiutil.ONE_DAY
|
||||
apiutil.MAX_USER_GROUPS_REPETITIONS = 10
|
||||
|
||||
apiutil.CLASS_DURATION = {
|
||||
once: Infinity
|
||||
, bookable: Infinity
|
||||
, standard: Infinity
|
||||
, hourly: apiutil.ONE_HOUR
|
||||
, daily: apiutil.ONE_DAY
|
||||
, weekly: apiutil.ONE_WEEK
|
||||
, monthly: apiutil.ONE_MONTH
|
||||
, quaterly: apiutil.ONE_QUATER
|
||||
, halfyearly: apiutil.ONE_HALF_YEAR
|
||||
, yearly: apiutil.ONE_YEAR
|
||||
, debug: apiutil.FIVE_MN
|
||||
}
|
||||
|
||||
apiutil.isOriginGroup = function(_class) {
|
||||
return _class === apiutil.BOOKABLE || _class === apiutil.STANDARD
|
||||
}
|
||||
|
||||
apiutil.isAdminGroup = function(_class) {
|
||||
return apiutil.isOriginGroup(_class) || _class === apiutil.DEBUG
|
||||
}
|
||||
|
||||
apiutil.internalError = function(res, ...args) {
|
||||
log.error.apply(log, args)
|
||||
apiutil.respond(res, 500, 'Internal Server Error')
|
||||
}
|
||||
|
||||
apiutil.respond = function(res, code, message, data) {
|
||||
const status = code >= 200 && code < 300
|
||||
const response = {
|
||||
success: status
|
||||
, description: message
|
||||
}
|
||||
|
||||
if (data) {
|
||||
for (const key in data) {
|
||||
if (data.hasOwnProperty(key)) {
|
||||
response[key] = data[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
res.status(code).json(response)
|
||||
return status
|
||||
}
|
||||
|
||||
apiutil.publishGroup = function(group) {
|
||||
// delete group.lock
|
||||
delete group.createdAt
|
||||
delete group.ticket
|
||||
return group
|
||||
}
|
||||
|
||||
apiutil.publishDevice = function(device, user) {
|
||||
datautil.normalize(device, user)
|
||||
// delete device.group.lock
|
||||
return device
|
||||
}
|
||||
|
||||
apiutil.publishUser = function(user) {
|
||||
// delete user.groups.lock
|
||||
return user
|
||||
}
|
||||
|
||||
apiutil.publishAccessToken = function(token) {
|
||||
delete token.email
|
||||
delete token.jwt
|
||||
return token
|
||||
}
|
||||
|
||||
apiutil.filterDevice = function(req, device) {
|
||||
const fields = req.swagger.params.fields.value
|
||||
|
||||
if (fields) {
|
||||
return _.pick(apiutil.publishDevice(device, req.user), fields.split(','))
|
||||
}
|
||||
return apiutil.publishDevice(device, req.user)
|
||||
}
|
||||
|
||||
apiutil.computeDuration = function(group, deviceNumber) {
|
||||
return (group.devices.length + deviceNumber) *
|
||||
(group.dates[0].stop - group.dates[0].start) *
|
||||
(group.repetitions + 1)
|
||||
}
|
||||
|
||||
apiutil.lightComputeStats = function(res, stats) {
|
||||
if (stats.locked) {
|
||||
apiutil.respond(res, 503, 'Server too busy, please try again later')
|
||||
return Promise.reject('busy')
|
||||
}
|
||||
return 'not found'
|
||||
}
|
||||
|
||||
apiutil.computeStats = function(res, stats, objectName, ...lock) {
|
||||
if (!stats.replaced) {
|
||||
if (stats.skipped) {
|
||||
return apiutil.respond(res, 404, `Not Found (${objectName})`)
|
||||
}
|
||||
if (stats.locked) {
|
||||
return apiutil.respond(res, 503, 'Server too busy, please try again later')
|
||||
}
|
||||
return apiutil.respond(res, 403, `Forbidden (${objectName})`)
|
||||
}
|
||||
if (lock.length) {
|
||||
lock[0][objectName] = stats.changes[0].new_val
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
apiutil.lockResult = function(stats) {
|
||||
const result = {status: false, data: stats}
|
||||
|
||||
if (stats.replaced || stats.skipped) {
|
||||
result.status = true
|
||||
result.data.locked = false
|
||||
}
|
||||
else {
|
||||
result.data.locked = true
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
apiutil.lockDeviceResult = function(stats, fn, groups, serial) {
|
||||
const result = apiutil.lockResult(stats)
|
||||
if (!result.status) {
|
||||
return fn(groups, serial).then(function(devices) {
|
||||
if (!devices.length) {
|
||||
result.data.locked = false
|
||||
result.status = true
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
apiutil.setIntervalWrapper = function(fn, numTimes, delay) {
|
||||
return fn().then(function(result) {
|
||||
if (result.status) {
|
||||
return result.data
|
||||
}
|
||||
return new Promise(function(resolve, reject) {
|
||||
let counter = 0
|
||||
const interval = setInterval(function() {
|
||||
return fn().then(function(result) {
|
||||
if (result.status || ++counter === numTimes) {
|
||||
if (!result.status && counter === numTimes) {
|
||||
log.debug('%s() failed %s times in a loop!', fn.name, counter)
|
||||
}
|
||||
clearInterval(interval)
|
||||
resolve(result.data)
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
clearInterval(interval)
|
||||
reject(err)
|
||||
})
|
||||
}, delay)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
apiutil.redirectApiWrapper = function(field, fn, req, res) {
|
||||
if (typeof req.body === 'undefined') {
|
||||
req.body = {}
|
||||
}
|
||||
req.body[field + 's'] = req.swagger.params[field].value
|
||||
req.swagger.params.redirected = {value: true}
|
||||
fn(req, res)
|
||||
}
|
||||
|
||||
apiutil.computeGroupDates = function(lifeTime, _class, repetitions) {
|
||||
const dates = new Array(lifeTime)
|
||||
|
||||
for(let repetition = 1
|
||||
, currentLifeTime = {
|
||||
start: new Date(lifeTime.start.getTime())
|
||||
, stop: new Date(lifeTime.stop.getTime())
|
||||
}
|
||||
; repetition <= repetitions
|
||||
; repetition++) {
|
||||
currentLifeTime.start = new Date(
|
||||
currentLifeTime.start.getTime() +
|
||||
apiutil.CLASS_DURATION[_class]
|
||||
)
|
||||
currentLifeTime.stop = new Date(
|
||||
currentLifeTime.stop.getTime() +
|
||||
apiutil.CLASS_DURATION[_class]
|
||||
)
|
||||
dates.push({
|
||||
start: new Date(currentLifeTime.start.getTime())
|
||||
, stop: new Date(currentLifeTime.stop.getTime())
|
||||
})
|
||||
}
|
||||
return dates
|
||||
}
|
||||
|
||||
apiutil.checkBodyParameter = function(body, parameter) {
|
||||
return typeof body !== 'undefined' && typeof body[parameter] !== 'undefined'
|
||||
}
|
||||
|
||||
apiutil.getBodyParameter = function(body, parameter) {
|
||||
let undef
|
||||
|
||||
return apiutil.checkBodyParameter(body, parameter) ? body[parameter] : undef
|
||||
}
|
||||
|
||||
apiutil.checkQueryParameter = function(parameter) {
|
||||
return typeof parameter !== 'undefined' && typeof parameter.value !== 'undefined'
|
||||
}
|
||||
|
||||
apiutil.getQueryParameter = function(parameter) {
|
||||
let undef
|
||||
|
||||
return apiutil.checkQueryParameter(parameter) ? parameter.value : undef
|
||||
}
|
||||
|
||||
module.exports = apiutil
|
|
@ -1,3 +1,7 @@
|
|||
/**
|
||||
* Copyright © 2019 contains code contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
|
||||
**/
|
||||
|
||||
var deviceData = require('stf-device-db')
|
||||
var browserData = require('stf-browser-db')
|
||||
|
||||
|
@ -41,13 +45,14 @@ datautil.applyBrowsers = function(device) {
|
|||
}
|
||||
|
||||
datautil.applyOwner = function(device, user) {
|
||||
device.using = !!device.owner && device.owner.email === user.email
|
||||
device.using = !!device.owner &&
|
||||
(device.owner.email === user.email || user.privilege === 'admin')
|
||||
return device
|
||||
}
|
||||
|
||||
// Only owner can see this information
|
||||
datautil.applyOwnerOnlyInfo = function(device, user) {
|
||||
if (device.owner && device.owner.email === user.email) {
|
||||
if (device.owner && (device.owner.email === user.email || user.privilege === 'admin')) {
|
||||
// No-op
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
/**
|
||||
* Copyright © 2019 contains code contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
|
||||
**/
|
||||
|
||||
var logger = require('./logger')
|
||||
|
||||
var log = logger.createLogger('util:deviceutil')
|
||||
|
@ -8,7 +12,7 @@ deviceutil.isOwnedByUser = function(device, user) {
|
|||
return device.present &&
|
||||
device.ready &&
|
||||
device.owner &&
|
||||
device.owner.email === user.email &&
|
||||
(device.owner.email === user.email || user.privilege === 'admin') &&
|
||||
device.using
|
||||
}
|
||||
|
||||
|
|
|
@ -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 uuid = require('uuid')
|
||||
|
@ -7,10 +11,8 @@ var dbapi = require('../db/api')
|
|||
var devices = require('stf-device-db/dist/devices-latest')
|
||||
|
||||
module.exports.generate = function(wantedModel) {
|
||||
var serial = util.format(
|
||||
'fake-%s'
|
||||
, uuid.v4(null, new Buffer(16)).toString('base64')
|
||||
)
|
||||
// no base64 because some characters as '=' or '/' are not compatible through API (delete devices)
|
||||
const serial = 'fake-' + util.format('%s', uuid.v4()).replace(/-/g, '')
|
||||
|
||||
return dbapi.saveDeviceInitialState(serial, {
|
||||
provider: {
|
||||
|
@ -28,7 +30,7 @@ module.exports.generate = function(wantedModel) {
|
|||
, model: model
|
||||
, version: '4.1.2'
|
||||
, abi: 'armeabi-v7a'
|
||||
, sdk: 8 + Math.floor(Math.random() * 12)
|
||||
, sdk: (8 + Math.floor(Math.random() * 12)).toString() // string required!
|
||||
, display: {
|
||||
density: 3
|
||||
, fps: 60
|
||||
|
@ -49,6 +51,8 @@ module.exports.generate = function(wantedModel) {
|
|||
, phoneNumber: '0000000000'
|
||||
}
|
||||
, product: model
|
||||
, cpuPlatform: 'msm8996'
|
||||
, openGLESVersion: '3.1'
|
||||
})
|
||||
})
|
||||
.then(function() {
|
||||
|
|
42
lib/util/fakegroup.js
Normal file
42
lib/util/fakegroup.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* Copyright © 2019 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
|
||||
**/
|
||||
|
||||
const util = require('util')
|
||||
const uuid = require('uuid')
|
||||
const dbapi = require('../db/api')
|
||||
const apiutil = require('./apiutil')
|
||||
|
||||
module.exports.generate = function() {
|
||||
return dbapi.getRootGroup().then(function(rootGroup) {
|
||||
const now = Date.now()
|
||||
|
||||
return dbapi.createUserGroup({
|
||||
name: 'fakegroup-' + util.format('%s', uuid.v4()).replace(/-/g, '')
|
||||
, owner: {
|
||||
email: rootGroup.owner.email
|
||||
, name: rootGroup.owner.name
|
||||
}
|
||||
, privilege: apiutil.ADMIN
|
||||
, class: apiutil.BOOKABLE
|
||||
, repetitions: 0
|
||||
, isActive: true
|
||||
, dates: apiutil.computeGroupDates(
|
||||
{
|
||||
start: new Date(now)
|
||||
, stop: new Date(now + apiutil.ONE_YEAR)
|
||||
}
|
||||
, apiutil.BOOKABLE
|
||||
, 0
|
||||
)
|
||||
, duration: 0
|
||||
, state: apiutil.READY
|
||||
})
|
||||
.then(function(group) {
|
||||
if (group) {
|
||||
return group.id
|
||||
}
|
||||
throw new Error('Forbidden (groups number quota is reached)')
|
||||
})
|
||||
})
|
||||
}
|
14
lib/util/fakeuser.js
Normal file
14
lib/util/fakeuser.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* Copyright © 2019 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
|
||||
**/
|
||||
|
||||
const util = require('util')
|
||||
const uuid = require('uuid')
|
||||
const dbapi = require('../db/api')
|
||||
|
||||
module.exports.generate = function() {
|
||||
const name = 'fakeuser-' + util.format('%s', uuid.v4()).replace(/-/g, '')
|
||||
const email = name + '@openstf.com'
|
||||
|
||||
return dbapi.createUser(email, name, '127.0.0.1').return(email)
|
||||
}
|
69
lib/util/lockutil.js
Normal file
69
lib/util/lockutil.js
Normal file
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* Copyright © 2019 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
|
||||
**/
|
||||
|
||||
const apiutil = require('./apiutil')
|
||||
const dbapi = require('../db/api')
|
||||
|
||||
const lockutil = Object.create(null)
|
||||
|
||||
lockutil.unlockDevice = function(lock) {
|
||||
if (lock.device) {
|
||||
dbapi.unlockDevice(lock.device.serial)
|
||||
}
|
||||
}
|
||||
|
||||
lockutil.lockUser = function(email, res, lock) {
|
||||
return dbapi.lockUser(email)
|
||||
.then(function(stats) {
|
||||
return apiutil.computeStats(res, stats, 'user', lock)
|
||||
})
|
||||
}
|
||||
|
||||
lockutil.unlockUser = function(lock) {
|
||||
if (lock.user) {
|
||||
dbapi.unlockUser(lock.user.email)
|
||||
}
|
||||
}
|
||||
|
||||
lockutil.lockGroupAndUser = function(req, res, lock) {
|
||||
return lockutil.lockGroup(req, res, lock).then(function(lockingSuccessed) {
|
||||
return lockingSuccessed ?
|
||||
lockutil.lockUser(req.user.email, res, lock) :
|
||||
false
|
||||
})
|
||||
}
|
||||
|
||||
lockutil.unlockGroupAndUser = function(lock) {
|
||||
lockutil.unlockGroup(lock)
|
||||
lockutil.unlockUser(lock)
|
||||
}
|
||||
|
||||
lockutil.lockGroup = function(req, res, lock) {
|
||||
const id = req.swagger.params.id.value
|
||||
const email = req.user.email
|
||||
|
||||
return dbapi.lockGroupByOwner(email, id).then(function(stats) {
|
||||
return apiutil.computeStats(res, stats, 'group', lock)
|
||||
})
|
||||
}
|
||||
|
||||
lockutil.unlockGroup = function(lock) {
|
||||
if (lock.group) {
|
||||
dbapi.unlockGroup(lock.group.id)
|
||||
}
|
||||
}
|
||||
|
||||
lockutil.unlockGroupAndDevice = function(lock) {
|
||||
lockutil.unlockGroup(lock)
|
||||
lockutil.unlockDevice(lock)
|
||||
}
|
||||
|
||||
lockutil.lockGenericDevice = function(req, res, lock, lockDevice) {
|
||||
return lockDevice(req.user.groups.subscribed, req.swagger.params.serial.value)
|
||||
.then(function(stats) {
|
||||
return apiutil.computeStats(res, stats, 'device', lock)
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = lockutil
|
22
lib/util/timeutil.js
Normal file
22
lib/util/timeutil.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* Copyright © 2019 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
|
||||
**/
|
||||
|
||||
const timeutil = Object.create(null)
|
||||
|
||||
timeutil.now = function(unit) {
|
||||
const hrTime = process.hrtime()
|
||||
|
||||
switch (unit) {
|
||||
case 'milli':
|
||||
return hrTime[0] * 1000 + hrTime[1] / 1000000
|
||||
case 'micro':
|
||||
return hrTime[0] * 1000000 + hrTime[1] / 1000
|
||||
case 'nano':
|
||||
return hrTime[0] * 1000000000 + hrTime[1]
|
||||
default:
|
||||
return hrTime[0] * 1000000000 + hrTime[1]
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = timeutil
|
Loading…
Add table
Add a link
Reference in a new issue