mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-05-21 13:18:53 +00:00
Merge pull request #11707 from cvuorinen/11682-quest-invitation-webhook
Quest invitation webhook
This commit is contained in:
commit
fa9f962e45
7 changed files with 143 additions and 6 deletions
|
|
@ -43,6 +43,7 @@ describe('webhooks', () => {
|
|||
options: {
|
||||
questStarted: true,
|
||||
questFinised: true,
|
||||
questInvited: true,
|
||||
},
|
||||
}, {
|
||||
id: 'userActivity',
|
||||
|
|
@ -576,7 +577,7 @@ describe('webhooks', () => {
|
|||
};
|
||||
});
|
||||
|
||||
['questStarted', 'questFinised'].forEach(type => {
|
||||
['questStarted', 'questFinised', 'questInvited'].forEach(type => {
|
||||
it(`sends ${type} webhooks`, () => {
|
||||
data.type = type;
|
||||
|
||||
|
|
|
|||
|
|
@ -183,6 +183,7 @@ describe('Webhook Model', () => {
|
|||
options: {
|
||||
questStarted: true,
|
||||
questFinished: true,
|
||||
questInvited: true,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
@ -197,6 +198,7 @@ describe('Webhook Model', () => {
|
|||
expect(wh.options).to.eql({
|
||||
questStarted: false,
|
||||
questFinished: false,
|
||||
questInvited: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -210,6 +212,7 @@ describe('Webhook Model', () => {
|
|||
expect(wh.options).to.eql({
|
||||
questStarted: false,
|
||||
questFinished: true,
|
||||
questInvited: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -224,6 +227,7 @@ describe('Webhook Model', () => {
|
|||
expect(wh.options).to.eql({
|
||||
questStarted: true,
|
||||
questFinished: true,
|
||||
questInvited: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { v4 as generateUUID } from 'uuid';
|
|||
import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
server,
|
||||
sleep,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { quests as questScrolls } from '../../../../../website/common/script/content/quests';
|
||||
|
|
@ -210,5 +211,39 @@ describe('POST /groups/:groupId/quests/invite/:questKey', () => {
|
|||
const returnedGroup = await groupLeader.get(`/groups/${group._id}`);
|
||||
expect(returnedGroup.chat[0]._meta).to.be.undefined;
|
||||
});
|
||||
|
||||
context('sending quest activity webhooks', () => {
|
||||
before(async () => {
|
||||
await server.start();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await server.close();
|
||||
});
|
||||
|
||||
it('sends quest invited webhook', async () => {
|
||||
const uuid = generateUUID();
|
||||
|
||||
await member.post('/user/webhook', {
|
||||
url: `http://localhost:${server.port}/webhooks/${uuid}`,
|
||||
type: 'questActivity',
|
||||
enabled: true,
|
||||
options: {
|
||||
questInvited: true,
|
||||
},
|
||||
});
|
||||
|
||||
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||
|
||||
await sleep();
|
||||
|
||||
const body = server.getWebhookData(uuid);
|
||||
|
||||
expect(body.type).to.eql('questInvited');
|
||||
expect(body.group.id).to.eql(questingGroup.id);
|
||||
expect(body.group.name).to.eql(questingGroup.name);
|
||||
expect(body.quest.key).to.eql(PET_QUEST);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -226,6 +226,69 @@ describe('POST /user/webhook', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('defaults questActivity options', async () => {
|
||||
body.type = 'questActivity';
|
||||
|
||||
const webhook = await user.post('/user/webhook', body);
|
||||
|
||||
expect(webhook.options).to.eql({
|
||||
questStarted: false,
|
||||
questFinished: false,
|
||||
questInvited: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('can set questActivity options', async () => {
|
||||
body.type = 'questActivity';
|
||||
body.options = {
|
||||
questStarted: true,
|
||||
questFinished: true,
|
||||
questInvited: true,
|
||||
};
|
||||
|
||||
const webhook = await user.post('/user/webhook', body);
|
||||
|
||||
expect(webhook.options).to.eql({
|
||||
questStarted: true,
|
||||
questFinished: true,
|
||||
questInvited: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('discards extra properties in questActivity options', async () => {
|
||||
body.type = 'questActivity';
|
||||
body.options = {
|
||||
questStarted: false,
|
||||
questFinished: true,
|
||||
questInvited: true,
|
||||
foo: 'bar',
|
||||
};
|
||||
|
||||
const webhook = await user.post('/user/webhook', body);
|
||||
|
||||
expect(webhook.options.foo).to.not.exist;
|
||||
expect(webhook.options).to.eql({
|
||||
questStarted: false,
|
||||
questFinished: true,
|
||||
questInvited: true,
|
||||
});
|
||||
});
|
||||
|
||||
['questStarted', 'questFinished', 'questInvited'].forEach(option => {
|
||||
it(`requires questActivity option ${option} to be a boolean`, async () => {
|
||||
body.type = 'questActivity';
|
||||
body.options = {
|
||||
[option]: 'not a boolean',
|
||||
};
|
||||
|
||||
await expect(user.post('/user/webhook', body)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('webhookBooleanOption', { option }),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('discards extra properties in globalActivity options', async () => {
|
||||
body.type = 'globalActivity';
|
||||
body.options = {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import {
|
|||
import common from '../../../common';
|
||||
import { sendNotification as sendPushNotification } from '../../libs/pushNotifications';
|
||||
import apiError from '../../libs/apiError';
|
||||
import { questActivityWebhook } from '../../libs/webhook';
|
||||
|
||||
const questScrolls = common.content.quests;
|
||||
|
||||
|
|
@ -79,7 +80,7 @@ api.inviteToQuest = {
|
|||
'party._id': group._id,
|
||||
_id: { $ne: user._id },
|
||||
})
|
||||
.select('auth.facebook auth.google auth.local preferences.emailNotifications preferences.pushNotifications preferences.language profile.name pushDevices')
|
||||
.select('auth.facebook auth.google auth.local preferences.emailNotifications preferences.pushNotifications preferences.language profile.name pushDevices webhooks')
|
||||
.exec();
|
||||
|
||||
group.markModified('quest');
|
||||
|
|
@ -132,6 +133,13 @@ api.inviteToQuest = {
|
|||
);
|
||||
}
|
||||
|
||||
// Send webhooks
|
||||
questActivityWebhook.send(member, {
|
||||
type: 'questInvited',
|
||||
group,
|
||||
quest,
|
||||
});
|
||||
|
||||
return member.preferences.emailNotifications.invitedQuest !== false;
|
||||
});
|
||||
sendTxnEmail(membersToEmail, `invite-${quest.boss ? 'boss' : 'collection'}-quest`, [
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ const api = {};
|
|||
* @apiParam (Body) {String} [label] A label to remind you what this webhook does
|
||||
* @apiParam (Body) {Boolean} [enabled=true] If the webhook should be enabled
|
||||
* @apiParam (Body) {String="taskActivity","groupChatReceived",
|
||||
"userActivity"} [type="taskActivity"] The webhook's type.
|
||||
* @apiParam (Body) {Object} [options] The webhook's options. Wil differ depending on type.
|
||||
"userActivity","questActivity"} [type="taskActivity"] The webhook's type.
|
||||
* @apiParam (Body) {Object} [options] The webhook's options. Will differ depending on type.
|
||||
* Required for `groupChatReceived` type.
|
||||
* If a webhook supports options, the default values
|
||||
* are displayed in the examples below
|
||||
|
|
@ -63,6 +63,30 @@ const api = {};
|
|||
* "groupId": "required-uuid-of-group"
|
||||
* }
|
||||
* }
|
||||
* @apiParamExample {json} User Activity Example
|
||||
* {
|
||||
* "enabled": true,
|
||||
* "url": "http://some-webhook-url.com",
|
||||
* "label": "My Activity Webhook",
|
||||
* "type": "userActivity",
|
||||
* "options": { // set at least one to true
|
||||
* "petHatched": false, // default
|
||||
* "mountRaised": false, // default
|
||||
* "leveledUp": false, // default
|
||||
* }
|
||||
* }
|
||||
* @apiParamExample {json} Quest Activity Example
|
||||
* {
|
||||
* "enabled": true,
|
||||
* "url": "http://some-webhook-url.com",
|
||||
* "label": "My Quest Webhook",
|
||||
* "type": "questActivity",
|
||||
* "options": { // set at least one to true
|
||||
* "questStarted": false, // default
|
||||
* "questFinished": false, // default
|
||||
* "questInvited": false, // default
|
||||
* }
|
||||
* }
|
||||
* @apiParamExample {json} Minimal Example
|
||||
* {
|
||||
* "url": "http://some-webhook-url.com"
|
||||
|
|
@ -131,8 +155,9 @@ api.getWebhook = {
|
|||
* @apiParam (Body) {String} [url] The webhook's URL
|
||||
* @apiParam (Body) {String} [label] A label to remind you what this webhook does
|
||||
* @apiParam (Body) {Boolean} [enabled] If the webhook should be enabled
|
||||
* @apiParam (Body) {String="taskActivity","groupChatReceived"} [type] The webhook's type.
|
||||
* @apiParam (Body) {Object} [options] The webhook's options. Wil differ depending on type.
|
||||
* @apiParam (Body) {String="taskActivity","groupChatReceived",
|
||||
* "userActivity","questActivity"} [type] The webhook's type.
|
||||
* @apiParam (Body) {Object} [options] The webhook's options. Will differ depending on type.
|
||||
* The options are enumerated in the
|
||||
* [add webhook examples](#api-Webhook-UserAddWebhook).
|
||||
* @apiParamExample {json} Update Enabled and Type Properties
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ const USER_ACTIVITY_DEFAULT_OPTIONS = Object.freeze({
|
|||
const QUEST_ACTIVITY_DEFAULT_OPTIONS = Object.freeze({
|
||||
questStarted: false,
|
||||
questFinished: false,
|
||||
questInvited: false,
|
||||
});
|
||||
|
||||
export const schema = new Schema({
|
||||
|
|
|
|||
Loading…
Reference in a new issue