diff --git a/gbot.example.yml b/gbot.example.yml index b662d80..9f2d66d 100644 --- a/gbot.example.yml +++ b/gbot.example.yml @@ -16,3 +16,6 @@ unapproved: 24h: ":emoji1:" 12h: ":emoji2:" default: ":emoji3:" + tag: + approvers: false + author: false diff --git a/tasks/Unapproved.js b/tasks/Unapproved.js index 69925dd..e87b52b 100644 --- a/tasks/Unapproved.js +++ b/tasks/Unapproved.js @@ -1,8 +1,7 @@ const _ = require("lodash") -const timeUtils = require("../utils/time") - const BaseCommand = require("./BaseCommand") +const UnapprovedRequestDescription = require("./unapproved/UnapprovedRequestDescription") class Unapproved extends BaseCommand { perform = () => { @@ -23,7 +22,7 @@ class Unapproved extends BaseCommand { __buildMessage = requests => { if (requests.length) { const list = requests.map(this.__buildRequestDescription).join("\n") - const head = "#### Hey, there is a couple of requests waiting for your review" + const head = "#### Hey, there are a couple of requests waiting for your review" return `${head}\n\n${list}` } else { @@ -35,40 +34,9 @@ class Unapproved extends BaseCommand { } __buildRequestDescription = request => { - const updated = new Date(request.updated_at) - const reaction = this.__getEmoji(updated) - - const link = `[${request.title}](${request.web_url})` - const author = `@${request.author.username}` - const project = `[${request.project.name}](${request.project.web_url})` - const unresolvedAuthors = this.__unresolvedAuthorsString(request) - const approvedBy = this.__approvedByString(request) - - let message = [`${reaction} **${link}** (${project}) by **${author}**`] - - if (unresolvedAuthors.length > 0) { - message.push(`unresolved threads by: ${unresolvedAuthors}`) - } - if (approvedBy.length > 0) { - message.push(`already approved by: ${approvedBy}`) - } - - return message.join("\n") - } - - __getEmoji = lastUpdate => { - const emoji = _.get(this.config, "unapproved.emoji", {}) - const interval = new Date().getTime() - lastUpdate.getTime() - - const findEmoji = _.flow( - _.partialRight(_.toPairs), - _.partialRight(_.map, ([key, value]) => [timeUtils.parseInterval(key), value]), - _.partialRight(_.sortBy, ([time]) => -time), - _.partialRight(_.find, ([time]) => time < interval), - _.partialRight(_.last), - ) + const descriptionBuilder = new UnapprovedRequestDescription(request, this.config) - return findEmoji(emoji) || emoji.default || "" + return descriptionBuilder.build() } __getUnapprovedRequests = projectId => this.__getExtendedRequests(projectId) @@ -100,44 +68,6 @@ class Unapproved extends BaseCommand { __appendDiscussions = (project, request) => this.gitlab .discussions(project.id, request.iid) .then(discussions => ({ ...request, discussions })) - - __unresolvedAuthorsString = request => { - return this.__unresolvedAuthorsFor(request).map(author => { - return `@${author.username}` - }).join(", ") - } - - __approvedByString = request => { - return request.approved_by.map(approve => { - const { user } = approve - - return `@${user.username}` - }).join(", ") - } - - __unresolvedAuthorsFor = request => { - const { discussions } = request - - const userNames = _.flow( - _.partialRight( - _.filter, - discussion => discussion.notes.some( - note => note.resolvable && !note.resolved - ) - ), - _.partialRight( - _.map, - discussion => discussion.notes.map(note => note.author) - ), - _.partialRight(_.flatten), - _.partialRight( - _.uniqBy, - author => author.username - ), - ) - - return userNames(discussions) - } } module.exports = Unapproved diff --git a/tasks/unapproved/UnapprovedRequestDescription.js b/tasks/unapproved/UnapprovedRequestDescription.js new file mode 100644 index 0000000..ad9f2eb --- /dev/null +++ b/tasks/unapproved/UnapprovedRequestDescription.js @@ -0,0 +1,108 @@ +const _ = require("lodash") +const timeUtils = require("../../utils/time") +const stringUtils = require("../../utils/strings") + +class UnapprovedRequestDescription { + constructor(request, config) { + this.config = config + this.request = request + } + + build = () => { + const updated = new Date(this.request.updated_at) + const reaction = this.__getEmoji(updated) + + const link = `[${this.request.title}](${this.request.web_url})` + const author = this.__authorString() + const project = `[${this.request.project.name}](${this.request.project.web_url})` + const unresolvedAuthors = this.__unresolvedAuthorsString() + const approvedBy = this.__approvedByString() + + let message = [`${reaction} **${link}** (${project}) by **${author}**`] + + if (unresolvedAuthors.length > 0) { + message.push(`unresolved threads by: ${unresolvedAuthors}`) + } + if (approvedBy.length > 0) { + message.push(`already approved by: ${approvedBy}`) + } + + return message.join("\n") + } + + __getConfigSetting = (settingName, defaultValue = null) => { + return _.get(this.config, settingName, defaultValue) + } + + __getEmoji = lastUpdate => { + const emoji = this.__getConfigSetting("unapproved.emoji", {}) + const interval = new Date().getTime() - lastUpdate.getTime() + + const findEmoji = _.flow( + _.partialRight(_.toPairs), + _.partialRight(_.map, ([key, value]) => [timeUtils.parseInterval(key), value]), + _.partialRight(_.sortBy, ([time]) => -time), + _.partialRight(_.find, ([time]) => time < interval), + _.partialRight(_.last), + ) + + return findEmoji(emoji) || emoji.default || "" + } + + __unresolvedAuthorsString = () => { + return this.__unresolvedAuthorsFor(this.request).map(author => { + return `@${author.username}` + }).join(", ") + } + + __approvedByString = () => { + const tagApprovers = this.__getConfigSetting("unapproved.tag.approvers", false) + + return this.request.approved_by.map(approve => { + const { user } = approve + let message = `@${user.username}` + + if (!tagApprovers) { + message = stringUtils.wrapString(message) + } + + return message + }).join(", ") + } + + __authorString = () => { + const tagAuthor = this.__getConfigSetting("unapproved.tag.author", false) + let message = `@${this.request.author.username}` + + if (!tagAuthor) { + message = stringUtils.wrapString(message) + } + return message + } + + __unresolvedAuthorsFor = () => { + const { discussions } = this.request + + const userNames = _.flow( + _.partialRight( + _.filter, + discussion => discussion.notes.some( + note => note.resolvable && !note.resolved + ) + ), + _.partialRight( + _.map, + discussion => discussion.notes.map(note => note.author) + ), + _.partialRight(_.flatten), + _.partialRight( + _.uniqBy, + author => author.username + ), + ) + + return userNames(discussions) + } +} + +module.exports = UnapprovedRequestDescription diff --git a/utils/strings.js b/utils/strings.js new file mode 100644 index 0000000..eb35760 --- /dev/null +++ b/utils/strings.js @@ -0,0 +1,7 @@ +const wrapString = (string, wrapper = "`") => { + return `${wrapper}${string}${wrapper}` +} + +module.exports = { + wrapString, +}