mirror of
https://github.com/misskey-dev/misskey.git
synced 2024-12-26 17:00:19 +01:00
SystemWebhook側の対処
This commit is contained in:
parent
6f64691219
commit
ef61f24c33
7 changed files with 149 additions and 72 deletions
|
@ -160,22 +160,14 @@ export class AbuseReportNotificationService implements OnApplicationShutdown {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const recipientWebhookIds = await this.fetchWebhookRecipients()
|
return Promise.all(
|
||||||
.then(it => it
|
convertedReports.map(it => {
|
||||||
.filter(it => it.isActive && it.systemWebhookId && it.method === 'webhook')
|
return this.systemWebhookService.enqueueSystemWebhook(
|
||||||
.map(it => it.systemWebhookId)
|
type,
|
||||||
.filter(x => x != null));
|
it,
|
||||||
for (const webhookId of recipientWebhookIds) {
|
);
|
||||||
await Promise.all(
|
}),
|
||||||
convertedReports.map(it => {
|
);
|
||||||
return this.systemWebhookService.enqueueSystemWebhook(
|
|
||||||
webhookId,
|
|
||||||
type,
|
|
||||||
it,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -50,7 +50,6 @@ export type SystemWebhookPayload<T extends SystemWebhookEventType> =
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SystemWebhookService implements OnApplicationShutdown {
|
export class SystemWebhookService implements OnApplicationShutdown {
|
||||||
private logger: Logger;
|
|
||||||
private activeSystemWebhooksFetched = false;
|
private activeSystemWebhooksFetched = false;
|
||||||
private activeSystemWebhooks: MiSystemWebhook[] = [];
|
private activeSystemWebhooks: MiSystemWebhook[] = [];
|
||||||
|
|
||||||
|
@ -62,11 +61,9 @@ export class SystemWebhookService implements OnApplicationShutdown {
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
private queueService: QueueService,
|
private queueService: QueueService,
|
||||||
private moderationLogService: ModerationLogService,
|
private moderationLogService: ModerationLogService,
|
||||||
private loggerService: LoggerService,
|
|
||||||
private globalEventService: GlobalEventService,
|
private globalEventService: GlobalEventService,
|
||||||
) {
|
) {
|
||||||
this.redisForSub.on('message', this.onMessage);
|
this.redisForSub.on('message', this.onMessage);
|
||||||
this.logger = this.loggerService.getLogger('webhook');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
@ -193,28 +190,19 @@ export class SystemWebhookService implements OnApplicationShutdown {
|
||||||
/**
|
/**
|
||||||
* SystemWebhook をWebhook配送キューに追加する
|
* SystemWebhook をWebhook配送キューに追加する
|
||||||
* @see QueueService.systemWebhookDeliver
|
* @see QueueService.systemWebhookDeliver
|
||||||
* // TODO: contentの型を厳格化する
|
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async enqueueSystemWebhook<T extends SystemWebhookEventType>(
|
public async enqueueSystemWebhook<T extends SystemWebhookEventType>(
|
||||||
webhook: MiSystemWebhook | MiSystemWebhook['id'],
|
|
||||||
type: T,
|
type: T,
|
||||||
content: SystemWebhookPayload<T>,
|
content: SystemWebhookPayload<T>,
|
||||||
) {
|
) {
|
||||||
const webhookEntity = typeof webhook === 'string'
|
const webhooks = await this.fetchActiveSystemWebhooks()
|
||||||
? (await this.fetchActiveSystemWebhooks()).find(a => a.id === webhook)
|
.then(webhooks => webhooks.filter(webhook => webhook.on.includes(type)));
|
||||||
: webhook;
|
return Promise.all(
|
||||||
if (!webhookEntity || !webhookEntity.isActive) {
|
webhooks.map(webhook => {
|
||||||
this.logger.info(`SystemWebhook is not active or not found : ${webhook}`);
|
return this.queueService.systemWebhookDeliver(webhook, type, content);
|
||||||
return;
|
}),
|
||||||
}
|
);
|
||||||
|
|
||||||
if (!webhookEntity.on.includes(type)) {
|
|
||||||
this.logger.info(`SystemWebhook ${webhookEntity.id} is not listening to ${type}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.queueService.systemWebhookDeliver(webhookEntity, type, content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
|
|
@ -63,13 +63,6 @@ export class UserService {
|
||||||
@bindThis
|
@bindThis
|
||||||
public async notifySystemWebhook(user: MiUser, type: 'userCreated') {
|
public async notifySystemWebhook(user: MiUser, type: 'userCreated') {
|
||||||
const packedUser = await this.userEntityService.pack(user, null, { schema: 'UserLite' });
|
const packedUser = await this.userEntityService.pack(user, null, { schema: 'UserLite' });
|
||||||
const recipientWebhookIds = await this.systemWebhookService.fetchSystemWebhooks({ isActive: true, on: [type] });
|
return this.systemWebhookService.enqueueSystemWebhook(type, packedUser);
|
||||||
for (const webhookId of recipientWebhookIds) {
|
|
||||||
await this.systemWebhookService.enqueueSystemWebhook(
|
|
||||||
webhookId,
|
|
||||||
type,
|
|
||||||
packedUser,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,15 +231,10 @@ export class CheckModeratorsActivityProcessorService {
|
||||||
|
|
||||||
// -- SystemWebhook
|
// -- SystemWebhook
|
||||||
|
|
||||||
const systemWebhooks = await this.systemWebhookService.fetchActiveSystemWebhooks()
|
return this.systemWebhookService.enqueueSystemWebhook(
|
||||||
.then(it => it.filter(it => it.on.includes('inactiveModeratorsWarning')));
|
'inactiveModeratorsWarning',
|
||||||
for (const systemWebhook of systemWebhooks) {
|
{ remainingTime: remainingTime },
|
||||||
this.systemWebhookService.enqueueSystemWebhook(
|
);
|
||||||
systemWebhook,
|
|
||||||
'inactiveModeratorsWarning',
|
|
||||||
{ remainingTime: remainingTime },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
@ -269,15 +264,10 @@ export class CheckModeratorsActivityProcessorService {
|
||||||
|
|
||||||
// -- SystemWebhook
|
// -- SystemWebhook
|
||||||
|
|
||||||
const systemWebhooks = await this.systemWebhookService.fetchActiveSystemWebhooks()
|
return this.systemWebhookService.enqueueSystemWebhook(
|
||||||
.then(it => it.filter(it => it.on.includes('inactiveModeratorsInvitationOnlyChanged')));
|
'inactiveModeratorsInvitationOnlyChanged',
|
||||||
for (const systemWebhook of systemWebhooks) {
|
{},
|
||||||
this.systemWebhookService.enqueueSystemWebhook(
|
);
|
||||||
systemWebhook,
|
|
||||||
'inactiveModeratorsInvitationOnlyChanged',
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
|
|
@ -314,9 +314,10 @@ describe('SystemWebhookService', () => {
|
||||||
isActive: true,
|
isActive: true,
|
||||||
on: ['abuseReport'],
|
on: ['abuseReport'],
|
||||||
});
|
});
|
||||||
await service.enqueueSystemWebhook(webhook.id, 'abuseReport', { foo: 'bar' } as any);
|
await service.enqueueSystemWebhook('abuseReport', { foo: 'bar' } as any);
|
||||||
|
|
||||||
expect(queueService.systemWebhookDeliver).toHaveBeenCalled();
|
expect(queueService.systemWebhookDeliver).toHaveBeenCalledTimes(1);
|
||||||
|
expect(queueService.systemWebhookDeliver.mock.calls[0][0] as MiSystemWebhook).toEqual(webhook);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('非アクティブなWebhookはキューに追加されない', async () => {
|
test('非アクティブなWebhookはキューに追加されない', async () => {
|
||||||
|
@ -324,7 +325,7 @@ describe('SystemWebhookService', () => {
|
||||||
isActive: false,
|
isActive: false,
|
||||||
on: ['abuseReport'],
|
on: ['abuseReport'],
|
||||||
});
|
});
|
||||||
await service.enqueueSystemWebhook(webhook.id, 'abuseReport', { foo: 'bar' } as any);
|
await service.enqueueSystemWebhook('abuseReport', { foo: 'bar' } as any);
|
||||||
|
|
||||||
expect(queueService.systemWebhookDeliver).not.toHaveBeenCalled();
|
expect(queueService.systemWebhookDeliver).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
@ -338,11 +339,33 @@ describe('SystemWebhookService', () => {
|
||||||
isActive: true,
|
isActive: true,
|
||||||
on: ['abuseReportResolved'],
|
on: ['abuseReportResolved'],
|
||||||
});
|
});
|
||||||
await service.enqueueSystemWebhook(webhook1.id, 'abuseReport', { foo: 'bar' } as any);
|
await service.enqueueSystemWebhook('abuseReport', { foo: 'bar' } as any);
|
||||||
await service.enqueueSystemWebhook(webhook2.id, 'abuseReport', { foo: 'bar' } as any);
|
|
||||||
|
|
||||||
expect(queueService.systemWebhookDeliver).not.toHaveBeenCalled();
|
expect(queueService.systemWebhookDeliver).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('混在した時、有効かつ許可されたイベント種別のみ', async () => {
|
||||||
|
const webhook1 = await createWebhook({
|
||||||
|
isActive: true,
|
||||||
|
on: ['abuseReport'],
|
||||||
|
});
|
||||||
|
const webhook2 = await createWebhook({
|
||||||
|
isActive: true,
|
||||||
|
on: ['abuseReportResolved'],
|
||||||
|
});
|
||||||
|
const webhook3 = await createWebhook({
|
||||||
|
isActive: false,
|
||||||
|
on: ['abuseReport'],
|
||||||
|
});
|
||||||
|
const webhook4 = await createWebhook({
|
||||||
|
isActive: false,
|
||||||
|
on: ['abuseReportResolved'],
|
||||||
|
});
|
||||||
|
await service.enqueueSystemWebhook('abuseReport', { foo: 'bar' } as any);
|
||||||
|
|
||||||
|
expect(queueService.systemWebhookDeliver).toHaveBeenCalledTimes(1);
|
||||||
|
expect(queueService.systemWebhookDeliver.mock.calls[0][0] as MiSystemWebhook).toEqual(webhook1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('fetchActiveSystemWebhooks', () => {
|
describe('fetchActiveSystemWebhooks', () => {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
@ -71,7 +70,7 @@ describe('UserWebhookService', () => {
|
||||||
LoggerService,
|
LoggerService,
|
||||||
GlobalEventService,
|
GlobalEventService,
|
||||||
{
|
{
|
||||||
provide: QueueService, useFactory: () => ({ systemWebhookDeliver: jest.fn() }),
|
provide: QueueService, useFactory: () => ({ userWebhookDeliver: jest.fn() }),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
@ -242,4 +241,92 @@ describe('UserWebhookService', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('アプリを毎回作り直す必要があるグループ', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await beforeAllImpl();
|
||||||
|
await beforeEachImpl();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await afterEachImpl();
|
||||||
|
await afterAllImpl();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('enqueueUserWebhook', () => {
|
||||||
|
test('キューに追加成功', async () => {
|
||||||
|
const webhook = await createWebhook({
|
||||||
|
active: true,
|
||||||
|
on: ['note'],
|
||||||
|
});
|
||||||
|
await service.enqueueUserWebhook(webhook.userId, 'note', { foo: 'bar' } as any);
|
||||||
|
|
||||||
|
expect(queueService.userWebhookDeliver).toHaveBeenCalledTimes(1);
|
||||||
|
expect(queueService.userWebhookDeliver.mock.calls[0][0] as MiWebhook).toEqual(webhook);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('非アクティブなWebhookはキューに追加されない', async () => {
|
||||||
|
const webhook = await createWebhook({
|
||||||
|
active: false,
|
||||||
|
on: ['note'],
|
||||||
|
});
|
||||||
|
await service.enqueueUserWebhook(webhook.userId, 'note', { foo: 'bar' } as any);
|
||||||
|
|
||||||
|
expect(queueService.userWebhookDeliver).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('未許可のイベント種別が渡された場合はWebhookはキューに追加されない', async () => {
|
||||||
|
const webhook1 = await createWebhook({
|
||||||
|
active: true,
|
||||||
|
on: [],
|
||||||
|
});
|
||||||
|
const webhook2 = await createWebhook({
|
||||||
|
active: true,
|
||||||
|
on: ['note'],
|
||||||
|
});
|
||||||
|
await service.enqueueUserWebhook(webhook1.userId, 'renote', { foo: 'bar' } as any);
|
||||||
|
await service.enqueueUserWebhook(webhook2.userId, 'renote', { foo: 'bar' } as any);
|
||||||
|
|
||||||
|
expect(queueService.userWebhookDeliver).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ユーザIDが異なるWebhookはキューに追加されない', async () => {
|
||||||
|
const webhook = await createWebhook({
|
||||||
|
active: true,
|
||||||
|
on: ['note'],
|
||||||
|
});
|
||||||
|
await service.enqueueUserWebhook(idService.gen(), 'note', { foo: 'bar' } as any);
|
||||||
|
|
||||||
|
expect(queueService.userWebhookDeliver).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('混在した時、有効かつ許可されたイベント種別のみ', async () => {
|
||||||
|
const userId = root.id;
|
||||||
|
const webhook1 = await createWebhook({
|
||||||
|
userId,
|
||||||
|
active: true,
|
||||||
|
on: ['note'],
|
||||||
|
});
|
||||||
|
const webhook2 = await createWebhook({
|
||||||
|
userId,
|
||||||
|
active: true,
|
||||||
|
on: ['renote'],
|
||||||
|
});
|
||||||
|
const webhook3 = await createWebhook({
|
||||||
|
userId,
|
||||||
|
active: false,
|
||||||
|
on: ['note'],
|
||||||
|
});
|
||||||
|
const webhook4 = await createWebhook({
|
||||||
|
userId,
|
||||||
|
active: false,
|
||||||
|
on: ['renote'],
|
||||||
|
});
|
||||||
|
await service.enqueueUserWebhook(userId, 'note', { foo: 'bar' } as any);
|
||||||
|
|
||||||
|
expect(queueService.userWebhookDeliver).toHaveBeenCalledTimes(1);
|
||||||
|
expect(queueService.userWebhookDeliver.mock.calls[0][0] as MiWebhook).toEqual(webhook1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { QueueLoggerService } from '@/queue/QueueLoggerService.js';
|
||||||
import { EmailService } from '@/core/EmailService.js';
|
import { EmailService } from '@/core/EmailService.js';
|
||||||
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
|
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
|
||||||
import { AnnouncementService } from '@/core/AnnouncementService.js';
|
import { AnnouncementService } from '@/core/AnnouncementService.js';
|
||||||
|
import { SystemWebhookEventType } from '@/models/SystemWebhook.js';
|
||||||
|
|
||||||
const baseDate = new Date(Date.UTC(2000, 11, 15, 12, 0, 0));
|
const baseDate = new Date(Date.UTC(2000, 11, 15, 12, 0, 0));
|
||||||
|
|
||||||
|
@ -334,9 +335,10 @@ describe('CheckModeratorsActivityProcessorService', () => {
|
||||||
mockModeratorRole([user1]);
|
mockModeratorRole([user1]);
|
||||||
await service.notifyInactiveModeratorsWarning({ time: 1, asDays: 0, asHours: 0 });
|
await service.notifyInactiveModeratorsWarning({ time: 1, asDays: 0, asHours: 0 });
|
||||||
|
|
||||||
expect(systemWebhookService.enqueueSystemWebhook).toHaveBeenCalledTimes(2);
|
// typeとactiveによる絞り込みが機能しているかはSystemWebhookServiceのテストで確認する.
|
||||||
expect(systemWebhookService.enqueueSystemWebhook.mock.calls[0][0]).toEqual(systemWebhook1);
|
// ここでは呼び出されているか、typeが正しいかのみを確認する
|
||||||
expect(systemWebhookService.enqueueSystemWebhook.mock.calls[1][0]).toEqual(systemWebhook2);
|
expect(systemWebhookService.enqueueSystemWebhook).toHaveBeenCalledTimes(1);
|
||||||
|
expect(systemWebhookService.enqueueSystemWebhook.mock.calls[0][0] as SystemWebhookEventType).toEqual('inactiveModeratorsWarning');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -372,8 +374,10 @@ describe('CheckModeratorsActivityProcessorService', () => {
|
||||||
mockModeratorRole([user1]);
|
mockModeratorRole([user1]);
|
||||||
await service.notifyChangeToInvitationOnly();
|
await service.notifyChangeToInvitationOnly();
|
||||||
|
|
||||||
|
// typeとactiveによる絞り込みが機能しているかはSystemWebhookServiceのテストで確認する.
|
||||||
|
// ここでは呼び出されているか、typeが正しいかのみを確認する
|
||||||
expect(systemWebhookService.enqueueSystemWebhook).toHaveBeenCalledTimes(1);
|
expect(systemWebhookService.enqueueSystemWebhook).toHaveBeenCalledTimes(1);
|
||||||
expect(systemWebhookService.enqueueSystemWebhook.mock.calls[0][0]).toEqual(systemWebhook2);
|
expect(systemWebhookService.enqueueSystemWebhook.mock.calls[0][0] as SystemWebhookEventType).toEqual('inactiveModeratorsInvitationOnlyChanged');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue