1
0
Fork 0
mirror of https://github.com/openstf/stf synced 2025-10-05 10:39:25 +02:00

add groups feature

This commit is contained in:
Denis barbaron 2019-06-12 10:29:07 +02:00
parent 6fd750dad5
commit 7f5dc4c152
119 changed files with 12416 additions and 402 deletions

257
lib/util/apiutil.js Normal file
View 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

View file

@ -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 {

View file

@ -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
}

View file

@ -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
View 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
View 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
View 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
View 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