Added ability to pass minThreshold for Milestone Slack notifications

closes ENG-632

- This listens to a new property in the `milestones` config to set a minimum value of Milestones we wanna use the Slack notification service for
This commit is contained in:
Aileen Booker 2024-02-22 12:51:41 -04:00 committed by Aileen Booker
parent 60d81b2003
commit f16d9802d0
4 changed files with 57 additions and 12 deletions

View File

@ -12,10 +12,11 @@ class SlackNotificationsServiceWrapper {
* @param {string} deps.siteUrl * @param {string} deps.siteUrl
* @param {boolean} deps.isEnabled * @param {boolean} deps.isEnabled
* @param {URL} deps.webhookUrl * @param {URL} deps.webhookUrl
* @param {number} deps.minThreshold
* *
* @returns {import('@tryghost/slack-notifications/lib/SlackNotificationsService')} * @returns {import('@tryghost/slack-notifications/lib/SlackNotificationsService')}
*/ */
static create({siteUrl, isEnabled, webhookUrl}) { static create({siteUrl, isEnabled, webhookUrl, minThreshold}) {
const { const {
SlackNotificationsService, SlackNotificationsService,
SlackNotifications SlackNotifications
@ -32,7 +33,8 @@ class SlackNotificationsServiceWrapper {
logging, logging,
config: { config: {
isEnabled, isEnabled,
webhookUrl webhookUrl,
minThreshold
}, },
slackNotifications slackNotifications
}); });
@ -49,8 +51,9 @@ class SlackNotificationsServiceWrapper {
const siteUrl = urlUtils.getSiteUrl(); const siteUrl = urlUtils.getSiteUrl();
const isEnabled = !!(hostSettings?.milestones?.enabled && hostSettings?.milestones?.url); const isEnabled = !!(hostSettings?.milestones?.enabled && hostSettings?.milestones?.url);
const webhookUrl = hostSettings?.milestones?.url; const webhookUrl = hostSettings?.milestones?.url;
const minThreshold = hostSettings?.milestones?.minThreshold ? parseInt(hostSettings.milestones.minThreshold) : 0;
this.#api = SlackNotificationsServiceWrapper.create({siteUrl, isEnabled, webhookUrl}); this.#api = SlackNotificationsServiceWrapper.create({siteUrl, isEnabled, webhookUrl, minThreshold});
this.#api.subscribeEvents(); this.#api.subscribeEvents();
} }

View File

@ -9,7 +9,7 @@ describe('Slack Notifications Service', function () {
let scope; let scope;
beforeEach(function () { beforeEach(function () {
configUtils.set('hostSettings', {milestones: {enabled: true, url: 'https://testhooks.slack.com/'}}); configUtils.set('hostSettings', {milestones: {enabled: true, url: 'https://testhooks.slack.com/', minThreshold: '100'}});
scope = nock('https://testhooks.slack.com/') scope = nock('https://testhooks.slack.com/')
.post('/') .post('/')
@ -28,13 +28,13 @@ describe('Slack Notifications Service', function () {
milestone: { milestone: {
type: 'arr', type: 'arr',
currency: 'usd', currency: 'usd',
name: 'arr-100-usd', name: 'arr-1000-usd',
value: 100, value: 1000,
createdAt: new Date(), createdAt: new Date(),
emailSentAt: new Date() emailSentAt: new Date()
}, },
meta: { meta: {
currentValue: 105 currentValue: 1005
} }
})); }));

View File

@ -27,6 +27,7 @@ const {MilestoneCreatedEvent} = require('@tryghost/milestones');
* @typedef {object} config * @typedef {object} config
* @prop {boolean} isEnabled * @prop {boolean} isEnabled
* @prop {URL} webhookUrl * @prop {URL} webhookUrl
* @prop {number} minThreshold
*/ */
module.exports = class SlackNotificationsService { module.exports = class SlackNotificationsService {
@ -71,6 +72,7 @@ module.exports = class SlackNotificationsService {
&& event.data.milestone && event.data.milestone
&& this.#config.isEnabled && this.#config.isEnabled
&& this.#config.webhookUrl && this.#config.webhookUrl
&& this.#config.minThreshold < event.data.milestone.value
) { ) {
try { try {
await this.#slackNotifications.notifyMilestoneReceived(event.data); await this.#slackNotifications.notifyMilestoneReceived(event.data);

View File

@ -19,7 +19,8 @@ describe('SlackNotificationsService', function () {
const config = { const config = {
isEnabled: true, isEnabled: true,
webhookUrl: 'https://slack-webhook.example' webhookUrl: 'https://slack-webhook.example',
minThreshold: 1000
}; };
beforeEach(function () { beforeEach(function () {
@ -75,13 +76,13 @@ describe('SlackNotificationsService', function () {
milestone: { milestone: {
id: new ObjectId().toHexString(), id: new ObjectId().toHexString(),
type: 'arr', type: 'arr',
value: 1000, value: 10000,
currency: 'usd', currency: 'usd',
createdAt: new Date(), createdAt: new Date(),
emailSentAt: new Date() emailSentAt: new Date()
}, },
meta: { meta: {
currentValue: 1398 currentValue: 13980
} }
})); }));
@ -118,6 +119,45 @@ describe('SlackNotificationsService', function () {
assert(slackNotificationStub.callCount === 0); assert(slackNotificationStub.callCount === 0);
}); });
it('does not send notification when milestone value below notification threshold', async function () {
service = new SlackNotificationsService({
logging: {
warn: () => {},
error: loggingSpy
},
DomainEvents,
siteUrl: 'https://ghost.example',
config: {
isEnabled: false,
webhookUrl: 'https://slack-webhook.example'
},
slackNotifications: {
notifyMilestoneReceived: slackNotificationStub
}
});
service.subscribeEvents();
DomainEvents.dispatch(MilestoneCreatedEvent.create({
milestone: {
id: new ObjectId().toHexString(),
type: 'arr',
value: 1000,
currency: 'usd',
createdAt: new Date(),
emailSentAt: new Date()
},
meta: {
currentValue: 1398
}
}));
await DomainEvents.allSettled();
assert(loggingSpy.callCount === 0);
assert(slackNotificationStub.callCount === 0);
});
it('does not send notification when no url in hostSettings provided', async function () { it('does not send notification when no url in hostSettings provided', async function () {
service = new SlackNotificationsService({ service = new SlackNotificationsService({
logging: { logging: {
@ -166,8 +206,8 @@ describe('SlackNotificationsService', function () {
DomainEvents.dispatch(MilestoneCreatedEvent.create({ DomainEvents.dispatch(MilestoneCreatedEvent.create({
milestone: { milestone: {
type: 'members', type: 'members',
name: 'members-100', name: 'members-10000',
value: 100, value: 10000,
createdAt: new Date() createdAt: new Date()
} }
})); }));