From 06e35c0322889eaea705b9d07784504f00144314 Mon Sep 17 00:00:00 2001 From: decaffeinate Date: Wed, 13 Sep 2017 17:52:05 +0200 Subject: [PATCH 1/9] style: Rename example.coffee and 8 other files from .coffee to .js --- example.coffee => example.js | 0 src/{config.coffee => config.js} | 0 src/{index.coffee => index.js} | 0 src/{notify.coffee => notify.js} | 0 src/{pagerduty-api.coffee => pagerduty-api.js} | 0 src/{pagerduty.coffee => pagerduty.js} | 0 test/{config-test.coffee => config-test.js} | 0 test/{notify-test.coffee => notify-test.js} | 0 test/{pagerduty-test.coffee => pagerduty-test.js} | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename example.coffee => example.js (100%) rename src/{config.coffee => config.js} (100%) rename src/{index.coffee => index.js} (100%) rename src/{notify.coffee => notify.js} (100%) rename src/{pagerduty-api.coffee => pagerduty-api.js} (100%) rename src/{pagerduty.coffee => pagerduty.js} (100%) rename test/{config-test.coffee => config-test.js} (100%) rename test/{notify-test.coffee => notify-test.js} (100%) rename test/{pagerduty-test.coffee => pagerduty-test.js} (100%) diff --git a/example.coffee b/example.js similarity index 100% rename from example.coffee rename to example.js diff --git a/src/config.coffee b/src/config.js similarity index 100% rename from src/config.coffee rename to src/config.js diff --git a/src/index.coffee b/src/index.js similarity index 100% rename from src/index.coffee rename to src/index.js diff --git a/src/notify.coffee b/src/notify.js similarity index 100% rename from src/notify.coffee rename to src/notify.js diff --git a/src/pagerduty-api.coffee b/src/pagerduty-api.js similarity index 100% rename from src/pagerduty-api.coffee rename to src/pagerduty-api.js diff --git a/src/pagerduty.coffee b/src/pagerduty.js similarity index 100% rename from src/pagerduty.coffee rename to src/pagerduty.js diff --git a/test/config-test.coffee b/test/config-test.js similarity index 100% rename from test/config-test.coffee rename to test/config-test.js diff --git a/test/notify-test.coffee b/test/notify-test.js similarity index 100% rename from test/notify-test.coffee rename to test/notify-test.js diff --git a/test/pagerduty-test.coffee b/test/pagerduty-test.js similarity index 100% rename from test/pagerduty-test.coffee rename to test/pagerduty-test.js From 12370f71cd4a29aa01d015ee0c05ba3e4f9b5564 Mon Sep 17 00:00:00 2001 From: decaffeinate Date: Wed, 13 Sep 2017 17:52:11 +0200 Subject: [PATCH 2/9] style: Convert example.coffee and 8 other files to JS --- example.js | 42 +++-- src/config.js | 31 ++-- src/index.js | 8 +- src/notify.js | 286 ++++++++++++++++++------------- src/pagerduty-api.js | 59 ++++--- src/pagerduty.js | 381 +++++++++++++++++++++++------------------ test/config-test.js | 37 ++-- test/notify-test.js | 85 +++++---- test/pagerduty-test.js | 319 +++++++++++++++++++--------------- 9 files changed, 715 insertions(+), 533 deletions(-) diff --git a/example.js b/example.js index b5541c6..a83c374 100644 --- a/example.js +++ b/example.js @@ -1,18 +1,28 @@ -config = require './src/config' -nconf = require 'nconf' -pd = require './src/pagerduty' +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const config = require('./src/config'); +const nconf = require('nconf'); +const pd = require('./src/pagerduty'); -configPath = __dirname + '/config.json' +const configPath = __dirname + '/config.json'; -config.setupConfig configPath, (err) -> - if err then console.error err - pd.checkSchedulesIds (err, res) -> - if err then console.error err - unless res - console.error "Check failed" - else - pd.processSchedulesFromConfig (err, msg) -> - if err - console.error(err) - else - console.log(msg) +config.setupConfig(configPath, function(err) { + if (err) { console.error(err); } + return pd.checkSchedulesIds(function(err, res) { + if (err) { console.error(err); } + if (!res) { + return console.error("Check failed"); + } else { + return pd.processSchedulesFromConfig(function(err, msg) { + if (err) { + return console.error(err); + } else { + return console.log(msg); + } + }); + } + }); +}); diff --git a/src/config.js b/src/config.js index 6b00682..766ed85 100644 --- a/src/config.js +++ b/src/config.js @@ -1,18 +1,25 @@ -fs = require 'fs' -nconf = require 'nconf' +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const fs = require('fs'); +const nconf = require('nconf'); -setupConfig = (configPath, cb) -> - if fs.existsSync(configPath) - console.log 'Loading config from :', configPath - # Priority order argv before ENV and file as defaults +const setupConfig = function(configPath, cb) { + if (fs.existsSync(configPath)) { + console.log('Loading config from :', configPath); + // Priority order argv before ENV and file as defaults nconf.argv() .env() - .file({ file: configPath }) - process.env.DEBUG = nconf.get('DEBUG') - cb() - else - cb new Error "Config does not exist: #{configPath}" + .file({ file: configPath }); + process.env.DEBUG = nconf.get('DEBUG'); + return cb(); + } else { + return cb(new Error(`Config does not exist: ${configPath}`)); + } +}; module.exports = { setupConfig -} +}; diff --git a/src/index.js b/src/index.js index b47706a..c05e6b9 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,5 @@ module.exports = { - checker: require './pagerduty' - config: require './config' - notify: require './notify' -} + checker: require('./pagerduty'), + config: require('./config'), + notify: require('./notify') +}; diff --git a/src/notify.js b/src/notify.js index 2c4ee2d..212a9f4 100644 --- a/src/notify.js +++ b/src/notify.js @@ -1,138 +1,186 @@ -Slack = require 'node-slackr' -nconf = require 'nconf' -async = require 'async' -request = require 'request' -debug = require('debug')('pagerduty-overrides:notifications') -pdApi = require './pagerduty-api' +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS103: Rewrite code to no longer use __guard__ + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const Slack = require('node-slackr'); +const nconf = require('nconf'); +const async = require('async'); +const request = require('request'); +const debug = require('debug')('pagerduty-overrides:notifications'); +const pdApi = require('./pagerduty-api'); -createPagerDutyIncident = (options, message, cb) -> - debug("Creating PD incident #{JSON.stringify(message)} with options #{JSON.stringify(options)}") +const createPagerDutyIncident = function(options, message, cb) { + debug(`Creating PD incident ${JSON.stringify(message)} with options ${JSON.stringify(options)}`); - unless options.pdToken and options.serviceId and options.from - cb new Error "Missing PAGERDUTY settings (you'll need PAGERDUTY_TOKEN, PAGERDUTY_SERVICE_ID and PAGERDUTY_FROM)" + if (!options.pdToken || !options.serviceId || !options.from) { + cb(new Error("Missing PAGERDUTY settings (you'll need PAGERDUTY_TOKEN, PAGERDUTY_SERVICE_ID and PAGERDUTY_FROM)")); + } - unless message.userId or options.escalationPolicyId - cb new Error "No userId or escalation policy specified" + if (!message.userId && !options.escalationPolicyId) { + return cb(new Error("No userId or escalation policy specified")); - else - incident = - type: "incident" - title: "On-call overlap found!" - service: - id: options.serviceId + } else { + const incident = { + type: "incident", + title: "On-call overlap found!", + service: { + id: options.serviceId, type: "service_reference" - body: - type: "incident_body" + }, + body: { + type: "incident_body", details: message.messages.join('\n') + } + }; - if options.escalationPolicyId - incident.escalationPolicy = - id: options.escalationPolicyId + if (options.escalationPolicyId) { + incident.escalationPolicy = { + id: options.escalationPolicyId, type: "escalation_policy_reference" - else - incident.assignments = [ - assignee : - id: message.userId + }; + } else { + incident.assignments = [{ + assignee : { + id: message.userId, type: "user_reference" - ] + } + } + ]; + } - incidentOptions = - method: "POST" - json: - incident: incident - headers: - From: options.from - Authorization: 'Token token=' + options.pdToken + const incidentOptions = { + method: "POST", + json: { + incident + }, + headers: { + From: options.from, + Authorization: `Token token=${options.pdToken}` + } + }; - pdApi.send '/incidents', incidentOptions, (err, res, body) -> - if body?.errors?.length > 0 - err ?= new Error "INCIDENT_CREATION_FAILED Errors: #{JSON.stringify body.errors}" - if res?.statusCode isnt 200 and res?.statusCode isnt 201 - err ?= new Error "INCIDENT_CREATION_FAILED Creating incident failed with status #{res.statusCode}. Returned body: #{JSON.stringify body}" - if err - debug("INCIDENT_CREATION_FAILED: ", err) - cb err + return pdApi.send('/incidents', incidentOptions, function(err, res, body) { + if (__guard__(body != null ? body.errors : undefined, x => x.length) > 0) { + if (err == null) { err = new Error(`INCIDENT_CREATION_FAILED Errors: ${JSON.stringify(body.errors)}`); } + } + if (((res != null ? res.statusCode : undefined) !== 200) && ((res != null ? res.statusCode : undefined) !== 201)) { + if (err == null) { err = new Error(`INCIDENT_CREATION_FAILED Creating incident failed with status ${res.statusCode}. Returned body: ${JSON.stringify(body)}`); } + } + if (err) { + debug("INCIDENT_CREATION_FAILED: ", err); + } + return cb(err); + }); + } +}; -# https://www.npmjs.com/package/node-slackr -createSlackMessage = (options, message, cb) -> - if options.webhookUrl - slack = new Slack(options.webhookUrl) - slack.notify message, (err, result) -> - if err - console.error("SLACK_SEND_MESSAGE_FAILED:", err) - return cb err - cb null, result - else - cb new Error "Missing Slack webhook URL." +// https://www.npmjs.com/package/node-slackr +const createSlackMessage = function(options, message, cb) { + if (options.webhookUrl) { + const slack = new Slack(options.webhookUrl); + return slack.notify(message, function(err, result) { + if (err) { + console.error("SLACK_SEND_MESSAGE_FAILED:", err); + return cb(err); + } + return cb(null, result); + }); + } else { + return cb(new Error("Missing Slack webhook URL.")); + } +}; -# Input is array of messages is we have more overlaps -formatMessage = (messages, option = 'plain') -> - if typeof messages is 'string' - return messages - else - switch option - when 'plain' - outputMessage = "_Following overlaps found:_\n" - for message in messages - outputMessage += """#{message.user}: #{message.schedules[0]} and #{message.schedules[1]} (the first starting on #{message.date.toUTCString()}, the second on #{message.crossDate.toUTCString()})\n""" - when 'markdown' - outputMessage = "Following overlaps found:\n" - for message in messages - outputMessage += """*#{message.user}:* `#{message.schedules[0]}` and `#{message.schedules[1]}` (the first starting on #{message.date.toUTCString()}, the second on #{message.crossDate.toUTCString()})\n""" - when 'json' - outputMessage = messages.reduce((acc, curr)-> - acc[curr.userId] ?= {} - acc[curr.userId].userId ?= curr.userId - acc[curr.userId].user ?= curr.user - acc[curr.userId].messages ?= [] - acc[curr.userId].messages.push("#{curr.schedules[0]} and #{curr.schedules[1]} (the first starting on #{curr.date.toUTCString()}, the second on #{curr.crossDate.toUTCString()})") - return acc - , {}) +// Input is array of messages is we have more overlaps +const formatMessage = function(messages, option) { + let outputMessage; + if (option == null) { option = 'plain'; } + if (typeof messages === 'string') { + return messages; + } else { + switch (option) { + case 'plain': + outputMessage = "_Following overlaps found:_\n"; + for (var message of Array.from(messages)) { + outputMessage += `${message.user}: ${message.schedules[0]} and ${message.schedules[1]} (the first starting on ${message.date.toUTCString()}, the second on ${message.crossDate.toUTCString()})\n`; + } + break; + case 'markdown': + outputMessage = "Following overlaps found:\n"; + for (message of Array.from(messages)) { + outputMessage += `*${message.user}:* \`${message.schedules[0]}\` and \`${message.schedules[1]}\` (the first starting on ${message.date.toUTCString()}, the second on ${message.crossDate.toUTCString()})\n`; + } + break; + case 'json': + outputMessage = messages.reduce(function(acc, curr){ + if (acc[curr.userId] == null) { acc[curr.userId] = {}; } + if (acc[curr.userId].userId == null) { acc[curr.userId].userId = curr.userId; } + if (acc[curr.userId].user == null) { acc[curr.userId].user = curr.user; } + if (acc[curr.userId].messages == null) { acc[curr.userId].messages = []; } + acc[curr.userId].messages.push(`${curr.schedules[0]} and ${curr.schedules[1]} (the first starting on ${curr.date.toUTCString()}, the second on ${curr.crossDate.toUTCString()})`); + return acc; + } + , {}); + break; + } + } - debug('Notification - formatMessage option: ', option) - debug('Notification - formatMessage: ', outputMessage) - return outputMessage + debug('Notification - formatMessage option: ', option); + debug('Notification - formatMessage: ', outputMessage); + return outputMessage; +}; -send = (options, message, cb) -> - debug('send:', options, message) +const send = function(options, message, cb) { + debug('send:', options, message); - async.parallel [ - (next) -> - if options['SLACK'] or options['SLACK_WEBHOOK_URL']? - debug('Found Slack webhook, sending a notification') - slackMessage = {} - slackMessage.text = formatMessage(message, 'markdown') - slackMessage.channel = options['SLACK']?['CHANNEL'] - slackOptions = {} - slackOptions.webhookUrl = options['SLACK']?['SLACK_WEBHOOK_URL'] or options['SLACK_WEBHOOK_URL'] - createSlackMessage slackOptions, slackMessage, next - else - debug('No Slack webhook defined') - next() - (next) -> - if not options['PAGERDUTY'] and not options['PAGERDUTY_TOKEN'] - debug('No PAGERDUTY options defined') - else if (options['PAGERDUTY']['PAGERDUTY_TOKEN'] or options['PAGERDUTY_TOKEN']) and options['PAGERDUTY']['PAGERDUTY_SERVICE_ID'] and options['PAGERDUTY']['PAGERDUTY_FROM'] - debug('Found PD token - creating an incident') - pdOptions = {} - pdOptions.pdToken = options['PAGERDUTY']['PAGERDUTY_TOKEN'] or options['PAGERDUTY_TOKEN'] - pdOptions.serviceId = options['PAGERDUTY']['PAGERDUTY_SERVICE_ID'] - pdOptions.escalationPolicyId = options['PAGERDUTY']['PAGERDUTY_ESCALATION_POLICY_ID'] - pdOptions.from = options['PAGERDUTY']?['PAGERDUTY_FROM'] - messagesByUser = formatMessage(message, 'json') - async.each(messagesByUser, - (item, cb) -> - createPagerDutyIncident pdOptions, item, cb - (err) -> - next err) - else - console.log("No PD options defined or defined incorrectly (#{JSON.stringify(options['PAGERDUTY'])})") - next() - ], (err, results) -> - if err then return cb err - output = results.filter (n) -> n isnt undefined - cb null, output + return async.parallel([ + function(next) { + if (options['SLACK'] || (options['SLACK_WEBHOOK_URL'] != null)) { + debug('Found Slack webhook, sending a notification'); + const slackMessage = {}; + slackMessage.text = formatMessage(message, 'markdown'); + slackMessage.channel = options['SLACK'] != null ? options['SLACK']['CHANNEL'] : undefined; + const slackOptions = {}; + slackOptions.webhookUrl = (options['SLACK'] != null ? options['SLACK']['SLACK_WEBHOOK_URL'] : undefined) || options['SLACK_WEBHOOK_URL']; + return createSlackMessage(slackOptions, slackMessage, next); + } else { + debug('No Slack webhook defined'); + return next(); + } + }, + function(next) { + if (!options['PAGERDUTY'] && !options['PAGERDUTY_TOKEN']) { + return debug('No PAGERDUTY options defined'); + } else if ((options['PAGERDUTY']['PAGERDUTY_TOKEN'] || options['PAGERDUTY_TOKEN']) && options['PAGERDUTY']['PAGERDUTY_SERVICE_ID'] && options['PAGERDUTY']['PAGERDUTY_FROM']) { + debug('Found PD token - creating an incident'); + const pdOptions = {}; + pdOptions.pdToken = options['PAGERDUTY']['PAGERDUTY_TOKEN'] || options['PAGERDUTY_TOKEN']; + pdOptions.serviceId = options['PAGERDUTY']['PAGERDUTY_SERVICE_ID']; + pdOptions.escalationPolicyId = options['PAGERDUTY']['PAGERDUTY_ESCALATION_POLICY_ID']; + pdOptions.from = options['PAGERDUTY'] != null ? options['PAGERDUTY']['PAGERDUTY_FROM'] : undefined; + const messagesByUser = formatMessage(message, 'json'); + return async.each(messagesByUser, + (item, cb) => createPagerDutyIncident(pdOptions, item, cb), + err => next(err)); + } else { + console.log(`No PD options defined or defined incorrectly (${JSON.stringify(options['PAGERDUTY'])})`); + return next(); + } + } + ], function(err, results) { + if (err) { return cb(err); } + const output = results.filter(n => n !== undefined); + return cb(null, output); + }); +}; module.exports = { send -} +}; + +function __guard__(value, transform) { + return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; +} \ No newline at end of file diff --git a/src/pagerduty-api.js b/src/pagerduty-api.js index b59468e..8563f04 100644 --- a/src/pagerduty-api.js +++ b/src/pagerduty-api.js @@ -1,35 +1,44 @@ -_ = require 'underscore' -debug = require('debug')('pagerduty-overrides') -request = require 'request' -nconf = require 'nconf' -url = require 'url' +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const _ = require('underscore'); +const debug = require('debug')('pagerduty-overrides'); +const request = require('request'); +const nconf = require('nconf'); +const url = require('url'); -# Factory for sending request to PD API -send = (endpointPath, overrideOptions, cb) -> - debug("Calling #{endpointPath} with options:", overrideOptions) - defaultOptions = - uri: url.resolve 'https://api.pagerduty.com', endpointPath - method: 'GET' +// Factory for sending request to PD API +const send = function(endpointPath, overrideOptions, cb) { + debug(`Calling ${endpointPath} with options:`, overrideOptions); + const defaultOptions = { + uri: url.resolve('https://api.pagerduty.com', endpointPath), + method: 'GET', json: true + }; - if typeof overrideOptions is 'function' - cb = overrideOptions - overrideOptions = {} + if (typeof overrideOptions === 'function') { + cb = overrideOptions; + overrideOptions = {}; + } - _.extend defaultOptions, overrideOptions + _.extend(defaultOptions, overrideOptions); - defaultOptions.headers ?= [] - defaultOptions.headers.Authorization ?= 'Token token=' + nconf.get('PAGERDUTY_READ_ONLY_TOKEN') - defaultOptions.headers.Accept = 'application/vnd.pagerduty+json;version=2' - defaultOptions.headers['Content-Type'] = 'application/json' + if (defaultOptions.headers == null) { defaultOptions.headers = []; } + if (defaultOptions.headers.Authorization == null) { defaultOptions.headers.Authorization = `Token token=${nconf.get('PAGERDUTY_READ_ONLY_TOKEN')}`; } + defaultOptions.headers.Accept = 'application/vnd.pagerduty+json;version=2'; + defaultOptions.headers['Content-Type'] = 'application/json'; - defaultOptions.qs ?= [] - defaultOptions.qs.limit = 100 - defaultOptions.qs.timezone = 'UTC' + if (defaultOptions.qs == null) { defaultOptions.qs = []; } + defaultOptions.qs.limit = 100; + defaultOptions.qs.timezone = 'UTC'; - debug('Calling request with: ', defaultOptions) - request defaultOptions, cb + debug('Calling request with: ', defaultOptions); + return request(defaultOptions, cb); +}; module.exports = { send -} +}; diff --git a/src/pagerduty.js b/src/pagerduty.js index 97300e2..2b94780 100644 --- a/src/pagerduty.js +++ b/src/pagerduty.js @@ -1,172 +1,217 @@ -async = require 'async' -nconf = require 'nconf' -_ = require 'underscore' -debug = require('debug')('pagerduty-overrides') -notify = require './notify' -pdApi = require './pagerduty-api' - -# Get schedule for ID and 2 weeks -getSchedule = (id, cb) -> - if nconf.get('WEEKS_TO_CHECK') > 0 - week = 7 * 86400 * 1000 - timeNow = new Date() - timeUntil = new Date(timeNow.getTime() + nconf.get('WEEKS_TO_CHECK') * week) - - scheduleOpts = - qs: - 'schedule_ids[]': id - until: timeUntil.toISOString() +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS104: Avoid inline assignments + * DS204: Change includes calls to have a more natural evaluation order + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const async = require('async'); +const nconf = require('nconf'); +const _ = require('underscore'); +const debug = require('debug')('pagerduty-overrides'); +const notify = require('./notify'); +const pdApi = require('./pagerduty-api'); + +// Get schedule for ID and 2 weeks +const getSchedule = function(id, cb) { + if (nconf.get('WEEKS_TO_CHECK') > 0) { + const week = 7 * 86400 * 1000; + const timeNow = new Date(); + const timeUntil = new Date(timeNow.getTime() + (nconf.get('WEEKS_TO_CHECK') * week)); + + const scheduleOpts = { + qs: { + 'schedule_ids[]': id, + until: timeUntil.toISOString(), since: timeNow.toISOString() - - - pdApi.send "/oncalls", scheduleOpts, (err, res, body) -> - if err - console.log "Request send error:", err - return cb err - - if res.statusCode isnt 200 then return cb new Error("Entries returned status code #{res.statusCode}") - cb err, id: id, entries: body.oncalls - else - cb new Error "Missing WEEKS_TO_CHECK settings" - -# Get all schedules and returns their ids -getSchedulesIds = (cb) -> - debug("Getting schedules from PD") - pdApi.send "/schedules", {}, (err, res, body) -> - if err - console.log "Request send error:", err - return cb err - - debug('Returned status code:', res.statusCode) - schedulesIds = [] - - for schedule in body.schedules - schedulesIds.push(schedule.id) - # UWAGA UWAGA - side effect follows! - # it's easier, cheaper and more comprehensive to load schedule names here and temporarily store them using nconf - nconf.set("schedulesNames:#{schedule.id}", schedule.name) - - debug("Schedules Ids from PD: ", schedulesIds) - debug("Schedules Names from PD: ", nconf.get("schedulesNames")) - cb null, schedulesIds - -# Check if all schedules defined in config are available in PD -checkSchedulesIds = (cb) -> - configSchedules = nconf.get('SCHEDULES') - listIds = [] - for ids in configSchedules - listIds.push ids['SCHEDULE'] - configSchedulesIds = _.uniq(_.flatten(listIds)) - debug("Schedules Ids from config: ", configSchedulesIds) - getSchedulesIds (err, schedulesIds) -> - if err then return cb err - debug('intersection: ', _.intersection(configSchedulesIds, schedulesIds).length) - debug('config: ', configSchedulesIds.length) - if (_.intersection(configSchedulesIds, schedulesIds).length) is configSchedulesIds.length - cb null, true - else - cb null, false - -processSchedulesFromConfig = (done) -> - messages = [] - configSchedules = nconf.get('SCHEDULES') - debug('configSchedules:', configSchedules.length) - async.forEach configSchedules, (processedConfig, cb) -> - debug('Process schedule:', ) - async.mapSeries processedConfig['SCHEDULE'], (i, next) -> - getSchedule i, next - , (err, results) -> - if err then return cb err - if results - processSchedules results, processedConfig['EXCLUSION_DAYS'], (err, message) -> - debug('processSchedules:', processedConfig) - if message isnt "OK" - messages = messages.concat(message) - if processedConfig['NOTIFICATIONS'] - debug('Sending notifications.') - return sendNotification processedConfig['NOTIFICATIONS'], message, cb - return cb() - else - return cb new Error "No schedule to process." - , (err) -> - if err then return done err - done null, messages - -sendNotification = (options, message, cb) -> - debug("NOTIFICATIONS:", message) - debug("NOTIFICATIONS-OPTIONS:", options) - notify.send options, message, (err) -> - cb err - -processSchedules = (allSchedules, days = [], cb) -> - if typeof days is 'function' - [cb, days] = [days, []] - messages = [] - duplicities = {} - debug('allSchedules:', allSchedules) - for schedule in allSchedules - debug('schedule:', JSON.stringify(schedule)) - otherSchedules = _.without(allSchedules, schedule) - debug('otherSchedules:',JSON.stringify(otherSchedules)) - for entry in schedule.entries - debug('checking entry: ', JSON.stringify(entry)) - myStart = entry.start - myEnd = entry.end - myUserId = entry.user.id - myUserName = entry.user.summary - duplicities.myUserName ?= [] - for crossSchedule in otherSchedules - for crossCheckEntry in crossSchedule.entries - overlap = false - startDate = new Date(myStart) - day = getDayAbbrev(startDate.getUTCDay()) - - scheduleId = nconf.get("schedulesNames:#{schedule.id}") - crossScheduleId = nconf.get("schedulesNames:#{crossSchedule.id}") - - message = {user: myUserName, userId: myUserId, schedules: [scheduleId, crossScheduleId], date: startDate, crossDate: new Date(crossCheckEntry.start)} - - if myStart <= crossCheckEntry.start < myEnd and - crossCheckEntry.user.id == myUserId - overlap = true - - if day in Object.keys(days) - - if days[day]?.start? and days[day]?.end? - exclusionStartTime = days[day].start.split(':') - exclusionEndTime = days[day].end.split(':') - exclusionStartDate = new Date(myStart) - exclusionStartDate.setUTCHours(exclusionStartTime[0]) - exclusionStartDate.setUTCMinutes(exclusionStartTime[1]) - exclusionEndDate = new Date(myStart) - exclusionEndDate.setUTCHours(exclusionEndTime[0]) - exclusionEndDate.setUTCMinutes(exclusionEndTime[1]) - - - if exclusionStartDate <= startDate < exclusionEndDate - debug('excluded:', message) - overlap = false - else - overlap = false - - if overlap and crossCheckEntry.start not in duplicities.myUserName - duplicities.myUserName.push(crossCheckEntry.start) - messages.push message - debug(_.uniq(messages)) - if messages.length is 0 - cb null, "OK" - else - cb null, _.uniq(messages) - -getDayAbbrev = (utcDay) -> - days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] - - return days[utcDay] + } + }; + + + return pdApi.send("/oncalls", scheduleOpts, function(err, res, body) { + if (err) { + console.log("Request send error:", err); + return cb(err); + } + + if (res.statusCode !== 200) { return cb(new Error(`Entries returned status code ${res.statusCode}`)); } + return cb(err, {id, entries: body.oncalls}); + }); + } else { + return cb(new Error("Missing WEEKS_TO_CHECK settings")); + } +}; + +// Get all schedules and returns their ids +const getSchedulesIds = function(cb) { + debug("Getting schedules from PD"); + return pdApi.send("/schedules", {}, function(err, res, body) { + if (err) { + console.log("Request send error:", err); + return cb(err); + } + + debug('Returned status code:', res.statusCode); + const schedulesIds = []; + + for (let schedule of Array.from(body.schedules)) { + schedulesIds.push(schedule.id); + // UWAGA UWAGA - side effect follows! + // it's easier, cheaper and more comprehensive to load schedule names here and temporarily store them using nconf + nconf.set(`schedulesNames:${schedule.id}`, schedule.name); + } + + debug("Schedules Ids from PD: ", schedulesIds); + debug("Schedules Names from PD: ", nconf.get("schedulesNames")); + return cb(null, schedulesIds); + }); +}; + +// Check if all schedules defined in config are available in PD +const checkSchedulesIds = function(cb) { + const configSchedules = nconf.get('SCHEDULES'); + const listIds = []; + for (let ids of Array.from(configSchedules)) { + listIds.push(ids['SCHEDULE']); + } + const configSchedulesIds = _.uniq(_.flatten(listIds)); + debug("Schedules Ids from config: ", configSchedulesIds); + return getSchedulesIds(function(err, schedulesIds) { + if (err) { return cb(err); } + debug('intersection: ', _.intersection(configSchedulesIds, schedulesIds).length); + debug('config: ', configSchedulesIds.length); + if ((_.intersection(configSchedulesIds, schedulesIds).length) === configSchedulesIds.length) { + return cb(null, true); + } else { + return cb(null, false); + } + }); +}; + +const processSchedulesFromConfig = function(done) { + let messages = []; + const configSchedules = nconf.get('SCHEDULES'); + debug('configSchedules:', configSchedules.length); + return async.forEach(configSchedules, function(processedConfig, cb) { + debug('Process schedule:' ); + return async.mapSeries(processedConfig['SCHEDULE'], (i, next) => getSchedule(i, next) + , function(err, results) { + if (err) { return cb(err); } + if (results) { + return processSchedules(results, processedConfig['EXCLUSION_DAYS'], function(err, message) { + debug('processSchedules:', processedConfig); + if (message !== "OK") { + messages = messages.concat(message); + if (processedConfig['NOTIFICATIONS']) { + debug('Sending notifications.'); + return sendNotification(processedConfig['NOTIFICATIONS'], message, cb); + } + } + return cb(); + }); + } else { + return cb(new Error("No schedule to process.")); + } + }); + } + , function(err) { + if (err) { return done(err); } + return done(null, messages); + }); +}; + +var sendNotification = function(options, message, cb) { + debug("NOTIFICATIONS:", message); + debug("NOTIFICATIONS-OPTIONS:", options); + return notify.send(options, message, err => cb(err)); +}; + +var processSchedules = function(allSchedules, days, cb) { + if (days == null) { days = []; } + if (typeof days === 'function') { + [cb, days] = Array.from([days, []]); + } + const messages = []; + const duplicities = {}; + debug('allSchedules:', allSchedules); + for (let schedule of Array.from(allSchedules)) { + debug('schedule:', JSON.stringify(schedule)); + const otherSchedules = _.without(allSchedules, schedule); + debug('otherSchedules:',JSON.stringify(otherSchedules)); + for (let entry of Array.from(schedule.entries)) { + debug('checking entry: ', JSON.stringify(entry)); + const myStart = entry.start; + const myEnd = entry.end; + const myUserId = entry.user.id; + const myUserName = entry.user.summary; + if (duplicities.myUserName == null) { duplicities.myUserName = []; } + for (let crossSchedule of Array.from(otherSchedules)) { + for (let crossCheckEntry of Array.from(crossSchedule.entries)) { + let overlap = false; + const startDate = new Date(myStart); + const day = getDayAbbrev(startDate.getUTCDay()); + + const scheduleId = nconf.get(`schedulesNames:${schedule.id}`); + const crossScheduleId = nconf.get(`schedulesNames:${crossSchedule.id}`); + + const message = {user: myUserName, userId: myUserId, schedules: [scheduleId, crossScheduleId], date: startDate, crossDate: new Date(crossCheckEntry.start)}; + + if ((myStart <= crossCheckEntry.start && crossCheckEntry.start < myEnd) && + (crossCheckEntry.user.id === myUserId)) { + var needle; + overlap = true; + + if ((needle = day, Array.from(Object.keys(days)).includes(needle))) { + + if (((days[day] != null ? days[day].start : undefined) != null) && ((days[day] != null ? days[day].end : undefined) != null)) { + const exclusionStartTime = days[day].start.split(':'); + const exclusionEndTime = days[day].end.split(':'); + const exclusionStartDate = new Date(myStart); + exclusionStartDate.setUTCHours(exclusionStartTime[0]); + exclusionStartDate.setUTCMinutes(exclusionStartTime[1]); + const exclusionEndDate = new Date(myStart); + exclusionEndDate.setUTCHours(exclusionEndTime[0]); + exclusionEndDate.setUTCMinutes(exclusionEndTime[1]); + + + if (exclusionStartDate <= startDate && startDate < exclusionEndDate) { + debug('excluded:', message); + overlap = false; + } + } else { + overlap = false; + } + } + } + + if (overlap && !Array.from(duplicities.myUserName).includes(crossCheckEntry.start)) { + duplicities.myUserName.push(crossCheckEntry.start); + messages.push(message); + } + } + } + } + } + debug(_.uniq(messages)); + if (messages.length === 0) { + return cb(null, "OK"); + } else { + return cb(null, _.uniq(messages)); + } +}; + +var getDayAbbrev = function(utcDay) { + const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + + return days[utcDay]; +}; module.exports = { - getSchedulesIds - checkSchedulesIds - processSchedules - processSchedulesFromConfig + getSchedulesIds, + checkSchedulesIds, + processSchedules, + processSchedulesFromConfig, sendNotification -} +}; diff --git a/test/config-test.js b/test/config-test.js index ab889b0..6473853 100644 --- a/test/config-test.js +++ b/test/config-test.js @@ -1,24 +1,27 @@ -assert = require('chai').assert -config = require '../src/config' -nconf = require 'nconf' -debug = require('debug')('pagerduty-overrides:tests') +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const { assert } = require('chai'); +const config = require('../src/config'); +const nconf = require('nconf'); +const debug = require('debug')('pagerduty-overrides:tests'); -describe 'Get config for PagerDuty Overrides', -> +describe('Get config for PagerDuty Overrides', function() { - it 'NODE_ENV isn\'t set', -> - assert.equal nconf.get('NODE_ENV'), undefined + it('NODE_ENV isn\'t set', () => assert.equal(nconf.get('NODE_ENV'), undefined)); - it 'PAGERDUTY_READ_ONLY_TOKEN isn\'t set', -> - assert.equal nconf.get('PAGERDUTY_READ_ONLY_TOKEN'), undefined + return it('PAGERDUTY_READ_ONLY_TOKEN isn\'t set', () => assert.equal(nconf.get('PAGERDUTY_READ_ONLY_TOKEN'), undefined)); +}); -describe 'Setup config and get config', -> +describe('Setup config and get config', function() { - before (done) -> - config.setupConfig __dirname + '/fixtures/config.json', (err) -> - done err + before(done => + config.setupConfig(__dirname + '/fixtures/config.json', err => done(err)) + ); - it 'NODE_ENV isn\'t set', -> - assert.equal nconf.get('NODE_ENV'), undefined + it('NODE_ENV isn\'t set', () => assert.equal(nconf.get('NODE_ENV'), undefined)); - it 'PAGERDUTY_READ_ONLY_TOKEN is set', -> - assert.equal nconf.get('PAGERDUTY_READ_ONLY_TOKEN'), 'E7px6VVr3PVHZPJq51oa' + return it('PAGERDUTY_READ_ONLY_TOKEN is set', () => assert.equal(nconf.get('PAGERDUTY_READ_ONLY_TOKEN'), 'E7px6VVr3PVHZPJq51oa')); +}); diff --git a/test/notify-test.js b/test/notify-test.js index abf4df4..f488745 100644 --- a/test/notify-test.js +++ b/test/notify-test.js @@ -1,58 +1,69 @@ -assert = require('chai').assert -nock = require 'nock' -nconf = require 'nconf' -debug = require('debug')('pagerduty-overrides:tests') +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const { assert } = require('chai'); +const nock = require('nock'); +const nconf = require('nconf'); +const debug = require('debug')('pagerduty-overrides:tests'); -config = require '../src/config' -notify = require '../src/notify' +const config = require('../src/config'); +const notify = require('../src/notify'); -configPath = __dirname + '/fixtures/config.json' +const configPath = __dirname + '/fixtures/config.json'; nock.disableNetConnect(); -# https://github.com/chenka/node-slackr/blob/master/test/index.coffee -describe 'Test send message using notify.send for both', -> +// https://github.com/chenka/node-slackr/blob/master/test/index.coffee +describe('Test send message using notify.send for both', function() { - actual = null + let actual = null; - before (done) -> - overlapDate = new Date() - message = - user: 'Test user' - userId: '1234' - schedules: ['TEST1', 'TEST2'] - date: overlapDate + before(function(done) { + const overlapDate = new Date(); + let message = { + user: 'Test user', + userId: '1234', + schedules: ['TEST1', 'TEST2'], + date: overlapDate, crossDate: overlapDate + }; - expectBody = - text:"Following overlaps found:\n*Test user:* `TEST1` and `TEST2` (the first starting on #{overlapDate.toUTCString()}, the second on #{overlapDate.toUTCString()})\n" + const expectBody = { + text:`Following overlaps found:\n*Test user:* \`TEST1\` and \`TEST2\` (the first starting on ${overlapDate.toUTCString()}, the second on ${overlapDate.toUTCString()})\n`, channel:"#channel-name" + }; - config.setupConfig configPath, (err) -> - if err then return done err + return config.setupConfig(configPath, function(err) { + if (err) { return done(err); } nock('https://incomingUrl') .post("/", expectBody) .query(true) - .reply(200, 'ok') + .reply(200, 'ok'); nock('https://api.pagerduty.com/') .post('/incidents') .query(true) - .reply(200, 'ok') - - configSchedules = nconf.get('SCHEDULES') - options = configSchedules[0]['NOTIFICATIONS'] - message = - user: 'Test user' - userId: '1234' - schedules: ['TEST1', 'TEST2'] - date: overlapDate + .reply(200, 'ok'); + + const configSchedules = nconf.get('SCHEDULES'); + const options = configSchedules[0]['NOTIFICATIONS']; + message = { + user: 'Test user', + userId: '1234', + schedules: ['TEST1', 'TEST2'], + date: overlapDate, crossDate: overlapDate + }; - notify.send options, [ message ], (err, result) -> - if err then return done err - actual = result - done() + return notify.send(options, [ message ], function(err, result) { + if (err) { return done(err); } + actual = result; + return done(); + }); + }); + }); - it 'Check result from send notification', -> - assert.equal 'ok', actual + return it('Check result from send notification', () => assert.equal('ok', actual)); +}); diff --git a/test/pagerduty-test.js b/test/pagerduty-test.js index 420e8c9..6e9c586 100644 --- a/test/pagerduty-test.js +++ b/test/pagerduty-test.js @@ -1,208 +1,257 @@ -assert = require('chai').assert -nock = require 'nock' -nconf = require 'nconf' -debug = require('debug')('pagerduty-overrides:tests') - -config = require '../src/config' -pd = require '../src/pagerduty' - -configPath = __dirname + '/fixtures/config.json' -configWithDaysPath = __dirname + '/fixtures/config-days.json' -configWrongPath = __dirname + '/fixtures/config-wrong.json' +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS205: Consider reworking code to avoid use of IIFEs + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const { assert } = require('chai'); +const nock = require('nock'); +const nconf = require('nconf'); +const debug = require('debug')('pagerduty-overrides:tests'); + +const config = require('../src/config'); +const pd = require('../src/pagerduty'); + +const configPath = __dirname + '/fixtures/config.json'; +const configWithDaysPath = __dirname + '/fixtures/config-days.json'; +const configWrongPath = __dirname + '/fixtures/config-wrong.json'; nock.disableNetConnect(); -describe 'Get schedules Ids', -> - schedules = null +describe('Get schedules Ids', function() { + let schedules = null; - before (done) -> - config.setupConfig configPath, (err) -> - if err then return done err + before(done => + config.setupConfig(configPath, function(err) { + if (err) { return done(err); } nock('https://api.pagerduty.com/') .get('/schedules') .query(true) - .replyWithFile(200, __dirname + '/fixtures/schedules.json') + .replyWithFile(200, __dirname + '/fixtures/schedules.json'); - pd.getSchedulesIds (err, schedulesIds) -> - schedules = schedulesIds - done err + return pd.getSchedulesIds(function(err, schedulesIds) { + schedules = schedulesIds; + return done(err); + }); + }) + ); - it 'Check how many schedules', -> - assert.equal schedules.length, 2 + return it('Check how many schedules', () => assert.equal(schedules.length, 2)); +}); -describe 'Check schedules', -> - schedules = null +describe('Check schedules', function() { + let schedules = null; - before (done) -> - config.setupConfig configPath, (err) -> - if err then return done err + before(done => + config.setupConfig(configPath, function(err) { + if (err) { return done(err); } nock('https://api.pagerduty.com/') .get('/schedules') .query(true) - .replyWithFile(200, __dirname + '/fixtures/schedules.json') + .replyWithFile(200, __dirname + '/fixtures/schedules.json'); - pd.checkSchedulesIds (err, res) -> - schedules = res - done err + return pd.checkSchedulesIds(function(err, res) { + schedules = res; + return done(err); + }); + }) + ); - it 'Check if config ids are in pagerduty schedules', -> - assert.ok schedules + return it('Check if config ids are in pagerduty schedules', () => assert.ok(schedules)); +}); -describe 'Check schedules with wrong config', -> - schedules = null +describe('Check schedules with wrong config', function() { + let schedules = null; - before (done) -> - config.setupConfig configWrongPath, (err) -> - if err then return done err + before(done => + config.setupConfig(configWrongPath, function(err) { + if (err) { return done(err); } nock('https://api.pagerduty.com/') .get('/schedules') .query(true) - .replyWithFile(200, __dirname + '/fixtures/schedules.json') + .replyWithFile(200, __dirname + '/fixtures/schedules.json'); - pd.checkSchedulesIds (err, res) -> - schedules = res - done err + return pd.checkSchedulesIds(function(err, res) { + schedules = res; + return done(err); + }); + }) + ); - it 'Check if config ids are in pagerduty schedules', -> - assert.notOk schedules + return it('Check if config ids are in pagerduty schedules', () => assert.notOk(schedules)); +}); -describe 'Compare schedules', -> +describe('Compare schedules', function() { - message = null + let message = null; - before (done) -> - config.setupConfig configPath, (err) -> - if err then return done err + before(done => + config.setupConfig(configPath, function(err) { + if (err) { return done(err); } nock('https://api.pagerduty.com/') .get('/schedules') .query(true) - .replyWithFile(200, __dirname + '/fixtures/schedules.json') + .replyWithFile(200, __dirname + '/fixtures/schedules.json'); nock('https://api.pagerduty.com/') .get('/oncalls') .query(true) - .replyWithFile(200, __dirname + '/fixtures/entries.json') + .replyWithFile(200, __dirname + '/fixtures/entries.json'); nock('https://api.pagerduty.com/') .get('/oncalls') .query(true) - .replyWithFile(200, __dirname + '/fixtures/entries.json') + .replyWithFile(200, __dirname + '/fixtures/entries.json'); nock('https://api.pagerduty.com/') .post('/incidents', require('./fixtures/incident.json')) .query(true) - .reply(200, 'ok') + .reply(200, 'ok'); nock('https://api.pagerduty.com/') .post('/incidents', require('./fixtures/incident2.json')) .query(true) - .reply(200, 'ok') - - nock('https://incomingUrl/').post("/").reply(200, 'ok') - - pd.checkSchedulesIds (err, res) -> - if err then return done err - unless res - return done new Error("Check failed") - pd.processSchedulesFromConfig (err, msg) -> - if err then return done err - message = msg - done err - - it 'Check if there are 2 returned messages', -> - assert.isArray message - assert.lengthOf message, 2 - - it 'Check returned messages if they contain "Primary and Secondary"', -> - for singleMessage in message - debug(singleMessage) - assert.isObject singleMessage - assert.include singleMessage.schedules, "Primary" - assert.include singleMessage.schedules, "Secondary" - - -describe 'Compare schedules on specific days', -> - - message = null - - before (done) -> - config.setupConfig configWithDaysPath, (err) -> - if err then return done err + .reply(200, 'ok'); + + nock('https://incomingUrl/').post("/").reply(200, 'ok'); + + return pd.checkSchedulesIds(function(err, res) { + if (err) { return done(err); } + if (!res) { + return done(new Error("Check failed")); + } + return pd.processSchedulesFromConfig(function(err, msg) { + if (err) { return done(err); } + message = msg; + return done(err); + }); + }); + }) + ); + + it('Check if there are 2 returned messages', function() { + assert.isArray(message); + return assert.lengthOf(message, 2); + }); + + return it('Check returned messages if they contain "Primary and Secondary"', () => + (() => { + const result = []; + for (let singleMessage of Array.from(message)) { + debug(singleMessage); + assert.isObject(singleMessage); + assert.include(singleMessage.schedules, "Primary"); + result.push(assert.include(singleMessage.schedules, "Secondary")); + } + return result; + })() + ); +}); + + +describe('Compare schedules on specific days', function() { + + let message = null; + + before(done => + config.setupConfig(configWithDaysPath, function(err) { + if (err) { return done(err); } nock('https://api.pagerduty.com/') .get('/schedules') .query(true) - .replyWithFile(200, __dirname + '/fixtures/schedules.json') + .replyWithFile(200, __dirname + '/fixtures/schedules.json'); nock('https://api.pagerduty.com/') .get('/oncalls') .query(true) - .replyWithFile(200, __dirname + '/fixtures/entries-days.json') + .replyWithFile(200, __dirname + '/fixtures/entries-days.json'); nock('https://api.pagerduty.com/') .get('/oncalls') .query(true) - .replyWithFile(200, __dirname + '/fixtures/entries-days.json') + .replyWithFile(200, __dirname + '/fixtures/entries-days.json'); nock('https://api.pagerduty.com/') .post('/incidents', require('./fixtures/incident.json')) .query(true) - .reply(200, 'ok') - - nock('https://incomingUrl/').post("/").reply(200, 'ok') - - pd.checkSchedulesIds (err, res) -> - if err then return done err - unless res - return done new Error("Check failed") - pd.processSchedulesFromConfig (err, msg) -> - if err then return done err - message = msg - done err - - it 'Check if there is 1 returned message', -> - assert.isArray message - assert.lengthOf message, 1 - - it 'Check if the returned message contains "Primary and Secondary"', -> - for singleMessage in message - debug(singleMessage) - assert.isObject singleMessage - assert.include singleMessage.schedules, "Primary" - assert.include singleMessage.schedules, "Secondary" - -describe 'Compare schedules with no overlap', -> - - message = null - - before (done) -> - config.setupConfig configPath, (err) -> - if err then return done err + .reply(200, 'ok'); + + nock('https://incomingUrl/').post("/").reply(200, 'ok'); + + return pd.checkSchedulesIds(function(err, res) { + if (err) { return done(err); } + if (!res) { + return done(new Error("Check failed")); + } + return pd.processSchedulesFromConfig(function(err, msg) { + if (err) { return done(err); } + message = msg; + return done(err); + }); + }); + }) + ); + + it('Check if there is 1 returned message', function() { + assert.isArray(message); + return assert.lengthOf(message, 1); + }); + + return it('Check if the returned message contains "Primary and Secondary"', () => + (() => { + const result = []; + for (let singleMessage of Array.from(message)) { + debug(singleMessage); + assert.isObject(singleMessage); + assert.include(singleMessage.schedules, "Primary"); + result.push(assert.include(singleMessage.schedules, "Secondary")); + } + return result; + })() + ); +}); + +describe('Compare schedules with no overlap', function() { + + let message = null; + + before(done => + config.setupConfig(configPath, function(err) { + if (err) { return done(err); } nock('https://api.pagerduty.com/') .get('/schedules') .query(true) - .replyWithFile(200, __dirname + '/fixtures/schedules.json') + .replyWithFile(200, __dirname + '/fixtures/schedules.json'); nock('https://api.pagerduty.com/') .get('/oncalls') .query(true) - .replyWithFile(200, __dirname + '/fixtures/entries.json') + .replyWithFile(200, __dirname + '/fixtures/entries.json'); nock('https://api.pagerduty.com/') .get('/oncalls') .query(true) - .replyWithFile(200, __dirname + '/fixtures/entries-no-overlap.json') - - pd.checkSchedulesIds (err, res) -> - if err then return done err - unless res - return done new Error("Check failed") - pd.processSchedulesFromConfig (err, msg) -> - if err then return done err - message = msg - done err - - it 'Check that there are no returned messages', -> - assert.isArray message - assert.isEmpty message + .replyWithFile(200, __dirname + '/fixtures/entries-no-overlap.json'); + + return pd.checkSchedulesIds(function(err, res) { + if (err) { return done(err); } + if (!res) { + return done(new Error("Check failed")); + } + return pd.processSchedulesFromConfig(function(err, msg) { + if (err) { return done(err); } + message = msg; + return done(err); + }); + }); + }) + ); + + return it('Check that there are no returned messages', function() { + assert.isArray(message); + return assert.isEmpty(message); + }); +}); From 2ddd9cebf887e8adbdb7fe330cbebd8a575f5f4c Mon Sep 17 00:00:00 2001 From: miiila Date: Thu, 14 Sep 2017 16:25:27 +0200 Subject: [PATCH 3/9] style: fix issues by eslint and hand --- bin/pdoverrides | 55 ++++----- example.js | 36 +++--- src/config.js | 16 +-- src/index.js | 10 +- src/notify.js | 271 +++++++++++++++++++---------------------- src/pagerduty-api.js | 47 ++++--- src/pagerduty.js | 221 +++++++++++++++++---------------- test/config-test.js | 20 +-- test/notify-test.js | 41 +++---- test/pagerduty-test.js | 186 +++++++++++++--------------- 10 files changed, 422 insertions(+), 481 deletions(-) diff --git a/bin/pdoverrides b/bin/pdoverrides index afa3b8b..ed5a0b6 100755 --- a/bin/pdoverrides +++ b/bin/pdoverrides @@ -1,39 +1,33 @@ #!/usr/bin/env node -var program = require('commander'); -var fs = require('fs'); -var pjson = require('../package.json'); -var config = require('../lib/config'); -var fsAccess = require('fs-access'); - -program - .command('check') - .description('Check PagerDuty Overlaps') - .action(runCheck); +const program = require('commander'); +const pjson = require('../package.json'); +const config = require('../lib/config'); +const fsAccess = require('fs-access'); +const pd = require('../lib/pagerduty'); function runCheck(configFile) { - fsAccess(configFile, function (err) { + fsAccess(configFile, (err) => { if (err) throw err; - config.setupConfig(configFile, function(err, res){ - if (err) { - console.error(err); + config.setupConfig(configFile, (configErr) => { + if (configErr) { + console.error(configErr); process.exit(1); } - var pd = require('../lib/pagerduty'); - pd.checkSchedulesIds(function(error, res) { - if (error) { - console.error("Check failed with error:", error) + pd.checkSchedulesIds((checkError, res) => { + if (checkError) { + console.error('Check failed with error:', checkError); process.exit(1); } if (!res) { - console.error("Check failed - empty response"); + console.error('Check failed - empty response'); process.exit(1); } else { - console.log("Config schedule IDs passed."); - pd.processSchedulesFromConfig(function(error, msg) { + console.log('Config schedule IDs passed.'); + pd.processSchedulesFromConfig((error, msg) => { if (error) { - console.error("Error while processing schedules from config", error); - process.exit(1) + console.error('Error while processing schedules from config', error); + process.exit(1); } console.log(msg); process.exit(0); @@ -42,13 +36,18 @@ function runCheck(configFile) { }); }); }); -}; +} + +program + .command('check') + .description('Check PagerDuty Overlaps') + .action(runCheck); program - .version(pjson.version) - .usage('check --config ') - .option('-c, --config', 'Path to config.json') - .parse(process.argv); + .version(pjson.version) + .usage('check --config ') + .option('-c, --config', 'Path to config.json') + .parse(process.argv); // default help if (!program.args.length) program.help(); diff --git a/example.js b/example.js index a83c374..08de165 100644 --- a/example.js +++ b/example.js @@ -1,28 +1,20 @@ -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ -const config = require('./src/config'); -const nconf = require('nconf'); -const pd = require('./src/pagerduty'); +const config = require('./src/config'); +const pd = require('./src/pagerduty'); -const configPath = __dirname + '/config.json'; +const configPath = `${__dirname}/config.json`; -config.setupConfig(configPath, function(err) { - if (err) { console.error(err); } - return pd.checkSchedulesIds(function(err, res) { - if (err) { console.error(err); } +config.setupConfig(configPath, (configErr) => { + if (configErr) { console.error(configErr); } + return pd.checkSchedulesIds((checkSchedulesErr, res) => { + if (checkSchedulesErr) { console.error(checkSchedulesErr); } if (!res) { - return console.error("Check failed"); - } else { - return pd.processSchedulesFromConfig(function(err, msg) { - if (err) { - return console.error(err); - } else { - return console.log(msg); - } - }); + return console.error('Check failed'); } + return pd.processSchedulesFromConfig((err, msg) => { + if (err) { + return console.error(err); + } + return console.log(msg); + }); }); }); diff --git a/src/config.js b/src/config.js index 766ed85..8febd82 100644 --- a/src/config.js +++ b/src/config.js @@ -1,12 +1,7 @@ -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ -const fs = require('fs'); +const fs = require('fs'); const nconf = require('nconf'); -const setupConfig = function(configPath, cb) { +function setupConfig(configPath, cb) { if (fs.existsSync(configPath)) { console.log('Loading config from :', configPath); // Priority order argv before ENV and file as defaults @@ -15,11 +10,10 @@ const setupConfig = function(configPath, cb) { .file({ file: configPath }); process.env.DEBUG = nconf.get('DEBUG'); return cb(); - } else { - return cb(new Error(`Config does not exist: ${configPath}`)); } -}; + return cb(new Error(`Config does not exist: ${configPath}`)); +} module.exports = { - setupConfig + setupConfig, }; diff --git a/src/index.js b/src/index.js index c05e6b9..5e8283c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,9 @@ +const checker = require('./pagerduty'); +const config = require('./config'); +const notify = require('./notify'); + module.exports = { - checker: require('./pagerduty'), - config: require('./config'), - notify: require('./notify') + checker, + config, + notify, }; diff --git a/src/notify.js b/src/notify.js index 212a9f4..7705980 100644 --- a/src/notify.js +++ b/src/notify.js @@ -1,186 +1,171 @@ -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS103: Rewrite code to no longer use __guard__ - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ -const Slack = require('node-slackr'); -const nconf = require('nconf'); -const async = require('async'); -const request = require('request'); -const debug = require('debug')('pagerduty-overrides:notifications'); -const pdApi = require('./pagerduty-api'); +const Slack = require('node-slackr'); +const async = require('async'); +const debug = require('debug')('pagerduty-overrides:notifications'); +const pdApi = require('./pagerduty-api'); -const createPagerDutyIncident = function(options, message, cb) { +function createPagerDutyIncident(options, message, cb) { debug(`Creating PD incident ${JSON.stringify(message)} with options ${JSON.stringify(options)}`); if (!options.pdToken || !options.serviceId || !options.from) { - cb(new Error("Missing PAGERDUTY settings (you'll need PAGERDUTY_TOKEN, PAGERDUTY_SERVICE_ID and PAGERDUTY_FROM)")); + return cb(new Error("Missing PAGERDUTY settings (you'll need PAGERDUTY_TOKEN, PAGERDUTY_SERVICE_ID and PAGERDUTY_FROM)")); } if (!message.userId && !options.escalationPolicyId) { - return cb(new Error("No userId or escalation policy specified")); + return cb(new Error('No userId or escalation policy specified')); + } + const incident = { + type: 'incident', + title: 'On-call overlap found!', + service: { + id: options.serviceId, + type: 'service_reference', + }, + body: { + type: 'incident_body', + details: message.messages.join('\n'), + }, + }; + if (options.escalationPolicyId) { + incident.escalationPolicy = { + id: options.escalationPolicyId, + type: 'escalation_policy_reference', + }; } else { - const incident = { - type: "incident", - title: "On-call overlap found!", - service: { - id: options.serviceId, - type: "service_reference" + incident.assignments = [{ + assignee: { + id: message.userId, + type: 'user_reference', }, - body: { - type: "incident_body", - details: message.messages.join('\n') - } - }; + }, + ]; + } - if (options.escalationPolicyId) { - incident.escalationPolicy = { - id: options.escalationPolicyId, - type: "escalation_policy_reference" - }; - } else { - incident.assignments = [{ - assignee : { - id: message.userId, - type: "user_reference" - } - } - ]; - } + const incidentOptions = { + method: 'POST', + json: { + incident, + }, + headers: { + From: options.from, + Authorization: `Token token=${options.pdToken}`, + }, + }; - const incidentOptions = { - method: "POST", - json: { - incident - }, - headers: { - From: options.from, - Authorization: `Token token=${options.pdToken}` - } - }; - - return pdApi.send('/incidents', incidentOptions, function(err, res, body) { - if (__guard__(body != null ? body.errors : undefined, x => x.length) > 0) { - if (err == null) { err = new Error(`INCIDENT_CREATION_FAILED Errors: ${JSON.stringify(body.errors)}`); } - } - if (((res != null ? res.statusCode : undefined) !== 200) && ((res != null ? res.statusCode : undefined) !== 201)) { - if (err == null) { err = new Error(`INCIDENT_CREATION_FAILED Creating incident failed with status ${res.statusCode}. Returned body: ${JSON.stringify(body)}`); } - } - if (err) { - debug("INCIDENT_CREATION_FAILED: ", err); - } - return cb(err); - }); - } -}; + return pdApi.send('/incidents', incidentOptions, (err, res, body) => { + let error = err; + if (!error && body !== undefined && body.errors !== undefined && body.errors.length > 0) { + error = new Error(`INCIDENT_CREATION_FAILED Errors: ${JSON.stringify(body.errors)}`); + } else if (!error && res !== undefined && (res.statusCode !== 200 || res.statusCode !== 201)) { + error = new Error(`INCIDENT_CREATION_FAILED Creating incident failed with status ${res.statusCode}. Returned body: ${JSON.stringify(body)}`); + } + if (error) { + debug('INCIDENT_CREATION_FAILED: ', error); + } + return cb(error); + }); +} // https://www.npmjs.com/package/node-slackr -const createSlackMessage = function(options, message, cb) { +function createSlackMessage(options, message, cb) { if (options.webhookUrl) { const slack = new Slack(options.webhookUrl); - return slack.notify(message, function(err, result) { + return slack.notify(message, (err, result) => { if (err) { - console.error("SLACK_SEND_MESSAGE_FAILED:", err); + console.error('SLACK_SEND_MESSAGE_FAILED:', err); return cb(err); } return cb(null, result); }); - } else { - return cb(new Error("Missing Slack webhook URL.")); } -}; + return cb(new Error('Missing Slack webhook URL.')); +} // Input is array of messages is we have more overlaps -const formatMessage = function(messages, option) { +function formatMessage(messages, option = 'plain') { let outputMessage; - if (option == null) { option = 'plain'; } if (typeof messages === 'string') { return messages; - } else { - switch (option) { - case 'plain': - outputMessage = "_Following overlaps found:_\n"; - for (var message of Array.from(messages)) { - outputMessage += `${message.user}: ${message.schedules[0]} and ${message.schedules[1]} (the first starting on ${message.date.toUTCString()}, the second on ${message.crossDate.toUTCString()})\n`; - } - break; - case 'markdown': - outputMessage = "Following overlaps found:\n"; - for (message of Array.from(messages)) { - outputMessage += `*${message.user}:* \`${message.schedules[0]}\` and \`${message.schedules[1]}\` (the first starting on ${message.date.toUTCString()}, the second on ${message.crossDate.toUTCString()})\n`; - } - break; - case 'json': - outputMessage = messages.reduce(function(acc, curr){ - if (acc[curr.userId] == null) { acc[curr.userId] = {}; } - if (acc[curr.userId].userId == null) { acc[curr.userId].userId = curr.userId; } - if (acc[curr.userId].user == null) { acc[curr.userId].user = curr.user; } - if (acc[curr.userId].messages == null) { acc[curr.userId].messages = []; } - acc[curr.userId].messages.push(`${curr.schedules[0]} and ${curr.schedules[1]} (the first starting on ${curr.date.toUTCString()}, the second on ${curr.crossDate.toUTCString()})`); - return acc; - } + } + switch (option) { + case 'plain': + outputMessage = '_Following overlaps found:_\n'; + messages.forEach((message) => { + outputMessage += `${message.user}: ${message.schedules[0]} and ${message.schedules[1]} (the first starting on ${message.date.toUTCString()}, the second on ${message.crossDate.toUTCString()})\n`; + }); + break; + case 'markdown': + outputMessage = 'Following overlaps found:\n'; + messages.forEach((message) => { + outputMessage += `*${message.user}:* \`${message.schedules[0]}\` and \`${message.schedules[1]}\` (the first starting on ${message.date.toUTCString()}, the second on ${message.crossDate.toUTCString()})\n`; + }); + break; + case 'json': + outputMessage = messages.reduce((acc, curr) => { + if (acc[curr.userId] == null) { acc[curr.userId] = {}; } + if (acc[curr.userId].userId == null) { acc[curr.userId].userId = curr.userId; } + if (acc[curr.userId].user == null) { acc[curr.userId].user = curr.user; } + if (acc[curr.userId].messages == null) { acc[curr.userId].messages = []; } + acc[curr.userId].messages.push(`${curr.schedules[0]} and ${curr.schedules[1]} (the first starting on ${curr.date.toUTCString()}, the second on ${curr.crossDate.toUTCString()})`); + return acc; + } , {}); - break; - } + break; + default: + console.error(`Unsupported option ${option} used.`); } + debug('Notification - formatMessage option: ', option); debug('Notification - formatMessage: ', outputMessage); return outputMessage; -}; +} -const send = function(options, message, cb) { +function send(options, message, cb) { debug('send:', options, message); return async.parallel([ - function(next) { - if (options['SLACK'] || (options['SLACK_WEBHOOK_URL'] != null)) { - debug('Found Slack webhook, sending a notification'); - const slackMessage = {}; - slackMessage.text = formatMessage(message, 'markdown'); - slackMessage.channel = options['SLACK'] != null ? options['SLACK']['CHANNEL'] : undefined; - const slackOptions = {}; - slackOptions.webhookUrl = (options['SLACK'] != null ? options['SLACK']['SLACK_WEBHOOK_URL'] : undefined) || options['SLACK_WEBHOOK_URL']; - return createSlackMessage(slackOptions, slackMessage, next); - } else { - debug('No Slack webhook defined'); - return next(); - } - }, - function(next) { - if (!options['PAGERDUTY'] && !options['PAGERDUTY_TOKEN']) { - return debug('No PAGERDUTY options defined'); - } else if ((options['PAGERDUTY']['PAGERDUTY_TOKEN'] || options['PAGERDUTY_TOKEN']) && options['PAGERDUTY']['PAGERDUTY_SERVICE_ID'] && options['PAGERDUTY']['PAGERDUTY_FROM']) { - debug('Found PD token - creating an incident'); - const pdOptions = {}; - pdOptions.pdToken = options['PAGERDUTY']['PAGERDUTY_TOKEN'] || options['PAGERDUTY_TOKEN']; - pdOptions.serviceId = options['PAGERDUTY']['PAGERDUTY_SERVICE_ID']; - pdOptions.escalationPolicyId = options['PAGERDUTY']['PAGERDUTY_ESCALATION_POLICY_ID']; - pdOptions.from = options['PAGERDUTY'] != null ? options['PAGERDUTY']['PAGERDUTY_FROM'] : undefined; - const messagesByUser = formatMessage(message, 'json'); - return async.each(messagesByUser, - (item, cb) => createPagerDutyIncident(pdOptions, item, cb), - err => next(err)); - } else { - console.log(`No PD options defined or defined incorrectly (${JSON.stringify(options['PAGERDUTY'])})`); - return next(); + function sendSlack(next) { + if (options.SLACK || (options.SLACK_WEBHOOK_URL)) { + debug('Found Slack webhook, sending a notification'); + const slackMessage = {}; + const slackOptions = {}; + slackMessage.text = formatMessage(message, 'markdown'); + if (options.SLACK) { + slackMessage.channel = options.SLACK.CHANNEL; + slackOptions.webhookUrl = options.SLACK.SLACK_WEBHOOK_URL; } + if (!slackOptions.webhookUrl) { slackOptions.webhookUrl = options.SLACK_WEBHOOK_URL; } + return createSlackMessage(slackOptions, slackMessage, next); + } + debug('No Slack webhook defined'); + return next(); + }, + function sendPagerDuty(next) { + if ((!options.PAGERDUTY && !options.PAGERDUTY.PAGERDUTY_TOKEN) || !options.PAGERDUTY_TOKEN) { + debug('No PAGERDUTY token defined'); + } else if (options.PAGERDUTY.PAGERDUTY_SERVICE_ID && options.PAGERDUTY.PAGERDUTY_FROM) { + debug('Found PD token - creating an incident'); + const pdOptions = {}; + pdOptions.pdToken = options.PAGERDUTY.PAGERDUTY_TOKEN || options.PAGERDUTY_TOKEN; + pdOptions.serviceId = options.PAGERDUTY.PAGERDUTY_SERVICE_ID; + pdOptions.escalationPolicyId = options.PAGERDUTY.PAGERDUTY_ESCALATION_POLICY_ID; + pdOptions.from = options.PAGERDUTY != null ? options.PAGERDUTY.PAGERDUTY_FROM : undefined; + const messagesByUser = formatMessage(message, 'json'); + return async.each(messagesByUser, + (item, pdCb) => createPagerDutyIncident(pdOptions, item, pdCb), + err => next(err)); } - ], function(err, results) { - if (err) { return cb(err); } - const output = results.filter(n => n !== undefined); - return cb(null, output); + console.log(`No PD options defined or defined incorrectly (${JSON.stringify(options.PAGERDUTY)})`); + return next(); + }, + ], (err, results) => { + if (err) { return cb(err); } + const output = results.filter(n => n !== undefined); + return cb(null, output); }); -}; +} module.exports = { - send + send, }; - -function __guard__(value, transform) { - return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; -} \ No newline at end of file diff --git a/src/pagerduty-api.js b/src/pagerduty-api.js index 8563f04..d2c48ab 100644 --- a/src/pagerduty-api.js +++ b/src/pagerduty-api.js @@ -1,44 +1,41 @@ -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ -const _ = require('underscore'); -const debug = require('debug')('pagerduty-overrides'); -const request = require('request'); -const nconf = require('nconf'); -const url = require('url'); +const _ = require('underscore'); +const debug = require('debug')('pagerduty-overrides'); +const request = require('request'); +const nconf = require('nconf'); +const url = require('url'); // Factory for sending request to PD API -const send = function(endpointPath, overrideOptions, cb) { - debug(`Calling ${endpointPath} with options:`, overrideOptions); +function send(endpointPath, overrideOptions, cb) { + let callback = cb; + let options = overrideOptions; + + debug(`Calling ${endpointPath} with options:`, options); const defaultOptions = { uri: url.resolve('https://api.pagerduty.com', endpointPath), method: 'GET', - json: true + json: true, }; - if (typeof overrideOptions === 'function') { - cb = overrideOptions; - overrideOptions = {}; + if (typeof options === 'function') { + callback = options; + options = {}; } - _.extend(defaultOptions, overrideOptions); + _.extend(defaultOptions, options); - if (defaultOptions.headers == null) { defaultOptions.headers = []; } - if (defaultOptions.headers.Authorization == null) { defaultOptions.headers.Authorization = `Token token=${nconf.get('PAGERDUTY_READ_ONLY_TOKEN')}`; } - defaultOptions.headers.Accept = 'application/vnd.pagerduty+json;version=2'; + if (!defaultOptions.headers) { defaultOptions.headers = []; } + if (!defaultOptions.headers.Authorization) { defaultOptions.headers.Authorization = `Token token=${nconf.get('PAGERDUTY_READ_ONLY_TOKEN')}`; } + defaultOptions.headers.Accept = 'application/vnd.pagerduty+json;version=2'; defaultOptions.headers['Content-Type'] = 'application/json'; - if (defaultOptions.qs == null) { defaultOptions.qs = []; } + if (!defaultOptions.qs) { defaultOptions.qs = []; } defaultOptions.qs.limit = 100; defaultOptions.qs.timezone = 'UTC'; debug('Calling request with: ', defaultOptions); - return request(defaultOptions, cb); -}; + return request(defaultOptions, callback); +} module.exports = { - send + send, }; diff --git a/src/pagerduty.js b/src/pagerduty.js index 2b94780..2161114 100644 --- a/src/pagerduty.js +++ b/src/pagerduty.js @@ -1,21 +1,12 @@ -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS104: Avoid inline assignments - * DS204: Change includes calls to have a more natural evaluation order - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ -const async = require('async'); -const nconf = require('nconf'); -const _ = require('underscore'); -const debug = require('debug')('pagerduty-overrides'); -const notify = require('./notify'); -const pdApi = require('./pagerduty-api'); +const async = require('async'); +const nconf = require('nconf'); +const _ = require('underscore'); +const debug = require('debug')('pagerduty-overrides'); +const notify = require('./notify'); +const pdApi = require('./pagerduty-api'); // Get schedule for ID and 2 weeks -const getSchedule = function(id, cb) { +function getSchedule(id, cb) { if (nconf.get('WEEKS_TO_CHECK') > 0) { const week = 7 * 86400 * 1000; const timeNow = new Date(); @@ -25,130 +16,108 @@ const getSchedule = function(id, cb) { qs: { 'schedule_ids[]': id, until: timeUntil.toISOString(), - since: timeNow.toISOString() - } + since: timeNow.toISOString(), + }, }; - return pdApi.send("/oncalls", scheduleOpts, function(err, res, body) { + return pdApi.send('/oncalls', scheduleOpts, (err, res, body) => { if (err) { - console.log("Request send error:", err); + console.log('Request send error:', err); return cb(err); } - if (res.statusCode !== 200) { return cb(new Error(`Entries returned status code ${res.statusCode}`)); } - return cb(err, {id, entries: body.oncalls}); + if (res.statusCode !== 200) { + return cb(new Error(`Entries returned status code ${res.statusCode}`)); + } + return cb(err, { id, entries: body.oncalls }); }); - } else { - return cb(new Error("Missing WEEKS_TO_CHECK settings")); } -}; + return cb(new Error('Missing WEEKS_TO_CHECK settings')); +} // Get all schedules and returns their ids -const getSchedulesIds = function(cb) { - debug("Getting schedules from PD"); - return pdApi.send("/schedules", {}, function(err, res, body) { +function getSchedulesIds(cb) { + debug('Getting schedules from PD'); + return pdApi.send('/schedules', {}, (err, res, body) => { if (err) { - console.log("Request send error:", err); + console.log('Request send error:', err); return cb(err); } debug('Returned status code:', res.statusCode); const schedulesIds = []; - for (let schedule of Array.from(body.schedules)) { + body.schedules.forEach((schedule) => { schedulesIds.push(schedule.id); // UWAGA UWAGA - side effect follows! - // it's easier, cheaper and more comprehensive to load schedule names here and temporarily store them using nconf + // it's easier and more comprehensive to load schedule names here and store them using nconf nconf.set(`schedulesNames:${schedule.id}`, schedule.name); - } + }); - debug("Schedules Ids from PD: ", schedulesIds); - debug("Schedules Names from PD: ", nconf.get("schedulesNames")); + debug('Schedules Ids from PD: ', schedulesIds); + debug('Schedules Names from PD: ', nconf.get('schedulesNames')); return cb(null, schedulesIds); }); -}; +} // Check if all schedules defined in config are available in PD -const checkSchedulesIds = function(cb) { +function checkSchedulesIds(cb) { const configSchedules = nconf.get('SCHEDULES'); const listIds = []; - for (let ids of Array.from(configSchedules)) { - listIds.push(ids['SCHEDULE']); - } - const configSchedulesIds = _.uniq(_.flatten(listIds)); - debug("Schedules Ids from config: ", configSchedulesIds); - return getSchedulesIds(function(err, schedulesIds) { + configSchedules.forEach((ids) => { + listIds.push(ids.SCHEDULE); + }); + const configSchedulesIds = _.uniq(_.flatten(listIds)); + debug('Schedules Ids from config: ', configSchedulesIds); + return getSchedulesIds((err, schedulesIds) => { if (err) { return cb(err); } debug('intersection: ', _.intersection(configSchedulesIds, schedulesIds).length); debug('config: ', configSchedulesIds.length); if ((_.intersection(configSchedulesIds, schedulesIds).length) === configSchedulesIds.length) { return cb(null, true); - } else { - return cb(null, false); } + return cb(null, false); }); -}; - -const processSchedulesFromConfig = function(done) { - let messages = []; - const configSchedules = nconf.get('SCHEDULES'); - debug('configSchedules:', configSchedules.length); - return async.forEach(configSchedules, function(processedConfig, cb) { - debug('Process schedule:' ); - return async.mapSeries(processedConfig['SCHEDULE'], (i, next) => getSchedule(i, next) - , function(err, results) { - if (err) { return cb(err); } - if (results) { - return processSchedules(results, processedConfig['EXCLUSION_DAYS'], function(err, message) { - debug('processSchedules:', processedConfig); - if (message !== "OK") { - messages = messages.concat(message); - if (processedConfig['NOTIFICATIONS']) { - debug('Sending notifications.'); - return sendNotification(processedConfig['NOTIFICATIONS'], message, cb); - } - } - return cb(); - }); - } else { - return cb(new Error("No schedule to process.")); - } - }); - } - , function(err) { - if (err) { return done(err); } - return done(null, messages); - }); -}; +} -var sendNotification = function(options, message, cb) { - debug("NOTIFICATIONS:", message); - debug("NOTIFICATIONS-OPTIONS:", options); +function sendNotification(options, message, cb) { + debug('NOTIFICATIONS:', message); + debug('NOTIFICATIONS-OPTIONS:', options); return notify.send(options, message, err => cb(err)); -}; +} + +function getDayAbbrev(utcDay) { + const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; -var processSchedules = function(allSchedules, days, cb) { - if (days == null) { days = []; } - if (typeof days === 'function') { - [cb, days] = Array.from([days, []]); + return days[utcDay]; +} + +function processSchedules(allSchedules, days = [], cb) { + let callback = cb; + let daysArray = days; + + if (typeof daysArray === 'function') { + callback = daysArray; + daysArray = []; } + const messages = []; const duplicities = {}; debug('allSchedules:', allSchedules); - for (let schedule of Array.from(allSchedules)) { + allSchedules.forEach((schedule) => { debug('schedule:', JSON.stringify(schedule)); const otherSchedules = _.without(allSchedules, schedule); - debug('otherSchedules:',JSON.stringify(otherSchedules)); - for (let entry of Array.from(schedule.entries)) { + debug('otherSchedules:', JSON.stringify(otherSchedules)); + schedule.entries.forEach((entry) => { debug('checking entry: ', JSON.stringify(entry)); const myStart = entry.start; const myEnd = entry.end; const myUserId = entry.user.id; const myUserName = entry.user.summary; if (duplicities.myUserName == null) { duplicities.myUserName = []; } - for (let crossSchedule of Array.from(otherSchedules)) { - for (let crossCheckEntry of Array.from(crossSchedule.entries)) { + otherSchedules.forEach((crossSchedule) => { + crossSchedule.entries.forEach((crossCheckEntry) => { let overlap = false; const startDate = new Date(myStart); const day = getDayAbbrev(startDate.getUTCDay()); @@ -156,18 +125,22 @@ var processSchedules = function(allSchedules, days, cb) { const scheduleId = nconf.get(`schedulesNames:${schedule.id}`); const crossScheduleId = nconf.get(`schedulesNames:${crossSchedule.id}`); - const message = {user: myUserName, userId: myUserId, schedules: [scheduleId, crossScheduleId], date: startDate, crossDate: new Date(crossCheckEntry.start)}; + const message = { + user: myUserName, + userId: myUserId, + schedules: [scheduleId, crossScheduleId], + date: startDate, + crossDate: new Date(crossCheckEntry.start), + }; if ((myStart <= crossCheckEntry.start && crossCheckEntry.start < myEnd) && (crossCheckEntry.user.id === myUserId)) { - var needle; overlap = true; - if ((needle = day, Array.from(Object.keys(days)).includes(needle))) { - - if (((days[day] != null ? days[day].start : undefined) != null) && ((days[day] != null ? days[day].end : undefined) != null)) { - const exclusionStartTime = days[day].start.split(':'); - const exclusionEndTime = days[day].end.split(':'); + if (Object.keys(daysArray).includes(day)) { + if (daysArray[day].start && daysArray[day].end) { + const exclusionStartTime = daysArray[day].start.split(':'); + const exclusionEndTime = daysArray[day].end.split(':'); const exclusionStartDate = new Date(myStart); exclusionStartDate.setUTCHours(exclusionStartTime[0]); exclusionStartDate.setUTCMinutes(exclusionStartTime[1]); @@ -186,32 +159,56 @@ var processSchedules = function(allSchedules, days, cb) { } } - if (overlap && !Array.from(duplicities.myUserName).includes(crossCheckEntry.start)) { + if (overlap && !duplicities.myUserName.includes(crossCheckEntry.start)) { duplicities.myUserName.push(crossCheckEntry.start); messages.push(message); } - } - } - } - } + }); + }); + }); + }); debug(_.uniq(messages)); if (messages.length === 0) { - return cb(null, "OK"); - } else { - return cb(null, _.uniq(messages)); + return callback(null, 'OK'); } -}; - -var getDayAbbrev = function(utcDay) { - const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + return callback(null, _.uniq(messages)); +} - return days[utcDay]; -}; +function processSchedulesFromConfig(done) { + let messages = []; + const configSchedules = nconf.get('SCHEDULES'); + debug('configSchedules:', configSchedules.length); + return async.forEach(configSchedules, (processedConfig, cb) => { + debug('Process schedule:'); + return async.mapSeries(processedConfig.SCHEDULE, (i, next) => getSchedule(i, next) + , (mapErr, results) => { + if (mapErr) { return cb(mapErr); } + if (results) { + return processSchedules(results, processedConfig.EXCLUSION_DAYS, (err, message) => { + debug('processSchedules:', processedConfig); + if (message !== 'OK') { + messages = messages.concat(message); + if (processedConfig.NOTIFICATIONS) { + debug('Sending notifications.'); + return sendNotification(processedConfig.NOTIFICATIONS, message, cb); + } + } + return cb(); + }); + } + return cb(new Error('No schedule to process.')); + }); + } + , (err) => { + if (err) { return done(err); } + return done(null, messages); + }); +} module.exports = { getSchedulesIds, checkSchedulesIds, processSchedules, processSchedulesFromConfig, - sendNotification + sendNotification, }; diff --git a/test/config-test.js b/test/config-test.js index 6473853..3aee28f 100644 --- a/test/config-test.js +++ b/test/config-test.js @@ -1,24 +1,16 @@ -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ -const { assert } = require('chai'); -const config = require('../src/config'); -const nconf = require('nconf'); -const debug = require('debug')('pagerduty-overrides:tests'); - -describe('Get config for PagerDuty Overrides', function() { +const { assert } = require('chai'); +const config = require('../src/config'); +const nconf = require('nconf'); +describe('Get config for PagerDuty Overrides', () => { it('NODE_ENV isn\'t set', () => assert.equal(nconf.get('NODE_ENV'), undefined)); return it('PAGERDUTY_READ_ONLY_TOKEN isn\'t set', () => assert.equal(nconf.get('PAGERDUTY_READ_ONLY_TOKEN'), undefined)); }); -describe('Setup config and get config', function() { - +describe('Setup config and get config', () => { before(done => - config.setupConfig(__dirname + '/fixtures/config.json', err => done(err)) + config.setupConfig(`${__dirname}/fixtures/config.json`, err => done(err)) ); it('NODE_ENV isn\'t set', () => assert.equal(nconf.get('NODE_ENV'), undefined)); diff --git a/test/notify-test.js b/test/notify-test.js index f488745..673b3e6 100644 --- a/test/notify-test.js +++ b/test/notify-test.js @@ -1,44 +1,37 @@ -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ -const { assert } = require('chai'); -const nock = require('nock'); -const nconf = require('nconf'); -const debug = require('debug')('pagerduty-overrides:tests'); +const { assert } = require('chai'); +const nock = require('nock'); +const nconf = require('nconf'); -const config = require('../src/config'); -const notify = require('../src/notify'); +const config = require('../src/config'); +const notify = require('../src/notify'); -const configPath = __dirname + '/fixtures/config.json'; +const configPath = `${__dirname}/fixtures/config.json`; nock.disableNetConnect(); // https://github.com/chenka/node-slackr/blob/master/test/index.coffee -describe('Test send message using notify.send for both', function() { - +describe('Test send message using notify.send for both', () => { let actual = null; - before(function(done) { + before((done) => { const overlapDate = new Date(); let message = { user: 'Test user', userId: '1234', schedules: ['TEST1', 'TEST2'], date: overlapDate, - crossDate: overlapDate + crossDate: overlapDate, }; const expectBody = { - text:`Following overlaps found:\n*Test user:* \`TEST1\` and \`TEST2\` (the first starting on ${overlapDate.toUTCString()}, the second on ${overlapDate.toUTCString()})\n`, - channel:"#channel-name" + text: `Following overlaps found:\n*Test user:* \`TEST1\` and \`TEST2\` (the first starting on ${overlapDate.toUTCString()}, the second on ${overlapDate.toUTCString()})\n`, + channel: '#channel-name', }; - return config.setupConfig(configPath, function(err) { - if (err) { return done(err); } + return config.setupConfig(configPath, (configErr) => { + if (configErr) { return done(configErr); } nock('https://incomingUrl') - .post("/", expectBody) + .post('/', expectBody) .query(true) .reply(200, 'ok'); @@ -48,16 +41,16 @@ describe('Test send message using notify.send for both', function() { .reply(200, 'ok'); const configSchedules = nconf.get('SCHEDULES'); - const options = configSchedules[0]['NOTIFICATIONS']; + const options = configSchedules[0].NOTIFICATIONS; message = { user: 'Test user', userId: '1234', schedules: ['TEST1', 'TEST2'], date: overlapDate, - crossDate: overlapDate + crossDate: overlapDate, }; - return notify.send(options, [ message ], function(err, result) { + return notify.send(options, [message], (err, result) => { if (err) { return done(err); } actual = result; return done(); diff --git a/test/pagerduty-test.js b/test/pagerduty-test.js index 6e9c586..03fbb98 100644 --- a/test/pagerduty-test.js +++ b/test/pagerduty-test.js @@ -1,36 +1,31 @@ -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS205: Consider reworking code to avoid use of IIFEs - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ -const { assert } = require('chai'); -const nock = require('nock'); -const nconf = require('nconf'); -const debug = require('debug')('pagerduty-overrides:tests'); - -const config = require('../src/config'); -const pd = require('../src/pagerduty'); - -const configPath = __dirname + '/fixtures/config.json'; -const configWithDaysPath = __dirname + '/fixtures/config-days.json'; -const configWrongPath = __dirname + '/fixtures/config-wrong.json'; +const { assert } = require('chai'); +const nock = require('nock'); +const debug = require('debug')('pagerduty-overrides:tests'); + +const config = require('../src/config'); +const pd = require('../src/pagerduty'); + +const configPath = `${__dirname}/fixtures/config.json`; +const configWithDaysPath = `${__dirname}/fixtures/config-days.json`; +const configWrongPath = `${__dirname}/fixtures/config-wrong.json`; + +const incident = require('./fixtures/incident.json'); +const incident2 = require('./fixtures/incident2.json'); nock.disableNetConnect(); -describe('Get schedules Ids', function() { +describe('Get schedules Ids', () => { let schedules = null; before(done => - config.setupConfig(configPath, function(err) { - if (err) { return done(err); } + config.setupConfig(configPath, (configErr) => { + if (configErr) { return done(configErr); } nock('https://api.pagerduty.com/') .get('/schedules') .query(true) - .replyWithFile(200, __dirname + '/fixtures/schedules.json'); + .replyWithFile(200, `${__dirname}/fixtures/schedules.json`); - return pd.getSchedulesIds(function(err, schedulesIds) { + return pd.getSchedulesIds((err, schedulesIds) => { schedules = schedulesIds; return done(err); }); @@ -40,19 +35,19 @@ describe('Get schedules Ids', function() { return it('Check how many schedules', () => assert.equal(schedules.length, 2)); }); -describe('Check schedules', function() { +describe('Check schedules', () => { let schedules = null; before(done => - config.setupConfig(configPath, function(err) { - if (err) { return done(err); } + config.setupConfig(configPath, (configErr) => { + if (configErr) { return done(configErr); } nock('https://api.pagerduty.com/') .get('/schedules') .query(true) - .replyWithFile(200, __dirname + '/fixtures/schedules.json'); + .replyWithFile(200, `${__dirname}/fixtures/schedules.json`); - return pd.checkSchedulesIds(function(err, res) { + return pd.checkSchedulesIds((err, res) => { schedules = res; return done(err); }); @@ -62,19 +57,19 @@ describe('Check schedules', function() { return it('Check if config ids are in pagerduty schedules', () => assert.ok(schedules)); }); -describe('Check schedules with wrong config', function() { +describe('Check schedules with wrong config', () => { let schedules = null; before(done => - config.setupConfig(configWrongPath, function(err) { - if (err) { return done(err); } + config.setupConfig(configWrongPath, (configErr) => { + if (configErr) { return done(configErr); } nock('https://api.pagerduty.com/') .get('/schedules') .query(true) - .replyWithFile(200, __dirname + '/fixtures/schedules.json'); + .replyWithFile(200, `${__dirname}/fixtures/schedules.json`); - return pd.checkSchedulesIds(function(err, res) { + return pd.checkSchedulesIds((err, res) => { schedules = res; return done(err); }); @@ -84,46 +79,45 @@ describe('Check schedules with wrong config', function() { return it('Check if config ids are in pagerduty schedules', () => assert.notOk(schedules)); }); -describe('Compare schedules', function() { - +describe('Compare schedules', () => { let message = null; before(done => - config.setupConfig(configPath, function(err) { - if (err) { return done(err); } + config.setupConfig(configPath, (configErr) => { + if (configErr) { return done(configErr); } nock('https://api.pagerduty.com/') .get('/schedules') .query(true) - .replyWithFile(200, __dirname + '/fixtures/schedules.json'); + .replyWithFile(200, `${__dirname}/fixtures/schedules.json`); nock('https://api.pagerduty.com/') .get('/oncalls') .query(true) - .replyWithFile(200, __dirname + '/fixtures/entries.json'); + .replyWithFile(200, `${__dirname}/fixtures/entries.json`); nock('https://api.pagerduty.com/') .get('/oncalls') .query(true) - .replyWithFile(200, __dirname + '/fixtures/entries.json'); + .replyWithFile(200, `${__dirname}/fixtures/entries.json`); nock('https://api.pagerduty.com/') - .post('/incidents', require('./fixtures/incident.json')) + .post('/incidents', incident) .query(true) .reply(200, 'ok'); nock('https://api.pagerduty.com/') - .post('/incidents', require('./fixtures/incident2.json')) + .post('/incidents', incident2) .query(true) .reply(200, 'ok'); - nock('https://incomingUrl/').post("/").reply(200, 'ok'); + nock('https://incomingUrl/').post('/').reply(200, 'ok'); - return pd.checkSchedulesIds(function(err, res) { - if (err) { return done(err); } + return pd.checkSchedulesIds((checkErr, res) => { + if (checkErr) { return done(checkErr); } if (!res) { - return done(new Error("Check failed")); + return done(new Error('Check failed')); } - return pd.processSchedulesFromConfig(function(err, msg) { + return pd.processSchedulesFromConfig((err, msg) => { if (err) { return done(err); } message = msg; return done(err); @@ -132,7 +126,7 @@ describe('Compare schedules', function() { }) ); - it('Check if there are 2 returned messages', function() { + it('Check if there are 2 returned messages', () => { assert.isArray(message); return assert.lengthOf(message, 2); }); @@ -140,53 +134,52 @@ describe('Compare schedules', function() { return it('Check returned messages if they contain "Primary and Secondary"', () => (() => { const result = []; - for (let singleMessage of Array.from(message)) { + message.forEach((singleMessage) => { debug(singleMessage); assert.isObject(singleMessage); - assert.include(singleMessage.schedules, "Primary"); - result.push(assert.include(singleMessage.schedules, "Secondary")); - } + assert.include(singleMessage.schedules, 'Primary'); + result.push(assert.include(singleMessage.schedules, 'Secondary')); + }); return result; })() ); }); -describe('Compare schedules on specific days', function() { - +describe('Compare schedules on specific days', () => { let message = null; before(done => - config.setupConfig(configWithDaysPath, function(err) { - if (err) { return done(err); } + config.setupConfig(configWithDaysPath, (configErr) => { + if (configErr) { return done(configErr); } nock('https://api.pagerduty.com/') .get('/schedules') .query(true) - .replyWithFile(200, __dirname + '/fixtures/schedules.json'); + .replyWithFile(200, `${__dirname}/fixtures/schedules.json`); nock('https://api.pagerduty.com/') .get('/oncalls') .query(true) - .replyWithFile(200, __dirname + '/fixtures/entries-days.json'); + .replyWithFile(200, `${__dirname}/fixtures/entries-days.json`); nock('https://api.pagerduty.com/') .get('/oncalls') .query(true) - .replyWithFile(200, __dirname + '/fixtures/entries-days.json'); + .replyWithFile(200, `${__dirname}/fixtures/entries-days.json`); nock('https://api.pagerduty.com/') - .post('/incidents', require('./fixtures/incident.json')) + .post('/incidents', incident) .query(true) .reply(200, 'ok'); - nock('https://incomingUrl/').post("/").reply(200, 'ok'); + nock('https://incomingUrl/').post('/').reply(200, 'ok'); - return pd.checkSchedulesIds(function(err, res) { - if (err) { return done(err); } + return pd.checkSchedulesIds((checkErr, res) => { + if (checkErr) { return done(checkErr); } if (!res) { - return done(new Error("Check failed")); + return done(new Error('Check failed')); } - return pd.processSchedulesFromConfig(function(err, msg) { + return pd.processSchedulesFromConfig((err, msg) => { if (err) { return done(err); } message = msg; return done(err); @@ -195,62 +188,57 @@ describe('Compare schedules on specific days', function() { }) ); - it('Check if there is 1 returned message', function() { + it('Check if there is 1 returned message', () => { assert.isArray(message); return assert.lengthOf(message, 1); }); - return it('Check if the returned message contains "Primary and Secondary"', () => - (() => { - const result = []; - for (let singleMessage of Array.from(message)) { - debug(singleMessage); - assert.isObject(singleMessage); - assert.include(singleMessage.schedules, "Primary"); - result.push(assert.include(singleMessage.schedules, "Secondary")); - } - return result; - })() - ); + it('Check if the returned message contains "Primary and Secondary"', () => { + message.forEach((singleMessage) => { + debug(singleMessage); + assert.isObject(singleMessage); + assert.include(singleMessage.schedules, 'Primary'); + assert.include(singleMessage.schedules, 'Secondary'); + }); + }); }); -describe('Compare schedules with no overlap', function() { - +describe('Compare schedules with no overlap', () => { let message = null; - before(done => - config.setupConfig(configPath, function(err) { - if (err) { return done(err); } + before((done) => { + config.setupConfig(configPath, (configErr) => { + if (configErr) { return done(configErr); } nock('https://api.pagerduty.com/') - .get('/schedules') - .query(true) - .replyWithFile(200, __dirname + '/fixtures/schedules.json'); + .get('/schedules') + .query(true) + .replyWithFile(200, `${__dirname}/fixtures/schedules.json`); nock('https://api.pagerduty.com/') - .get('/oncalls') - .query(true) - .replyWithFile(200, __dirname + '/fixtures/entries.json'); + .get('/oncalls') + .query(true) + .replyWithFile(200, `${__dirname}/fixtures/entries.json`); nock('https://api.pagerduty.com/') - .get('/oncalls') - .query(true) - .replyWithFile(200, __dirname + '/fixtures/entries-no-overlap.json'); + .get('/oncalls') + .query(true) + .replyWithFile(200, `${__dirname}/fixtures/entries-no-overlap.json`); - return pd.checkSchedulesIds(function(err, res) { - if (err) { return done(err); } + return pd.checkSchedulesIds((checkErr, res) => { + if (checkErr) { return done(checkErr); } if (!res) { - return done(new Error("Check failed")); + return done(new Error('Check failed')); } - return pd.processSchedulesFromConfig(function(err, msg) { + return pd.processSchedulesFromConfig((err, msg) => { if (err) { return done(err); } message = msg; return done(err); }); }); - }) - ); + }); + }); - return it('Check that there are no returned messages', function() { + return it('Check that there are no returned messages', () => { assert.isArray(message); return assert.isEmpty(message); }); From ed59babfdabb729e17562244221896a148d94e8e Mon Sep 17 00:00:00 2001 From: miiila Date: Thu, 14 Sep 2017 17:08:33 +0200 Subject: [PATCH 4/9] chore: remove node 5 support --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index abfeede..40e88a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: node_js node_js: - - "5" - "6" - node before_script: From 518dc06a83fcd27bdbd5325f5964abf9adea1246 Mon Sep 17 00:00:00 2001 From: miiila Date: Thu, 14 Sep 2017 17:33:02 +0200 Subject: [PATCH 5/9] test: move to eslint --- .eslintrc.js | 19 +++++++++ coffeelint.json | 103 ------------------------------------------------ package.json | 8 +++- 3 files changed, 25 insertions(+), 105 deletions(-) create mode 100644 .eslintrc.js delete mode 100644 coffeelint.json diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..b07f377 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,19 @@ +module.exports = { + 'extends': 'airbnb', + 'env': { + 'node': true, + 'mocha': true, + }, + 'rules': { + 'no-console': 0, + 'import/no-amd': 0, + 'import/no-extraneous-dependencies': 0, + 'comma-dangle': ['error', { + arrays: 'always-multiline', + objects: 'always-multiline', + imports: 'always-multiline', + exports: 'always-multiline', + functions: 'never', // This is not supported in Node without Babel transform + }], + }, +}; diff --git a/coffeelint.json b/coffeelint.json deleted file mode 100644 index fed5713..0000000 --- a/coffeelint.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "arrow_spacing": { - "level": "ignore" - }, - "camel_case_classes": { - "level": "error" - }, - "coffeescript_error": { - "level": "error" - }, - "colon_assignment_spacing": { - "level": "ignore", - "spacing": { - "left": 0, - "right": 0 - } - }, - "cyclomatic_complexity": { - "value": 10, - "level": "ignore" - }, - "duplicate_key": { - "level": "error" - }, - "empty_constructor_needs_parens": { - "level": "ignore" - }, - "indentation": { - "value": 2, - "level": "error" - }, - "line_endings": { - "level": "ignore", - "value": "unix" - }, - "max_line_length": { - "value": 500, - "level": "warn", - "limitComments": true - }, - "missing_fat_arrows": { - "level": "ignore" - }, - "newlines_after_classes": { - "value": 3, - "level": "ignore" - }, - "no_backticks": { - "level": "error" - }, - "no_debugger": { - "level": "warn" - }, - "no_empty_functions": { - "level": "ignore" - }, - "no_empty_param_list": { - "level": "ignore" - }, - "no_implicit_braces": { - "level": "ignore", - "strict": true - }, - "no_implicit_parens": { - "strict": true, - "level": "ignore" - }, - "no_interpolation_in_single_quotes": { - "level": "ignore" - }, - "no_plusplus": { - "level": "ignore" - }, - "no_stand_alone_at": { - "level": "ignore" - }, - "no_tabs": { - "level": "error" - }, - "no_throwing_strings": { - "level": "error" - }, - "no_trailing_semicolons": { - "level": "error" - }, - "no_trailing_whitespace": { - "level": "error", - "allowed_in_comments": false, - "allowed_in_empty_lines": true - }, - "no_unnecessary_double_quotes": { - "level": "ignore" - }, - "no_unnecessary_fat_arrows": { - "level": "warn" - }, - "non_empty_constructor_needs_parens": { - "level": "ignore" - }, - "space_operators": { - "level": "ignore" - } -} diff --git a/package.json b/package.json index ab7762a..e8f66a4 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts": { "test": "./node_modules/.bin/mocha --compilers \"coffee:coffee-script/register\"", "integration": "./node_modules/.bin/mocha --compilers \"coffee:coffee-script/register\" --recursive", - "lint": "conventional-changelog-lint --from=master && coffeelint ./src", + "lint": "conventional-changelog-lint --from=master && eslint ./src", "compile": "coffee -b -c -o lib/ src/", "pretest": "npm run compile", "prepublish": "npm run compile", @@ -42,9 +42,13 @@ "chai": "", "coffee-coverage": "^0.7.0", "coffee-script": "1.10.0", - "coffeelint": "^1.9.2", "conventional-changelog-lint": "^1.1.9", "coveralls": "~2.11.2", + "eslint": "^4.6.1", + "eslint-config-airbnb": "^15.1.0", + "eslint-plugin-import": "^2.7.0", + "eslint-plugin-jsx-a11y": "^5.1.1", + "eslint-plugin-react": "^7.3.0", "mocha": "", "mocha-lcov-reporter": "1.0.0", "nock": "7.2.2", From 11a8a1e784a83f1a82d182c55be788cb7ae1e333 Mon Sep 17 00:00:00 2001 From: miiila Date: Thu, 14 Sep 2017 18:37:16 +0200 Subject: [PATCH 6/9] chore: remove coffee completely + remove coveralls temporarily --- .editorconfig | 12 ++++++++++++ .gitignore | 1 - .npmignore | 3 --- .travis.yml | 2 +- package.json | 10 ++-------- scripts/build | 6 ------ test/mocha.opts | 1 - 7 files changed, 15 insertions(+), 20 deletions(-) create mode 100644 .editorconfig delete mode 100755 scripts/build diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..23aeb97 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +; EditorConfig file: http://EditorConfig.org +; Install the "EditorConfig" plugin into your editor to use + +root = true + +[*] +charset = utf-8 +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + diff --git a/.gitignore b/.gitignore index b7cda54..3d1f9b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ node_modules /npm-debug.log *.DS_Store -lib/ *.log !test/fixtures/config.json config.json diff --git a/.npmignore b/.npmignore index 74b0c5f..56b7d34 100644 --- a/.npmignore +++ b/.npmignore @@ -1,9 +1,6 @@ -src scripts test cov.info README.md -example.coffee config.json -coffeelint.json .travis.yml diff --git a/.travis.yml b/.travis.yml index 40e88a6..d3ff175 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ before_install: # so 'conventional-changelog-lint' could compare commits and lint them: marionebl/conventional-changelog-lint#7 - "git remote set-branches origin master && git fetch && git checkout master && git checkout -" after_success: - - "npm run coveralls || true" +# - "npm run coveralls || true"; @fixme: tempory commenting out, until coveralls flow for JS will be solved - "npm run semantic-release || true" env: global: diff --git a/package.json b/package.json index e8f66a4..f093de7 100644 --- a/package.json +++ b/package.json @@ -2,17 +2,13 @@ "name": "pagerduty-overlap-checker", "version": "0.0.0-semantically-released", "description": "PagerDuty Overlap Duties Checker", - "main": "lib/", + "main": "src/", "bin": { "pdoverrides": "bin/pdoverrides" }, "scripts": { - "test": "./node_modules/.bin/mocha --compilers \"coffee:coffee-script/register\"", - "integration": "./node_modules/.bin/mocha --compilers \"coffee:coffee-script/register\" --recursive", + "test": "./node_modules/.bin/mocha", "lint": "conventional-changelog-lint --from=master && eslint ./src", - "compile": "coffee -b -c -o lib/ src/", - "pretest": "npm run compile", - "prepublish": "npm run compile", "coverage": "./scripts/cov", "coveralls": "npm run coverage && cat ./cov.info | ./node_modules/coveralls/bin/coveralls.js", "semantic-release": "semantic-release pre && npm publish && semantic-release post" @@ -40,8 +36,6 @@ }, "devDependencies": { "chai": "", - "coffee-coverage": "^0.7.0", - "coffee-script": "1.10.0", "conventional-changelog-lint": "^1.1.9", "coveralls": "~2.11.2", "eslint": "^4.6.1", diff --git a/scripts/build b/scripts/build deleted file mode 100755 index 66814d8..0000000 --- a/scripts/build +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -if [ -d "src/" ]; then - rm -fr lib/ - node_modules/.bin/coffee -b -c -o lib/ src/ -fi diff --git a/test/mocha.opts b/test/mocha.opts index 8b574a1..52f04a7 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,3 +1,2 @@ ---compilers=coffee:coffee-script/register --reporter=spec --timeout=12000 From 66e81d34e30d6db3717eaad5ef17e8f6d5920b7b Mon Sep 17 00:00:00 2001 From: miiila Date: Tue, 26 Sep 2017 15:05:32 +0200 Subject: [PATCH 7/9] chore: addressing code review comments --- .npmignore | 1 + README.md | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.npmignore b/.npmignore index 56b7d34..ad52e86 100644 --- a/.npmignore +++ b/.npmignore @@ -4,3 +4,4 @@ cov.info README.md config.json .travis.yml +example.js diff --git a/README.md b/README.md index 218b601..0138109 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ # NodeJS supported versions -- 5.x - 6.x (LTS) # Pager Duty Overrides Checker From 88ebc7ceabb1f1ab0ea3078f0bef4dbe838eb3e1 Mon Sep 17 00:00:00 2001 From: miiila Date: Tue, 26 Sep 2017 15:56:29 +0200 Subject: [PATCH 8/9] fix: correcting path and ifs --- bin/pdoverrides | 4 ++-- src/notify.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/pdoverrides b/bin/pdoverrides index ed5a0b6..994caea 100755 --- a/bin/pdoverrides +++ b/bin/pdoverrides @@ -2,9 +2,9 @@ const program = require('commander'); const pjson = require('../package.json'); -const config = require('../lib/config'); +const config = require('../src/config'); const fsAccess = require('fs-access'); -const pd = require('../lib/pagerduty'); +const pd = require('../src/pagerduty'); function runCheck(configFile) { fsAccess(configFile, (err) => { diff --git a/src/notify.js b/src/notify.js index 7705980..34f14a6 100644 --- a/src/notify.js +++ b/src/notify.js @@ -56,7 +56,7 @@ function createPagerDutyIncident(options, message, cb) { let error = err; if (!error && body !== undefined && body.errors !== undefined && body.errors.length > 0) { error = new Error(`INCIDENT_CREATION_FAILED Errors: ${JSON.stringify(body.errors)}`); - } else if (!error && res !== undefined && (res.statusCode !== 200 || res.statusCode !== 201)) { + } else if (!error && res !== undefined && !(res.statusCode === 200 || res.statusCode === 201)) { error = new Error(`INCIDENT_CREATION_FAILED Creating incident failed with status ${res.statusCode}. Returned body: ${JSON.stringify(body)}`); } if (error) { @@ -142,7 +142,7 @@ function send(options, message, cb) { return next(); }, function sendPagerDuty(next) { - if ((!options.PAGERDUTY && !options.PAGERDUTY.PAGERDUTY_TOKEN) || !options.PAGERDUTY_TOKEN) { + if (!((options.PAGERDUTY && options.PAGERDUTY.PAGERDUTY_TOKEN) || options.PAGERDUTY_TOKEN)) { debug('No PAGERDUTY token defined'); } else if (options.PAGERDUTY.PAGERDUTY_SERVICE_ID && options.PAGERDUTY.PAGERDUTY_FROM) { debug('Found PD token - creating an incident'); From e4e3bf2d87e1e94b19fb6da197317b5d55615e4c Mon Sep 17 00:00:00 2001 From: miiila Date: Tue, 26 Sep 2017 16:51:25 +0200 Subject: [PATCH 9/9] refactor: shorter and more readable variable validation --- src/notify.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/notify.js b/src/notify.js index 34f14a6..670d75f 100644 --- a/src/notify.js +++ b/src/notify.js @@ -54,9 +54,9 @@ function createPagerDutyIncident(options, message, cb) { return pdApi.send('/incidents', incidentOptions, (err, res, body) => { let error = err; - if (!error && body !== undefined && body.errors !== undefined && body.errors.length > 0) { + if (!error && body && body.errors && body.errors.length > 0) { error = new Error(`INCIDENT_CREATION_FAILED Errors: ${JSON.stringify(body.errors)}`); - } else if (!error && res !== undefined && !(res.statusCode === 200 || res.statusCode === 201)) { + } else if (!error && res && !(res.statusCode === 200 || res.statusCode === 201)) { error = new Error(`INCIDENT_CREATION_FAILED Creating incident failed with status ${res.statusCode}. Returned body: ${JSON.stringify(body)}`); } if (error) {