--- /dev/null
+/* Copyright (c) 2019 AT&T Intellectual Property. #\r
+# #\r
+# Licensed under the Apache License, Version 2.0 (the "License"); #\r
+# you may not use this file except in compliance with the License. #\r
+# You may obtain a copy of the License at #\r
+# #\r
+# http://www.apache.org/licenses/LICENSE-2.0 #\r
+# #\r
+# Unless required by applicable law or agreed to in writing, software #\r
+# distributed under the License is distributed on an "AS IS" BASIS, #\r
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #\r
+# See the License for the specific language governing permissions and #\r
+# limitations under the License. #\r
+##############################################################################*/\r
+\r
+\r
+const logger = require('../../lib/logger');\r
+const agenda = require('../agenda').agenda;\r
+const emitter = require('../result-emitter').emitter;\r
+const utils = require('../../lib/otf-util');\r
+const nodeUtil = require('util');\r
+\r
+const ObjectId = require('mongoose').Types.ObjectId;\r
+\r
+const TestSchedule = require('../models/test-schedule');\r
+\r
+const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;\r
+\r
+module.exports = function (app) {\r
+ let scheduleTestResponse = { status: '', message: '' };\r
+ let cancelTestResponse = { status: '', message: '' };\r
+\r
+ // Main endpoint for scheduling\r
+ app.use('/' + app.get('base-path') + 'schedule-test', (req, res, next) => {\r
+ const authorizationHeader = req.headers.authorization;\r
+\r
+ const testInstanceId = req.body.testInstanceId;\r
+ const testInstanceStartDate = req.body.testInstanceStartDate;\r
+ const testInstanceExecFreqInSeconds = req.body.testInstanceExecFreqInSeconds;\r
+ const testInstanceEndDate = req.body.testInstanceEndDate;\r
+ const async = req.body.async;\r
+ const asyncTopic = req.body.asyncTopic;\r
+ const asyncMode = req.body.asyncMode;\r
+ const executorId = req.body.executorId;\r
+\r
+ let testSchedule = null;\r
+\r
+ try {\r
+ testSchedule = new TestSchedule(testInstanceId, testInstanceStartDate, testInstanceExecFreqInSeconds,\r
+ testInstanceEndDate, async, asyncTopic, asyncMode, executorId);\r
+ } catch (error) {\r
+ scheduleTestResponse.status = 400;\r
+ scheduleTestResponse.message = error.toString();\r
+ next();\r
+\r
+ return;\r
+ }\r
+\r
+ // The presence of this parameter indicates that we will be executing either job definition 2/3.\r
+ if (testSchedule.testInstanceStartDate) {\r
+ if (testSchedule.testInstanceExecFreqInSeconds) {\r
+ const job = agenda.create(\r
+ 'executeTestAsync',\r
+ { testSchedule, authorizationHeader });\r
+ job.schedule(testSchedule.testInstanceStartDate).repeatEvery(testSchedule.testInstanceExecFreqInSeconds + ' seconds', {\r
+ timezone: timeZone\r
+ });\r
+ job.save().then(function onJobCreated (result) {\r
+ logger.debug(JSON.stringify(result));\r
+ scheduleTestResponse.status = 200;\r
+ scheduleTestResponse.message = 'Successfully scheduled job.';\r
+ next();\r
+ })\r
+ .catch(function onError (error) {\r
+ logger.error(error);\r
+ scheduleTestResponse.status = 500;\r
+ scheduleTestResponse.message = 'Unable to schedule job.';\r
+ next();\r
+ });\r
+ } else if (!testSchedule.testInstanceExecFreqInSeconds && !testSchedule.testInstanceEndDate) {\r
+ agenda.schedule(\r
+ testSchedule._testInstanceStartDate,\r
+ 'executeTestAsync',\r
+ { testSchedule, authorizationHeader })\r
+ .then(function onJobCreated (result) {\r
+ scheduleTestResponse.status = 200;\r
+ scheduleTestResponse.message = 'Successfully scheduled job.';\r
+ next();\r
+ })\r
+ .catch(function onError (error) {\r
+ logger.error('error: ' + error);\r
+ scheduleTestResponse.status = 500;\r
+ scheduleTestResponse.message = 'Unable to schedule job.';\r
+ next();\r
+ });\r
+ return;\r
+ } else if (testSchedule.testInstanceEndDate && !testSchedule.testInstanceExecFreqInSeconds) {\r
+ scheduleTestResponse.status = 400;\r
+ scheduleTestResponse.message = 'Must specify \'testInstanceExecFreqInSeconds\' to use \'testInstanceEndDate\'';\r
+\r
+ next();\r
+ }\r
+ }\r
+\r
+ if (!testSchedule.testInstanceStartDate &&\r
+ !testSchedule.testInstanceExecFreqInSeconds &&\r
+ !testSchedule.testInstanceExecFreqInSeconds) {\r
+ agenda.now(\r
+ 'executeTestSync',\r
+ { testSchedule, authorizationHeader })\r
+ .then(function onJobCreated (result) {\r
+ emitter.once(result.attrs._id + '_error', (res) => {\r
+ logger.info(res);\r
+ scheduleTestResponse.message = res.message;\r
+ scheduleTestResponse.status = res.statusCode;\r
+ next();\r
+ });\r
+\r
+ emitter.once(result.attrs._id + '_ok', (res) => {\r
+ logger.info(res);\r
+ scheduleTestResponse.message = res;\r
+ scheduleTestResponse.status = 200;\r
+ next();\r
+ });\r
+ })\r
+ .catch(function onError (err) {\r
+ logger.error(err);\r
+\r
+ if (!Object.keys(scheduleTestResponse).includes('message')) {\r
+ scheduleTestResponse.message = 'Unknown error.';\r
+ }\r
+\r
+ if (!Object.keys(scheduleTestResponse).includes('status')) {\r
+ scheduleTestResponse.status = 500;\r
+ }\r
+ });\r
+ }\r
+ }, (req, res) => {\r
+ res.type('json');\r
+ res.status(scheduleTestResponse.status).send(scheduleTestResponse);\r
+ logger.debug('Sent response with status %d and body %s', scheduleTestResponse.status, scheduleTestResponse.message);\r
+ });\r
+\r
+ // Cancel\r
+ app.use('/' + app.get('base-path') + 'cancel-test', (req, res, next) => {\r
+ // validate the request parameters\r
+ if (req.body === null) {\r
+ cancelTestResponse.status = 400;\r
+ cancelTestResponse.message = 'Request data is invalid.';\r
+\r
+ next();\r
+ return;\r
+ }\r
+\r
+ let requestBody = req.body;\r
+\r
+ if (!requestBody.jobId) {\r
+ cancelTestResponse.status = 400;\r
+ cancelTestResponse.message = 'jobId is required.';\r
+\r
+ next();\r
+ return;\r
+ }\r
+\r
+ if (!utils.isValidObjectId(requestBody.jobId)) {\r
+ cancelTestResponse.status = 400;\r
+ cancelTestResponse.message = 'jobId must be a valid ObjectId.';\r
+\r
+ next();\r
+ return;\r
+ }\r
+\r
+ if (!requestBody.executorId) {\r
+ cancelTestResponse.status = 400;\r
+ cancelTestResponse.message = 'executorId is required.';\r
+\r
+ next();\r
+ return;\r
+ }\r
+\r
+ if (!utils.isValidObjectId(requestBody.executorId)) {\r
+ cancelTestResponse.status = 400;\r
+ cancelTestResponse.message = 'executorId must be a valid ObjectId.';\r
+\r
+ next();\r
+ return;\r
+ }\r
+\r
+ const jobId = new ObjectId(requestBody.jobId);\r
+ const executorId = new ObjectId(requestBody.executorId);\r
+\r
+ agenda.cancel({ _id: jobId, 'data.testSchedule._executorId': executorId })\r
+ .then(function onJobRemoved (numJobsRemoved) {\r
+ logger.info('Number of jobs removed: %s', numJobsRemoved);\r
+\r
+ cancelTestResponse.status = 200;\r
+ cancelTestResponse.message = nodeUtil.format('Successfully removed job with Id %s', jobId);\r
+\r
+ if (numJobsRemoved === 0) {\r
+ cancelTestResponse.status = 500;\r
+ cancelTestResponse.message =\r
+ nodeUtil.format('Unable to find job with Id %s, belonging to user with Id %s.', jobId, executorId);\r
+ }\r
+\r
+ next();\r
+ })\r
+ .catch(function onError (error) {\r
+ logger.error(error.toString());\r
+\r
+ cancelTestResponse.status = 500;\r
+ cancelTestResponse.message = 'Unable to cancel the job due to an unexpected error.';\r
+\r
+ next();\r
+ });\r
+ }, (req, res) => {\r
+ res.type('json');\r
+ res.status(cancelTestResponse.status).send(cancelTestResponse);\r
+ logger.debug('Sent response with status %d and body %s', cancelTestResponse.status, cancelTestResponse.message);\r
+ });\r
+};\r