diff --git a/src/reactions/gitlab/merge-request-opened.ts b/src/reactions/gitlab/merge-request-opened.ts new file mode 100644 index 0000000..0d7ffb3 --- /dev/null +++ b/src/reactions/gitlab/merge-request-opened.ts @@ -0,0 +1,29 @@ +import { Config } from '../../config'; +import { Reaction, ReactionCanHandleOptions, ReactionHandleOptions } from '../github/reaction'; +import { MergeRequestPayload } from '../../schemas/gitlab/merge-request-payload'; + +export class MergeRequestOpened extends Reaction { + getStreamLabsMessage({ payload }: ReactionHandleOptions): string { + return `*${payload.user.username}* just opened a merge request in *${payload.repository.name}*`; + } + + getTwitchChatMessage({ payload }: ReactionHandleOptions): string { + return `${payload.user.username} just opened a merge request in ${payload.repository.homepage}`; + } + + canHandle({ payload, event, config }: ReactionCanHandleOptions): boolean { + return ( + event === 'Merge Request Hook' && + payload.object_attributes.state === 'opened' && + this.isAllowedByConfig(config, payload) + ); + } + + private isAllowedByConfig(config: Config | undefined, payload: MergeRequestPayload): boolean { + return ( + !config || + !config.IGNORE_PR_OPENED_BY.includes(payload.user.username) || + config.IGNORE_PR_OPENED_BY.length === 0 + ); + } +} diff --git a/src/schemas/gitlab/merge-request-payload.ts b/src/schemas/gitlab/merge-request-payload.ts new file mode 100644 index 0000000..cc44015 --- /dev/null +++ b/src/schemas/gitlab/merge-request-payload.ts @@ -0,0 +1,18 @@ +type url = string; + +export interface MergeRequestPayload { + user: { + name: string; + username: string; + avatar_url: url; + }; + repository: { + name: string; + url: url; + description: string; + homepage: url; + }; + object_attributes: { + state: 'merged' | 'created' | 'updated' | 'closed' | 'opened'; + }; +} diff --git a/test/reactions/gitlab/merge-request-opened.spec.ts b/test/reactions/gitlab/merge-request-opened.spec.ts new file mode 100644 index 0000000..2af648a --- /dev/null +++ b/test/reactions/gitlab/merge-request-opened.spec.ts @@ -0,0 +1,114 @@ +import { StreamLabsMock } from '../../__mocks__/StreamLabs'; +import { TwitchChatMock } from '../../__mocks__/TwitchChat'; +import { MergeRequestOpened } from '../../../src/reactions/gitlab/merge-request-opened'; +import { Config } from '../../../src/config'; +import { MergeRequestPayload } from '../../../src/schemas/gitlab/merge-request-payload'; + +describe('MergeRequestOpened', () => { + const twitchChat = new TwitchChatMock(); + const streamlabs = new StreamLabsMock(); + + describe('#canHandle', () => { + it("creates a new 'MergeRequestOpened' reaction", () => { + const subject = new MergeRequestOpened(twitchChat, streamlabs); + + expect(subject).not.toBeNull(); + }); + + it("returns true if the event is 'Merge Request Hook' and 'object_attributes.state' is 'opened'", () => { + const subject = new MergeRequestOpened(twitchChat, streamlabs); + + const result = subject.canHandle({ + event: 'Merge Request Hook', + payload: { object_attributes: { state: 'opened' } } as MergeRequestPayload, + }); + + expect(result).toEqual(true); + }); + + it("returns false if the event is 'Fork' and 'object_attributes.state' is 'opened'", () => { + const subject = new MergeRequestOpened(twitchChat, streamlabs); + + const result = subject.canHandle({ + event: 'Fork', + payload: { object_attributes: { state: 'opened' } } as MergeRequestPayload, + }); + + expect(result).toEqual(false); + }); + + it("returns false if the event is 'Merge Request Hook' and 'object_attributes.state' is 'merged'", () => { + const subject = new MergeRequestOpened(twitchChat, streamlabs); + + const result = subject.canHandle({ + event: 'Merge Request Hook', + payload: { object_attributes: { state: 'merged' } } as MergeRequestPayload, + }); + + expect(result).toEqual(false); + }); + + it("returns false if the event is 'Merge Request Hook', 'object_attributes.state' is 'opened' but the opener is in the ignore list", () => { + const subject = new MergeRequestOpened(twitchChat, streamlabs); + + const result = subject.canHandle({ + event: 'Merge Request Hook', + payload: { + object_attributes: { state: 'opened' }, + user: { username: 'SantiMA10' }, + } as MergeRequestPayload, + config: { IGNORE_PR_OPENED_BY: ['SantiMA10'] } as Config, + }); + + expect(result).toEqual(false); + }); + + it("returns true if the event is 'Merge Request Hook', 'object_attributes.state' is 'opened' and the 'IGNORE_PR_OPENED_BY' is empty", () => { + const subject = new MergeRequestOpened(twitchChat, streamlabs); + + const result = subject.canHandle({ + event: 'Merge Request Hook', + payload: { + object_attributes: { state: 'opened' }, + user: { username: 'SantiMA10' }, + } as MergeRequestPayload, + config: { IGNORE_PR_OPENED_BY: [] as string[] } as Config, + }); + + expect(result).toEqual(true); + }); + }); + + describe('#handle', () => { + it('sends the expected message to TwitchChat', async () => { + const subject = new MergeRequestOpened(twitchChat, streamlabs); + const payload = { + object_attributes: { state: 'opened' }, + user: { username: 'SantiMA10' }, + repository: { homepage: 'https://gitlab.com/streamlabs/webhook' }, + } as MergeRequestPayload; + + const { twitchChat: response } = await subject.handle({ payload }); + + expect(response).toEqual({ + notified: true, + message: `${payload.user.username} just opened a merge request in ${payload.repository.homepage}`, + }); + }); + + it('sends the expected message to StreamLabs', async () => { + const subject = new MergeRequestOpened(twitchChat, streamlabs); + const payload = { + user: { username: 'SantiMA10' }, + repository: { name: 'streamdevs/webhook' }, + } as MergeRequestPayload; + + const { streamlabs: response } = await subject.handle({ payload }); + + expect(response).toEqual({ + notified: true, + message: `*${payload.user.username}* just opened a merge request in *${payload.repository.name}*`, + }); + }); + }); +});