mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 06:37:23 +01:00
Webhooks v2 (and other fixes) (#10265)
* begin implementing global webhooks * add checklist item scored webhook * add pet hatched and mount raised webhooks (no tests) * fix typo * add lvl up webhooks, remove corrupt notifications and reorganize pre-save hook * fix typo * add some tests, globalActivity webhook * fix bug in global activiy webhook and add more tests * add tests and fix typo for petHatched and mountRaised webhooks * fix errors and add tests for level up webhook * wip: add default data to all webhooks, change signature for WebhookSender.send (missing tests) * remove unused code * fix unit tests * fix chat webhooks * remove console * fix lint * add and fix webhook tests * add questStarted webhook and questActivity type * add unit tests * add finial tests and features
This commit is contained in:
@@ -4,11 +4,16 @@ import {
|
||||
taskScoredWebhook,
|
||||
groupChatReceivedWebhook,
|
||||
taskActivityWebhook,
|
||||
questActivityWebhook,
|
||||
userActivityWebhook,
|
||||
} from '../../../../../website/server/libs/webhook';
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-unit.helper.js';
|
||||
import { defer } from '../../../../helpers/api-unit.helper';
|
||||
|
||||
describe('webhooks', () => {
|
||||
let webhooks;
|
||||
let webhooks, user;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox.stub(got, 'post').returns(defer().promise);
|
||||
@@ -23,6 +28,26 @@ describe('webhooks', () => {
|
||||
updated: true,
|
||||
deleted: true,
|
||||
scored: true,
|
||||
checklistScored: true,
|
||||
},
|
||||
}, {
|
||||
id: 'questActivity',
|
||||
url: 'http://quest-activity.com',
|
||||
enabled: true,
|
||||
type: 'questActivity',
|
||||
options: {
|
||||
questStarted: true,
|
||||
questFinised: true,
|
||||
},
|
||||
}, {
|
||||
id: 'userActivity',
|
||||
url: 'http://user-activity.com',
|
||||
enabled: true,
|
||||
type: 'userActivity',
|
||||
options: {
|
||||
petHatched: true,
|
||||
mountRaised: true,
|
||||
leveledUp: true,
|
||||
},
|
||||
}, {
|
||||
id: 'groupChatReceived',
|
||||
@@ -33,6 +58,9 @@ describe('webhooks', () => {
|
||||
groupId: 'group-id',
|
||||
},
|
||||
}];
|
||||
|
||||
user = generateUser();
|
||||
user.webhooks = webhooks;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -57,7 +85,8 @@ describe('webhooks', () => {
|
||||
|
||||
let body = { foo: 'bar' };
|
||||
|
||||
sendWebhook.send([{id: 'custom-webhook', url: 'http://custom-url.com', enabled: true, type: 'custom'}], body);
|
||||
user.webhooks = [{id: 'custom-webhook', url: 'http://custom-url.com', enabled: true, type: 'custom'}];
|
||||
sendWebhook.send(user, body);
|
||||
|
||||
expect(WebhookSender.defaultTransformData).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledOnce;
|
||||
@@ -67,6 +96,30 @@ describe('webhooks', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('adds default data (user and webhookType) to the body', () => {
|
||||
let sendWebhook = new WebhookSender({
|
||||
type: 'custom',
|
||||
});
|
||||
sandbox.spy(sendWebhook, 'attachDefaultData');
|
||||
|
||||
let body = { foo: 'bar' };
|
||||
|
||||
user.webhooks = [{id: 'custom-webhook', url: 'http://custom-url.com', enabled: true, type: 'custom'}];
|
||||
sendWebhook.send(user, body);
|
||||
|
||||
expect(sendWebhook.attachDefaultData).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch('http://custom-url.com', {
|
||||
json: true,
|
||||
});
|
||||
|
||||
expect(body).to.eql({
|
||||
foo: 'bar',
|
||||
user: {_id: user._id},
|
||||
webhookType: 'custom',
|
||||
});
|
||||
});
|
||||
|
||||
it('can pass in a data transformation function', () => {
|
||||
sandbox.spy(WebhookSender, 'defaultTransformData');
|
||||
let sendWebhook = new WebhookSender({
|
||||
@@ -80,7 +133,8 @@ describe('webhooks', () => {
|
||||
|
||||
let body = { foo: 'bar' };
|
||||
|
||||
sendWebhook.send([{id: 'custom-webhook', url: 'http://custom-url.com', enabled: true, type: 'custom'}], body);
|
||||
user.webhooks = [{id: 'custom-webhook', url: 'http://custom-url.com', enabled: true, type: 'custom'}];
|
||||
sendWebhook.send(user, body);
|
||||
|
||||
expect(WebhookSender.defaultTransformData).to.not.be.called;
|
||||
expect(got.post).to.be.calledOnce;
|
||||
@@ -93,7 +147,7 @@ describe('webhooks', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('provieds a default filter function', () => {
|
||||
it('provides a default filter function', () => {
|
||||
sandbox.spy(WebhookSender, 'defaultWebhookFilter');
|
||||
let sendWebhook = new WebhookSender({
|
||||
type: 'custom',
|
||||
@@ -101,7 +155,8 @@ describe('webhooks', () => {
|
||||
|
||||
let body = { foo: 'bar' };
|
||||
|
||||
sendWebhook.send([{id: 'custom-webhook', url: 'http://custom-url.com', enabled: true, type: 'custom'}], body);
|
||||
user.webhooks = [{id: 'custom-webhook', url: 'http://custom-url.com', enabled: true, type: 'custom'}];
|
||||
sendWebhook.send(user, body);
|
||||
|
||||
expect(WebhookSender.defaultWebhookFilter).to.be.calledOnce;
|
||||
});
|
||||
@@ -117,7 +172,8 @@ describe('webhooks', () => {
|
||||
|
||||
let body = { foo: 'bar' };
|
||||
|
||||
sendWebhook.send([{id: 'custom-webhook', url: 'http://custom-url.com', enabled: true, type: 'custom'}], body);
|
||||
user.webhooks = [{id: 'custom-webhook', url: 'http://custom-url.com', enabled: true, type: 'custom'}];
|
||||
sendWebhook.send(user, body);
|
||||
|
||||
expect(WebhookSender.defaultWebhookFilter).to.not.be.called;
|
||||
expect(got.post).to.not.be.called;
|
||||
@@ -134,10 +190,11 @@ describe('webhooks', () => {
|
||||
|
||||
let body = { foo: 'bar' };
|
||||
|
||||
sendWebhook.send([
|
||||
user.webhooks = [
|
||||
{ id: 'custom-webhook', url: 'http://custom-url.com', enabled: true, type: 'custom', options: { foo: 'bar' }},
|
||||
{ id: 'other-custom-webhook', url: 'http://other-custom-url.com', enabled: true, type: 'custom', options: { foo: 'foo' }},
|
||||
], body);
|
||||
];
|
||||
sendWebhook.send(user, body);
|
||||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch('http://custom-url.com');
|
||||
@@ -150,7 +207,8 @@ describe('webhooks', () => {
|
||||
|
||||
let body = { foo: 'bar' };
|
||||
|
||||
sendWebhook.send([{id: 'custom-webhook', url: 'http://custom-url.com', enabled: false, type: 'custom'}], body);
|
||||
user.webhooks = [{id: 'custom-webhook', url: 'http://custom-url.com', enabled: false, type: 'custom'}];
|
||||
sendWebhook.send(user, body);
|
||||
|
||||
expect(got.post).to.not.be.called;
|
||||
});
|
||||
@@ -162,7 +220,8 @@ describe('webhooks', () => {
|
||||
|
||||
let body = { foo: 'bar' };
|
||||
|
||||
sendWebhook.send([{id: 'custom-webhook', url: 'httxp://custom-url!!', enabled: true, type: 'custom'}], body);
|
||||
user.webhooks = [{id: 'custom-webhook', url: 'httxp://custom-url!!!', enabled: true, type: 'custom'}];
|
||||
sendWebhook.send(user, body);
|
||||
|
||||
expect(got.post).to.not.be.called;
|
||||
});
|
||||
@@ -174,10 +233,30 @@ describe('webhooks', () => {
|
||||
|
||||
let body = { foo: 'bar' };
|
||||
|
||||
sendWebhook.send([
|
||||
user.webhooks = [
|
||||
{ id: 'custom-webhook', url: 'http://custom-url.com', enabled: true, type: 'custom'},
|
||||
{ id: 'other-webhook', url: 'http://other-url.com', enabled: true, type: 'other'},
|
||||
], body);
|
||||
];
|
||||
sendWebhook.send(user, body);
|
||||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch('http://custom-url.com', {
|
||||
body,
|
||||
json: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('sends every type of activity to global webhooks', () => {
|
||||
let sendWebhook = new WebhookSender({
|
||||
type: 'custom',
|
||||
});
|
||||
|
||||
let body = { foo: 'bar' };
|
||||
|
||||
user.webhooks = [
|
||||
{ id: 'global-webhook', url: 'http://custom-url.com', enabled: true, type: 'globalActivity'},
|
||||
];
|
||||
sendWebhook.send(user, body);
|
||||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch('http://custom-url.com', {
|
||||
@@ -193,10 +272,11 @@ describe('webhooks', () => {
|
||||
|
||||
let body = { foo: 'bar' };
|
||||
|
||||
sendWebhook.send([
|
||||
user.webhooks = [
|
||||
{ id: 'custom-webhook', url: 'http://custom-url.com', enabled: true, type: 'custom'},
|
||||
{ id: 'other-custom-webhook', url: 'http://other-url.com', enabled: true, type: 'custom'},
|
||||
], body);
|
||||
];
|
||||
sendWebhook.send(user, body);
|
||||
|
||||
expect(got.post).to.be.calledTwice;
|
||||
expect(got.post).to.be.calledWithMatch('http://custom-url.com', {
|
||||
@@ -216,7 +296,6 @@ describe('webhooks', () => {
|
||||
beforeEach(() => {
|
||||
data = {
|
||||
user: {
|
||||
_id: 'user-id',
|
||||
_tmp: {foo: 'bar'},
|
||||
stats: {
|
||||
lvl: 5,
|
||||
@@ -248,15 +327,54 @@ describe('webhooks', () => {
|
||||
});
|
||||
|
||||
it('sends task and stats data', () => {
|
||||
taskScoredWebhook.send(webhooks, data);
|
||||
taskScoredWebhook.send(user, data);
|
||||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch(webhooks[0].url, {
|
||||
json: true,
|
||||
body: {
|
||||
type: 'scored',
|
||||
webhookType: 'taskActivity',
|
||||
user: {
|
||||
_id: 'user-id',
|
||||
_id: user._id,
|
||||
_tmp: {foo: 'bar'},
|
||||
stats: {
|
||||
lvl: 5,
|
||||
int: 10,
|
||||
str: 5,
|
||||
exp: 423,
|
||||
toNextLevel: 40,
|
||||
maxHealth: 50,
|
||||
maxMP: 103,
|
||||
},
|
||||
},
|
||||
task: {
|
||||
text: 'text',
|
||||
},
|
||||
direction: 'up',
|
||||
delta: 176,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('sends task and stats data to globalActivity webhookd', () => {
|
||||
user.webhooks = [{
|
||||
id: 'globalActivity',
|
||||
url: 'http://global-activity.com',
|
||||
enabled: true,
|
||||
type: 'globalActivity',
|
||||
}];
|
||||
|
||||
taskScoredWebhook.send(user, data);
|
||||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch('http://global-activity.com', {
|
||||
json: true,
|
||||
body: {
|
||||
type: 'scored',
|
||||
webhookType: 'taskActivity',
|
||||
user: {
|
||||
_id: user._id,
|
||||
_tmp: {foo: 'bar'},
|
||||
stats: {
|
||||
lvl: 5,
|
||||
@@ -280,7 +398,7 @@ describe('webhooks', () => {
|
||||
it('does not send task scored data if scored option is not true', () => {
|
||||
webhooks[0].options.scored = false;
|
||||
|
||||
taskScoredWebhook.send(webhooks, data);
|
||||
taskScoredWebhook.send(user, data);
|
||||
|
||||
expect(got.post).to.not.be.called;
|
||||
});
|
||||
@@ -301,13 +419,17 @@ describe('webhooks', () => {
|
||||
it(`sends ${type} tasks`, () => {
|
||||
data.type = type;
|
||||
|
||||
taskActivityWebhook.send(webhooks, data);
|
||||
taskActivityWebhook.send(user, data);
|
||||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch(webhooks[0].url, {
|
||||
json: true,
|
||||
body: {
|
||||
type,
|
||||
webhookType: 'taskActivity',
|
||||
user: {
|
||||
_id: user._id,
|
||||
},
|
||||
task: data.task,
|
||||
},
|
||||
});
|
||||
@@ -317,7 +439,142 @@ describe('webhooks', () => {
|
||||
data.type = type;
|
||||
webhooks[0].options[type] = false;
|
||||
|
||||
taskActivityWebhook.send(webhooks, data);
|
||||
taskActivityWebhook.send(user, data);
|
||||
|
||||
expect(got.post).to.not.be.called;
|
||||
});
|
||||
});
|
||||
|
||||
describe('checklistScored', () => {
|
||||
beforeEach(() => {
|
||||
data = {
|
||||
task: {
|
||||
text: 'text',
|
||||
},
|
||||
item: {
|
||||
text: 'item-text',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('sends \'checklistScored\' tasks', () => {
|
||||
data.type = 'checklistScored';
|
||||
|
||||
taskActivityWebhook.send(user, data);
|
||||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch(webhooks[0].url, {
|
||||
json: true,
|
||||
body: {
|
||||
webhookType: 'taskActivity',
|
||||
user: {
|
||||
_id: user._id,
|
||||
},
|
||||
type: data.type,
|
||||
task: data.task,
|
||||
item: data.item,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('does not send task \'checklistScored\' data if \'checklistScored\' option is not true', () => {
|
||||
data.type = 'checklistScored';
|
||||
webhooks[0].options.checklistScored = false;
|
||||
|
||||
taskActivityWebhook.send(user, data);
|
||||
|
||||
expect(got.post).to.not.be.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('userActivityWebhook', () => {
|
||||
let data;
|
||||
|
||||
beforeEach(() => {
|
||||
data = {
|
||||
something: true,
|
||||
};
|
||||
});
|
||||
|
||||
['petHatched', 'mountRaised', 'leveledUp'].forEach((type) => {
|
||||
it(`sends ${type} webhooks`, () => {
|
||||
data.type = type;
|
||||
|
||||
userActivityWebhook.send(user, data);
|
||||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch(webhooks[2].url, {
|
||||
json: true,
|
||||
body: {
|
||||
type,
|
||||
webhookType: 'userActivity',
|
||||
user: {
|
||||
_id: user._id,
|
||||
},
|
||||
something: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`does not send webhook ${type} data if ${type} option is not true`, () => {
|
||||
data.type = type;
|
||||
webhooks[2].options[type] = false;
|
||||
|
||||
userActivityWebhook.send(user, data);
|
||||
|
||||
expect(got.post).to.not.be.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('questActivityWebhook', () => {
|
||||
let data;
|
||||
|
||||
beforeEach(() => {
|
||||
data = {
|
||||
group: {
|
||||
id: 'group-id',
|
||||
name: 'some group',
|
||||
otherData: 'foo',
|
||||
},
|
||||
quest: {
|
||||
key: 'some-key',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
['questStarted', 'questFinised'].forEach((type) => {
|
||||
it(`sends ${type} webhooks`, () => {
|
||||
data.type = type;
|
||||
|
||||
questActivityWebhook.send(user, data);
|
||||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch(webhooks[1].url, {
|
||||
json: true,
|
||||
body: {
|
||||
type,
|
||||
webhookType: 'questActivity',
|
||||
user: {
|
||||
_id: user._id,
|
||||
},
|
||||
group: {
|
||||
id: 'group-id',
|
||||
name: 'some group',
|
||||
},
|
||||
quest: {
|
||||
key: 'some-key',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`does not send webhook ${type} data if ${type} option is not true`, () => {
|
||||
data.type = type;
|
||||
webhooks[1].options[type] = false;
|
||||
|
||||
userActivityWebhook.send(user, data);
|
||||
|
||||
expect(got.post).to.not.be.called;
|
||||
});
|
||||
@@ -338,12 +595,16 @@ describe('webhooks', () => {
|
||||
},
|
||||
};
|
||||
|
||||
groupChatReceivedWebhook.send(webhooks, data);
|
||||
groupChatReceivedWebhook.send(user, data);
|
||||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch(webhooks[webhooks.length - 1].url, {
|
||||
json: true,
|
||||
body: {
|
||||
webhookType: 'groupChatReceived',
|
||||
user: {
|
||||
_id: user._id,
|
||||
},
|
||||
group: {
|
||||
id: 'group-id',
|
||||
name: 'some group',
|
||||
@@ -369,7 +630,7 @@ describe('webhooks', () => {
|
||||
},
|
||||
};
|
||||
|
||||
groupChatReceivedWebhook.send(webhooks, data);
|
||||
groupChatReceivedWebhook.send(user, data);
|
||||
|
||||
expect(got.post).to.not.be.called;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user