Skip to content
This repository has been archived by the owner on Jan 29, 2024. It is now read-only.

feat: filter out dependabot notifications #599

Merged
merged 3 commits into from
Apr 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ export const notificationConfig = rcfile('notification-config', {
cwd: join(process.cwd(), 'config'),
configFileName: 'notification-config',
})

// Adding a new github username here all the creation / changes on the PRs
// and Issues from this author will be excluded and not notified.
export const excludedAuthors = ['dependabot']
3 changes: 3 additions & 0 deletions src/graphql.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ export const getActivityById = async ({ graphqlClient, id }) => {
number
title
url
author {
login
}
}
}
}`,
Expand Down
6 changes: 5 additions & 1 deletion src/slackbot.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getRawMessage, formatMessage } from './messages.js'
import { notificationConfig } from './config.js'
import { excludedAuthors, notificationConfig } from './config.js'
import {
getProjectById,
getActivityById,
Expand Down Expand Up @@ -35,6 +35,10 @@ export async function sendNotification({ request, app }) {

const { node } = await getItem(request)

if (excludedAuthors.includes(node.content?.author?.login)) {
return
}

const message = formatMessage({
content_type: getContentType(request),
node,
Expand Down
12 changes: 6 additions & 6 deletions test/fixtures/graphql/getProjectItemByIdResponse.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export default {
export default (user = 'testuser') => ({
node: {
creator: {
url: 'https://github.com/testuser',
login: 'testuser',
url: `https://github.com/${user}`,
login: user,
},
id: 'PVTI_lADOBpWafM4AEgSvzgB_LQk',
project: {
Expand All @@ -23,8 +23,8 @@ export default {
nodes: [],
},
author: {
url: 'https://github.com/testuser',
login: 'testuser',
url: `https://github.com/${user}`,
login: user,
name: 'Test User',
},
id: 'I_kwDOHwYncM5PZJ_7',
Expand Down Expand Up @@ -55,4 +55,4 @@ export default {
],
},
},
}
})
84 changes: 84 additions & 0 deletions test/fixtures/webhook/itemCreatedDependabot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
export default {
action: 'created',
projects_v2_item: {
id: 8156154,
node_id: 'dependabot_node',
project_node_id: 'PVT_kwDOBpWafM4AEgSx_x',
content_node_id: 'I_kwDOHwYncM5PMB7x',
content_type: 'Issue',
creator: {
login: 'dependabot',
id: 29484805,
node_id: 'MDQ6VXNlcjI5NDg0ODA1a',
avatar_url: 'https://avatars.githubusercontent.com/u/29484805?v=4',
gravatar_id: '',
url: 'https://api.github.com/users/some-github-user',
html_url: 'https://github.com/some-github-user',
followers_url: 'https://api.github.com/users/some-github-user/followers',
following_url:
'https://api.github.com/users/some-github-user/following{/other_user}',
gists_url:
'https://api.github.com/users/some-github-user/gists{/gist_id}',
starred_url:
'https://api.github.com/users/some-github-user/starred{/owner}{/repo}',
subscriptions_url:
'https://api.github.com/users/some-github-user/subscriptions',
organizations_url: 'https://api.github.com/users/some-github-user/orgs',
repos_url: 'https://api.github.com/users/some-github-user/repos',
events_url:
'https://api.github.com/users/some-github-user/events{/privacy}',
received_events_url:
'https://api.github.com/users/some-github-user/received_events',
type: 'User',
site_admin: false,
},
created_at: '2022-08-04T12:54:31Z',
updated_at: '2022-08-04T12:54:31Z',
archived_at: null,
},
organization: {
login: 'dev-test-org-3000',
id: 110467708,
node_id: 'O_kgDOBpWafA',
url: 'https://api.github.com/orgs/dev-test-org-3000',
repos_url: 'https://api.github.com/orgs/dev-test-org-3000/repos',
events_url: 'https://api.github.com/orgs/dev-test-org-3000/events',
hooks_url: 'https://api.github.com/orgs/dev-test-org-3000/hooks',
issues_url: 'https://api.github.com/orgs/dev-test-org-3000/issues',
members_url:
'https://api.github.com/orgs/dev-test-org-3000/members{/member}',
public_members_url:
'https://api.github.com/orgs/dev-test-org-3000/public_members{/member}',
avatar_url: 'https://avatars.githubusercontent.com/u/110467708?v=4',
description: null,
},
sender: {
login: 'some-github-user',
id: 29484805,
node_id: 'MDQ6VXNlcjI5NDg0ODA1',
avatar_url: 'https://avatars.githubusercontent.com/u/29484805?v=4',
gravatar_id: '',
url: 'https://api.github.com/users/some-github-user',
html_url: 'https://github.com/some-github-user',
followers_url: 'https://api.github.com/users/some-github-user/followers',
following_url:
'https://api.github.com/users/some-github-user/following{/other_user}',
gists_url: 'https://api.github.com/users/some-github-user/gists{/gist_id}',
starred_url:
'https://api.github.com/users/some-github-user/starred{/owner}{/repo}',
subscriptions_url:
'https://api.github.com/users/some-github-user/subscriptions',
organizations_url: 'https://api.github.com/users/some-github-user/orgs',
repos_url: 'https://api.github.com/users/some-github-user/repos',
events_url:
'https://api.github.com/users/some-github-user/events{/privacy}',
received_events_url:
'https://api.github.com/users/some-github-user/received_events',
type: 'User',
site_admin: false,
},
installation: {
id: 27961269,
node_id: 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMjc5NjEyNjk=',
},
}
163 changes: 154 additions & 9 deletions test/routes/webhook.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { createSignature } from '../../lib/verify-request.js'
import config from '../../src/config.js'
import getProjectItemByIdResponse from '../fixtures/graphql/getProjectItemByIdResponse.js'
import itemCreated from '../fixtures/webhook/itemCreated.js'
import itemCreatedDependabot from '../fixtures/webhook/itemCreatedDependabot.js'
import itemDeleted from '../fixtures/webhook/itemDeleted.js'
import itemMovedNoStatusToTodo from '../fixtures/webhook/itemMovedNoStatusToTodo.js'
import pullRequestCreated from '../fixtures/webhook/pullRequestCreated.js'
Expand Down Expand Up @@ -80,7 +81,7 @@ test('POST /webhook', async t => {
const app = await build(t, {
// the authentication function returns a function
graphqlClient: async () => () => {
return getProjectItemByIdResponse
return getProjectItemByIdResponse()
},
slackApp: async function slackApp() {
return {
Expand Down Expand Up @@ -123,7 +124,8 @@ test('POST /webhook', async t => {
'X-Hub-Signature-256': signature,
},
})
t.equal(slackStub.callCount, 1)
sinon.assert.calledOnce(slackStub)

t.equal(res.statusCode, 200)
})
t.test('item deleted', async t => {
Expand All @@ -139,7 +141,8 @@ test('POST /webhook', async t => {
'X-Hub-Signature-256': signature,
},
})
t.equal(slackStub.callCount, 1)
sinon.assert.calledOnce(slackStub)

t.equal(res.statusCode, 200)
})

Expand Down Expand Up @@ -172,7 +175,8 @@ test('POST /webhook', async t => {
'X-Hub-Signature-256': signature,
},
})
t.equal(slackStub.callCount, 1)
sinon.assert.calledOnce(slackStub)

t.equal(res.statusCode, 200)
})

Expand All @@ -192,7 +196,7 @@ test('POST /webhook', async t => {
'X-Hub-Signature-256': signature,
},
})
t.equal(slackStub.callCount, 0)
sinon.assert.notCalled(slackStub)
t.equal(res.statusCode, 200)
})

Expand All @@ -209,20 +213,160 @@ test('POST /webhook', async t => {
'X-Hub-Signature-256': signature,
},
})
t.equal(slackStub.callCount, 1)
sinon.assert.calledOnce(slackStub)

t.equal(res.statusCode, 200)
})
}
)
t.test(`doesn't do anything if it's a dependabot action`, async t => {
const slackStub = sinon.stub()
const app = await build(t, {
// the authentication function returns a function
graphqlClient: async () => () => {
return getProjectItemByIdResponse('dependabot')
},
slackApp: async function slackApp() {
return {
client: {
chat: {
postMessage: params => {
slackStub()
return params
},
},
},
}
},
})

t.test('item created', async t => {
const signature = createSignature(
itemCreatedDependabot,
config.ORG_WEBHOOK_SECRET
)
const res = await app.inject({
url: '/webhook',
method: 'POST',
body: itemCreatedDependabot,
headers: {
'X-Hub-Signature-256': signature,
},
})
sinon.assert.notCalled(slackStub)
t.equal(res.statusCode, 200)
})

t.test('item updated', async t => {
const signature = createSignature(
itemMovedNoStatusToTodo,
config.ORG_WEBHOOK_SECRET
)
const res = await app.inject({
url: '/webhook',
method: 'POST',
body: itemMovedNoStatusToTodo,
headers: {
'X-Hub-Signature-256': signature,
},
})
sinon.assert.notCalled(slackStub)
t.equal(res.statusCode, 200)
})

t.test('item deleted', async t => {
const signature = createSignature(itemDeleted, config.ORG_WEBHOOK_SECRET)
const res = await app.inject({
url: '/webhook',
method: 'POST',
body: itemDeleted,
headers: {
'X-Hub-Signature-256': signature,
},
})
sinon.assert.notCalled(slackStub)
t.equal(res.statusCode, 200)
})

t.test('pr created', async t => {
const signature = createSignature(
pullRequestCreated,
config.ORG_WEBHOOK_SECRET
)
const res = await app.inject({
url: '/webhook',
method: 'POST',
body: pullRequestCreated,
headers: {
'X-Hub-Signature-256': signature,
},
})
sinon.assert.notCalled(slackStub)
t.equal(res.statusCode, 200)
})

t.test('pr deleted', async t => {
const signature = createSignature(
pullRequestDeleted,
config.ORG_WEBHOOK_SECRET
)
const res = await app.inject({
url: '/webhook',
method: 'POST',
body: pullRequestDeleted,
headers: {
'X-Hub-Signature-256': signature,
},
})
sinon.assert.notCalled(slackStub)
t.equal(res.statusCode, 200)
})

/**
* the request should be ignored because it is of type reordered
*/
t.test('pr moved', async t => {
const signature = createSignature(
pullRequestMoved,
config.ORG_WEBHOOK_SECRET
)
const res = await app.inject({
url: '/webhook',
method: 'POST',
body: pullRequestMoved,
headers: {
'X-Hub-Signature-256': signature,
},
})
sinon.assert.notCalled(slackStub)
t.equal(res.statusCode, 200)
})

t.test('item with a non existent node_id', async t => {
const signature = createSignature(
itemInvalidNodeId,
config.ORG_WEBHOOK_SECRET
)
const res = await app.inject({
url: '/webhook',
method: 'POST',
body: itemInvalidNodeId,
headers: {
'X-Hub-Signature-256': signature,
},
})
sinon.assert.notCalled(slackStub)
t.equal(res.statusCode, 200)
})
})
t.test(
'sends graphql and slack bot requests with invalid getProjectItemById:',
async t => {
const slackStub = sinon.stub()
const graphqlClientStub = sinon.stub()
graphqlClientStub.onFirstCall().rejects(new Error())
graphqlClientStub.onSecondCall().resolves(getProjectItemByIdResponse)
graphqlClientStub.onThirdCall().resolves(getProjectItemByIdResponse)
graphqlClientStub.onSecondCall().resolves(getProjectItemByIdResponse())
graphqlClientStub.onThirdCall().resolves(getProjectItemByIdResponse())
const app = await build(t, {
graphqlClient: async () => graphqlClientStub,
slackApp: async function slackApp() {
Expand Down Expand Up @@ -252,7 +396,8 @@ test('POST /webhook', async t => {
'X-Hub-Signature-256': signature,
},
})
t.equal(slackStub.callCount, 1)
sinon.assert.calledOnce(slackStub)

t.equal(res.statusCode, 200)
})
}
Expand Down