diff --git a/README.md b/README.md index bdd5b58b6..838743af5 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,13 @@ repository: # vulnerability alerts. enable_vulnerability_alerts: true +# Autolinks +autolinks: + - key_prefix: ASDF- + url_template: https://jira.company.com/browse/ASDF- + - key_prefix: BOLIGRAFO- + url_template: https://jira.company.com/browse/BOLIGRAFO- + # Labels: define labels for Issues and Pull Requests labels: - name: bug diff --git a/lib/plugins/autolinks.js b/lib/plugins/autolinks.js new file mode 100644 index 000000000..90c445f82 --- /dev/null +++ b/lib/plugins/autolinks.js @@ -0,0 +1,33 @@ +const Diffable = require('./diffable') +const previewHeaders = { accept: 'application/vnd.github.v3+json' } + +module.exports = class Autolinks extends Diffable { + find () { + const options = this.github.repos.listAutolinks.endpoint.merge(this.wrapAttrs({ per_page: 100 })) + return this.github.paginate(options) + } + + comparator (existing, attrs) { + return existing.key_prefix === attrs.key_prefix + } + + changed (existing, attrs) { + return existing.url_template !== attrs.url_template + } + + update (existing, attrs) { + return this.add(attrs) + } + + add (attrs) { + return this.github.repos.createAutolink(this.wrapAttrs(attrs)) + } + + remove (existing) { + return this.github.repos.deleteAutolink(this.wrapAttrs({ autolink_id: existing.id })) + } + + wrapAttrs (attrs) { + return Object.assign({}, attrs, this.repo, { headers: previewHeaders }) + } +} diff --git a/lib/settings.js b/lib/settings.js index 4549833f0..9daaa9397 100644 --- a/lib/settings.js +++ b/lib/settings.js @@ -24,6 +24,7 @@ Settings.FILE_NAME = '.github/settings.yml' Settings.PLUGINS = { repository: require('./plugins/repository'), + autolinks: require('./plugins/autolinks'), labels: require('./plugins/labels'), collaborators: require('./plugins/collaborators'), teams: require('./plugins/teams'), diff --git a/test/fixtures/autolinks-config.yml b/test/fixtures/autolinks-config.yml new file mode 100644 index 000000000..5b564c935 --- /dev/null +++ b/test/fixtures/autolinks-config.yml @@ -0,0 +1,11 @@ +repository: + name: bar + delete_branch_on_merge: true + is_template: true + +autolinks: + - key_prefix: ASDF- + url_template: https://jira.company.com/browse/ASDF- + + - key_prefix: BOLIGRAFO- + url_template: https://jira.company.com/browse/BOLIGRAFO- diff --git a/test/integration/plugins/autolinks.test.js b/test/integration/plugins/autolinks.test.js new file mode 100644 index 000000000..4748d5200 --- /dev/null +++ b/test/integration/plugins/autolinks.test.js @@ -0,0 +1,61 @@ +const path = require('path') +const fs = require('fs') +const { CREATED, NO_CONTENT, OK } = require('http-status-codes') +const settings = require('../../../lib/settings') +const { buildTriggerEvent, initializeNock, loadInstance, repository, teardownNock } = require('../common') + +describe('autolinks plugin', function () { + let probot, githubScope + + beforeEach(() => { + githubScope = initializeNock() + probot = loadInstance() + }) + + afterEach(() => { + teardownNock(githubScope) + }) + + it('syncs autolinks', async () => { + const pathToConfig = path.resolve(__dirname, '..', '..', 'fixtures', 'autolinks-config.yml') + const configFile = Buffer.from(fs.readFileSync(pathToConfig, 'utf8')) + const config = configFile.toString() + githubScope + .get(`/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent(settings.FILE_NAME)}`) + .reply(OK, config) + githubScope + .patch(`/repos/${repository.owner.name}/${repository.name}`) + .reply(200) + githubScope + .get(`/repos/${repository.owner.name}/${repository.name}/autolinks?per_page=100`) + .reply( + OK, + [ + { + id: 1, + key_prefix: 'ASDF-', + url_template: 'https://jira.company.com/browse/ASDF-' + }, + { + id: 2, + key_prefix: 'TEST-', + url_template: 'https://jira.company.com/browse/TEST-' + } + ] + ) + githubScope + .post(`/repos/${repository.owner.name}/${repository.name}/autolinks`, body => { + expect(body).toMatchObject({ + key_prefix: 'BOLIGRAFO-', + url_template: 'https://jira.company.com/browse/BOLIGRAFO-' + }) + return true + }) + .reply(CREATED) + githubScope + .delete(`/repos/${repository.owner.name}/${repository.name}/autolinks/2`) + .reply(NO_CONTENT) + + await probot.receive(buildTriggerEvent()) + }) +}) diff --git a/test/unit/lib/plugins/autolinks.test.js b/test/unit/lib/plugins/autolinks.test.js new file mode 100644 index 000000000..c015eabdf --- /dev/null +++ b/test/unit/lib/plugins/autolinks.test.js @@ -0,0 +1,73 @@ +const Autolinks = require('../../../../lib/plugins/autolinks') + +describe('Autolinks', () => { + let github + + function configure (config) { + return new Autolinks(github, { owner: 'bkeepers', repo: 'test' }, config) + } + + beforeEach(() => { + github = { + paginate: jest.fn().mockImplementation(() => Promise.resolve()), + repos: { + listAutolinks: { + endpoint: { + merge: jest.fn().mockImplementation(() => {}) + } + }, + deleteAutolink: jest.fn().mockImplementation(() => Promise.resolve()), + createAutolink: jest.fn().mockImplementation(() => Promise.resolve()) + } + } + }) + + describe('sync', () => { + it('syncs autolinks', () => { + github.paginate.mockReturnValueOnce(Promise.resolve([ + { + id: 1, + key_prefix: 'ASDF-', + url_template: 'https://jira.company.com/browse/ASDF-' + }, + { + id: 2, + key_prefix: 'TEST-', + url_template: 'https://jira.company.com/browse/TEST-' + } + ])) + + const plugin = configure([ + { + key_prefix: 'ASDF-', + url_template: 'https://jira.company.com/browse/ASDF-' + }, + { + key_prefix: 'BOLIGRAFO-', + url_template: 'https://jira.company.com/browse/BOLIGRAFO-' + } + ]) + + return plugin.sync().then(() => { + expect(github.repos.createAutolink).toHaveBeenCalledWith({ + key_prefix: 'BOLIGRAFO-', + url_template: 'https://jira.company.com/browse/BOLIGRAFO-', + owner: 'bkeepers', + repo: 'test', + headers: { accept: 'application/vnd.github.v3+json' } + }) + + expect(github.repos.createAutolink).toHaveBeenCalledTimes(1) + + expect(github.repos.deleteAutolink).toHaveBeenCalledWith({ + autolink_id: 2, + owner: 'bkeepers', + repo: 'test', + headers: { accept: 'application/vnd.github.v3+json' } + }) + + expect(github.repos.deleteAutolink).toHaveBeenCalledTimes(1) + }) + }) + }) +})