From 0a19915cc59b8795fefd066be5f3bbe02879a45a Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Thu, 7 Jan 2016 20:10:15 -0500 Subject: [PATCH] First commit --- Controller/Webhook.php | 35 ++ LICENSE | 21 + Locale/bs_BA/translations.php | 15 + Locale/cs_CZ/translations.php | 15 + Locale/da_DK/translations.php | 15 + Locale/de_DE/translations.php | 15 + Locale/es_ES/translations.php | 15 + Locale/fi_FI/translations.php | 15 + Locale/fr_FR/translations.php | 16 + Locale/hu_HU/translations.php | 15 + Locale/id_ID/translations.php | 15 + Locale/it_IT/translations.php | 15 + Locale/ja_JP/translations.php | 15 + Locale/nb_NO/translations.php | 15 + Locale/nl_NL/translations.php | 15 + Locale/pl_PL/translations.php | 15 + Locale/pt_BR/translations.php | 15 + Locale/pt_PT/translations.php | 15 + Locale/ru_RU/translations.php | 15 + Locale/sr_Latn_RS/translations.php | 15 + Locale/sv_SE/translations.php | 15 + Locale/th_TH/translations.php | 15 + Locale/tr_TR/translations.php | 15 + Locale/zh_CN/translations.php | 15 + Makefile | 3 + Plugin.php | 60 +++ README.md | 75 ++++ Template/project/integrations.php | 5 + Test/WebhookHandlerTest.php | 399 ++++++++++++++++++ Test/fixtures/bitbucket_comment_created.json | 147 +++++++ Test/fixtures/bitbucket_issue_assigned.json | 209 +++++++++ Test/fixtures/bitbucket_issue_closed.json | 183 ++++++++ Test/fixtures/bitbucket_issue_opened.json | 112 +++++ Test/fixtures/bitbucket_issue_reopened.json | 183 ++++++++ Test/fixtures/bitbucket_issue_unassigned.json | 193 +++++++++ Test/fixtures/bitbucket_push.json | 182 ++++++++ WebhookHandler.php | 312 ++++++++++++++ 37 files changed, 2450 insertions(+) create mode 100644 Controller/Webhook.php create mode 100644 LICENSE create mode 100644 Locale/bs_BA/translations.php create mode 100644 Locale/cs_CZ/translations.php create mode 100644 Locale/da_DK/translations.php create mode 100644 Locale/de_DE/translations.php create mode 100644 Locale/es_ES/translations.php create mode 100644 Locale/fi_FI/translations.php create mode 100644 Locale/fr_FR/translations.php create mode 100644 Locale/hu_HU/translations.php create mode 100644 Locale/id_ID/translations.php create mode 100644 Locale/it_IT/translations.php create mode 100644 Locale/ja_JP/translations.php create mode 100644 Locale/nb_NO/translations.php create mode 100644 Locale/nl_NL/translations.php create mode 100644 Locale/pl_PL/translations.php create mode 100644 Locale/pt_BR/translations.php create mode 100644 Locale/pt_PT/translations.php create mode 100644 Locale/ru_RU/translations.php create mode 100644 Locale/sr_Latn_RS/translations.php create mode 100644 Locale/sv_SE/translations.php create mode 100644 Locale/th_TH/translations.php create mode 100644 Locale/tr_TR/translations.php create mode 100644 Locale/zh_CN/translations.php create mode 100644 Makefile create mode 100644 Plugin.php create mode 100644 README.md create mode 100644 Template/project/integrations.php create mode 100644 Test/WebhookHandlerTest.php create mode 100644 Test/fixtures/bitbucket_comment_created.json create mode 100644 Test/fixtures/bitbucket_issue_assigned.json create mode 100644 Test/fixtures/bitbucket_issue_closed.json create mode 100644 Test/fixtures/bitbucket_issue_opened.json create mode 100644 Test/fixtures/bitbucket_issue_reopened.json create mode 100644 Test/fixtures/bitbucket_issue_unassigned.json create mode 100644 Test/fixtures/bitbucket_push.json create mode 100644 WebhookHandler.php diff --git a/Controller/Webhook.php b/Controller/Webhook.php new file mode 100644 index 0000000..e20ad68 --- /dev/null +++ b/Controller/Webhook.php @@ -0,0 +1,35 @@ +checkWebhookToken(); + + $bitbucketWebhook = new WebhookHandler($this->container); + $bitbucketWebhook->setProjectId($this->request->getIntegerParam('project_id')); + + $result = $bitbucketWebhook->parsePayload( + $this->request->getHeader('X-Event-Key'), + $this->request->getJson() + ); + + echo $result ? 'PARSED' : 'IGNORED'; + } +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a19d63a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Frédéric Guillot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Locale/bs_BA/translations.php b/Locale/bs_BA/translations.php new file mode 100644 index 0000000..4316ad2 --- /dev/null +++ b/Locale/bs_BA/translations.php @@ -0,0 +1,15 @@ + 'Bitbucket: commit dobijen', + 'Bitbucket webhooks' => 'Bitbucket: webhooks', + 'Help on Bitbucket webhooks' => 'Pomoć na Bitbucket webhooks', + 'By @%s on Bitbucket' => 'Od @%s na Bitbucket', + 'Bitbucket Issue' => 'Bitbucket problem', + 'Commit made by @%s on Bitbucket' => 'Commit-ao @%s na Bitbucket', + 'Bitbucket issue opened' => 'Bitbucket: otvoren problem', + 'Bitbucket issue closed' => 'Bitbucket: zatvoren problem', + 'Bitbucket issue reopened' => 'Bitbucket: problem ponovo otvoren', + 'Bitbucket issue assignee change' => 'Bitbucket: promijenjen izvršilac problema', +); + diff --git a/Locale/cs_CZ/translations.php b/Locale/cs_CZ/translations.php new file mode 100644 index 0000000..b2f7c3f --- /dev/null +++ b/Locale/cs_CZ/translations.php @@ -0,0 +1,15 @@ + 'Bitbucket commit erhalten', + 'Bitbucket webhooks' => 'Bitbucket webhooks', + 'Help on Bitbucket webhooks' => 'Hilfe für Bitbucket webhooks', + // 'By @%s on Bitbucket' => '', + // 'Bitbucket Issue' => '', + // 'Commit made by @%s on Bitbucket' => '', + // 'Bitbucket issue opened' => '', + // 'Bitbucket issue closed' => '', + // 'Bitbucket issue reopened' => '', + // 'Bitbucket issue assignee change' => '', +); + diff --git a/Locale/da_DK/translations.php b/Locale/da_DK/translations.php new file mode 100644 index 0000000..ffce3ff --- /dev/null +++ b/Locale/da_DK/translations.php @@ -0,0 +1,15 @@ + '', + // 'Bitbucket webhooks' => '', + // 'Help on Bitbucket webhooks' => '', + // 'By @%s on Bitbucket' => '', + // 'Bitbucket Issue' => '', + // 'Commit made by @%s on Bitbucket' => '', + // 'Bitbucket issue opened' => '', + // 'Bitbucket issue closed' => '', + // 'Bitbucket issue reopened' => '', + // 'Bitbucket issue assignee change' => '', +); + diff --git a/Locale/de_DE/translations.php b/Locale/de_DE/translations.php new file mode 100644 index 0000000..1ebed04 --- /dev/null +++ b/Locale/de_DE/translations.php @@ -0,0 +1,15 @@ + 'Bitbucket-Commit erhalten', + 'Bitbucket webhooks' => 'Bitbucket-Webhooks', + 'Help on Bitbucket webhooks' => 'Hilfe für Bitbucket-Webhooks', + 'By @%s on Bitbucket' => 'Durch @%s auf Bitbucket', + 'Bitbucket Issue' => 'Bitbucket-Issue', + 'Commit made by @%s on Bitbucket' => 'Commit von @%s auf Bitbucket', + 'Bitbucket issue opened' => 'Bitbucket Ticket eröffnet', + 'Bitbucket issue closed' => 'Bitbucket Ticket geschlossen', + 'Bitbucket issue reopened' => 'Bitbucket Ticket wieder eröffnet', + 'Bitbucket issue assignee change' => 'Bitbucket Ticket Zuordnung geändert', +); + diff --git a/Locale/es_ES/translations.php b/Locale/es_ES/translations.php new file mode 100644 index 0000000..6855f2f --- /dev/null +++ b/Locale/es_ES/translations.php @@ -0,0 +1,15 @@ + 'Recibido envío desde Bitbucket', + 'Bitbucket webhooks' => 'Disparadores Web (webhooks) de Bitbucket', + 'Help on Bitbucket webhooks' => 'Ayuda sobre disparadores web (webhooks) de Bitbucket', + 'By @%s on Bitbucket' => 'Mediante @%s en Bitbucket', + 'Bitbucket Issue' => 'Asunto de Bitbucket', + 'Commit made by @%s on Bitbucket' => 'Envío realizado por @%s en Bitbucket', + 'Bitbucket issue opened' => 'Abierto asunto de Bitbucket', + 'Bitbucket issue closed' => 'Cerrado asunto de Bitbucket', + 'Bitbucket issue reopened' => 'Reabierto asunto de Bitbucket', + 'Bitbucket issue assignee change' => 'Cambiado concesionario de asunto de Bitbucket', +); + diff --git a/Locale/fi_FI/translations.php b/Locale/fi_FI/translations.php new file mode 100644 index 0000000..ffce3ff --- /dev/null +++ b/Locale/fi_FI/translations.php @@ -0,0 +1,15 @@ + '', + // 'Bitbucket webhooks' => '', + // 'Help on Bitbucket webhooks' => '', + // 'By @%s on Bitbucket' => '', + // 'Bitbucket Issue' => '', + // 'Commit made by @%s on Bitbucket' => '', + // 'Bitbucket issue opened' => '', + // 'Bitbucket issue closed' => '', + // 'Bitbucket issue reopened' => '', + // 'Bitbucket issue assignee change' => '', +); + diff --git a/Locale/fr_FR/translations.php b/Locale/fr_FR/translations.php new file mode 100644 index 0000000..5f4ea68 --- /dev/null +++ b/Locale/fr_FR/translations.php @@ -0,0 +1,16 @@ + 'Commit reçu via Bitbucket', + 'Bitbucket webhooks' => 'Webhook Bitbucket', + 'Help on Bitbucket webhooks' => 'Aide sur les webhooks Bitbucket', + 'By @%s on Bitbucket' => 'Par @%s sur Bitbucket', + 'Bitbucket Issue' => 'Ticket Bitbucket', + 'Commit made by @%s on Bitbucket' => 'Commit fait par @%s sur Bitbucket', + 'Bitbucket issue opened' => 'Ticket Bitbucket ouvert', + 'Bitbucket issue closed' => 'Ticket Bitbucket fermé', + 'Bitbucket issue reopened' => 'Ticket Bitbucket rouvert', + 'Bitbucket issue assignee change' => 'Changement d\'assigné sur un ticket Bitbucket', + 'Bind Bitbucket webhook events to Kanboard automatic actions' => 'Connecte les événements de Bitbucket aux actions automatiques de Kanboard', +); + diff --git a/Locale/hu_HU/translations.php b/Locale/hu_HU/translations.php new file mode 100644 index 0000000..0540786 --- /dev/null +++ b/Locale/hu_HU/translations.php @@ -0,0 +1,15 @@ + 'Bitbucket commit érkezett', + 'Bitbucket webhooks' => 'Bitbucket webhooks', + 'Help on Bitbucket webhooks' => 'Bitbucket webhooks súgó', + // 'By @%s on Bitbucket' => '', + // 'Bitbucket Issue' => '', + // 'Commit made by @%s on Bitbucket' => '', + // 'Bitbucket issue opened' => '', + // 'Bitbucket issue closed' => '', + // 'Bitbucket issue reopened' => '', + // 'Bitbucket issue assignee change' => '', +); + diff --git a/Locale/id_ID/translations.php b/Locale/id_ID/translations.php new file mode 100644 index 0000000..fbfcde0 --- /dev/null +++ b/Locale/id_ID/translations.php @@ -0,0 +1,15 @@ + 'Menerima komit Bitbucket', + 'Bitbucket webhooks' => 'Webhook Bitbucket', + 'Help on Bitbucket webhooks' => 'Bantuan pada webhook Bitbucket', + 'By @%s on Bitbucket' => 'Oleh @%s pada Bitbucket', + 'Bitbucket Issue' => 'Tiket Bitbucket', + 'Commit made by @%s on Bitbucket' => 'Komit dibuat oleh @%s pada Bitbucket', + 'Bitbucket issue opened' => 'Tiket Bitbucket dibuka', + 'Bitbucket issue closed' => 'Tiket Bitbucket ditutup', + 'Bitbucket issue reopened' => 'Tiket Bitbucket dibuka kembali', + 'Bitbucket issue assignee change' => 'Perubahan penugasan tiket Bitbucket', +); + diff --git a/Locale/it_IT/translations.php b/Locale/it_IT/translations.php new file mode 100644 index 0000000..25de1b8 --- /dev/null +++ b/Locale/it_IT/translations.php @@ -0,0 +1,15 @@ + 'Commit ricevuto da Bitbucket', + 'Bitbucket webhooks' => 'Webhooks di Bitbucket', + 'Help on Bitbucket webhooks' => 'Guida ai Webhooks di Bitbucket', + // 'By @%s on Bitbucket' => '', + // 'Bitbucket Issue' => '', + // 'Commit made by @%s on Bitbucket' => '', + // 'Bitbucket issue opened' => '', + // 'Bitbucket issue closed' => '', + // 'Bitbucket issue reopened' => '', + // 'Bitbucket issue assignee change' => '', +); + diff --git a/Locale/ja_JP/translations.php b/Locale/ja_JP/translations.php new file mode 100644 index 0000000..c090c64 --- /dev/null +++ b/Locale/ja_JP/translations.php @@ -0,0 +1,15 @@ + 'Bitbucket コミットを受信しました', + 'Bitbucket webhooks' => 'Bitbucket Webhooks', + 'Help on Bitbucket webhooks' => 'Bitbucket Webhooks のヘルプ', + // 'By @%s on Bitbucket' => '', + // 'Bitbucket Issue' => '', + // 'Commit made by @%s on Bitbucket' => '', + // 'Bitbucket issue opened' => '', + // 'Bitbucket issue closed' => '', + // 'Bitbucket issue reopened' => '', + // 'Bitbucket issue assignee change' => '', +); + diff --git a/Locale/nb_NO/translations.php b/Locale/nb_NO/translations.php new file mode 100644 index 0000000..ffce3ff --- /dev/null +++ b/Locale/nb_NO/translations.php @@ -0,0 +1,15 @@ + '', + // 'Bitbucket webhooks' => '', + // 'Help on Bitbucket webhooks' => '', + // 'By @%s on Bitbucket' => '', + // 'Bitbucket Issue' => '', + // 'Commit made by @%s on Bitbucket' => '', + // 'Bitbucket issue opened' => '', + // 'Bitbucket issue closed' => '', + // 'Bitbucket issue reopened' => '', + // 'Bitbucket issue assignee change' => '', +); + diff --git a/Locale/nl_NL/translations.php b/Locale/nl_NL/translations.php new file mode 100644 index 0000000..990acf5 --- /dev/null +++ b/Locale/nl_NL/translations.php @@ -0,0 +1,15 @@ + 'Bitbucket commit ontvangen', + 'Bitbucket webhooks' => 'Bitbucket webhooks', + 'Help on Bitbucket webhooks' => 'Help bij Bitbucket webhooks', + // 'By @%s on Bitbucket' => '', + // 'Bitbucket Issue' => '', + // 'Commit made by @%s on Bitbucket' => '', + // 'Bitbucket issue opened' => '', + // 'Bitbucket issue closed' => '', + // 'Bitbucket issue reopened' => '', + // 'Bitbucket issue assignee change' => '', +); + diff --git a/Locale/pl_PL/translations.php b/Locale/pl_PL/translations.php new file mode 100644 index 0000000..ffce3ff --- /dev/null +++ b/Locale/pl_PL/translations.php @@ -0,0 +1,15 @@ + '', + // 'Bitbucket webhooks' => '', + // 'Help on Bitbucket webhooks' => '', + // 'By @%s on Bitbucket' => '', + // 'Bitbucket Issue' => '', + // 'Commit made by @%s on Bitbucket' => '', + // 'Bitbucket issue opened' => '', + // 'Bitbucket issue closed' => '', + // 'Bitbucket issue reopened' => '', + // 'Bitbucket issue assignee change' => '', +); + diff --git a/Locale/pt_BR/translations.php b/Locale/pt_BR/translations.php new file mode 100644 index 0000000..7eb7b7a --- /dev/null +++ b/Locale/pt_BR/translations.php @@ -0,0 +1,15 @@ + '"Commit" recebido via Bitbucket', + 'Bitbucket webhooks' => 'Webhook Bitbucket', + 'Help on Bitbucket webhooks' => 'Ajuda sobre os webhooks do Bitbucket', + 'By @%s on Bitbucket' => 'Por @%s no Bitbucket', + 'Bitbucket Issue' => 'Bitbucket Issue', + 'Commit made by @%s on Bitbucket' => 'Commit feito por @%s no Bitbucket', + 'Bitbucket issue opened' => 'Bitbucket issue opened', + 'Bitbucket issue closed' => 'Bitbucket issue closed', + 'Bitbucket issue reopened' => 'Bitbucket issue reopened', + 'Bitbucket issue assignee change' => 'Bitbucket issue assignee change', +); + diff --git a/Locale/pt_PT/translations.php b/Locale/pt_PT/translations.php new file mode 100644 index 0000000..478df15 --- /dev/null +++ b/Locale/pt_PT/translations.php @@ -0,0 +1,15 @@ + '"Commit" recebido via Bitbucket', + 'Bitbucket webhooks' => 'Webhook Bitbucket', + 'Help on Bitbucket webhooks' => 'Ajuda sobre os webhooks Bitbucket', + 'By @%s on Bitbucket' => 'Por @%s no Bitbucket', + 'Bitbucket Issue' => 'Problema Bitbucket', + 'Commit made by @%s on Bitbucket' => 'Commit feito por @%s no Bitbucket', + 'Bitbucket issue opened' => 'Problema aberto no Bitbucket', + 'Bitbucket issue closed' => 'Problema fechado no Bitbucket', + 'Bitbucket issue reopened' => 'Problema reaberto no Bitbucket', + 'Bitbucket issue assignee change' => 'Alterar assignação do problema no Bitbucket', +); + diff --git a/Locale/ru_RU/translations.php b/Locale/ru_RU/translations.php new file mode 100644 index 0000000..b32594e --- /dev/null +++ b/Locale/ru_RU/translations.php @@ -0,0 +1,15 @@ + 'Получен коммит с Bitbucket', + 'Bitbucket webhooks' => 'BitBucket webhooks', + 'Help on Bitbucket webhooks' => 'Помощь по BitBucket webhooks', + 'By @%s on Bitbucket' => 'Польз. @%s на Bitbucket', + 'Bitbucket Issue' => 'Задача Bitbucket', + 'Commit made by @%s on Bitbucket' => 'Коммит сделан польз. @%s на Bitbucket', + 'Bitbucket issue opened' => 'Открыта задача Bitbucket', + 'Bitbucket issue closed' => 'Закрыта задача Bitbucket', + 'Bitbucket issue reopened' => 'Переоткрыта задача Bitbucket', + 'Bitbucket issue assignee change' => 'Изменный назначенный для задачи на Bitbucket', +); + diff --git a/Locale/sr_Latn_RS/translations.php b/Locale/sr_Latn_RS/translations.php new file mode 100644 index 0000000..ffce3ff --- /dev/null +++ b/Locale/sr_Latn_RS/translations.php @@ -0,0 +1,15 @@ + '', + // 'Bitbucket webhooks' => '', + // 'Help on Bitbucket webhooks' => '', + // 'By @%s on Bitbucket' => '', + // 'Bitbucket Issue' => '', + // 'Commit made by @%s on Bitbucket' => '', + // 'Bitbucket issue opened' => '', + // 'Bitbucket issue closed' => '', + // 'Bitbucket issue reopened' => '', + // 'Bitbucket issue assignee change' => '', +); + diff --git a/Locale/sv_SE/translations.php b/Locale/sv_SE/translations.php new file mode 100644 index 0000000..91ebaa4 --- /dev/null +++ b/Locale/sv_SE/translations.php @@ -0,0 +1,15 @@ + 'Bitbucket bidrag mottaget', + 'Bitbucket webhooks' => 'Bitbucket webhooks', + 'Help on Bitbucket webhooks' => 'Hjälp för Bitbucket webhooks', + 'By @%s on Bitbucket' => 'Av @%s på Bitbucket', + 'Bitbucket Issue' => 'Bitbucket fråga', + 'Commit made by @%s on Bitbucket' => 'Bidrag gjort av @%s på Bitbucket', + 'Bitbucket issue opened' => 'Bitbucketfråga öppnad', + 'Bitbucket issue closed' => 'Bitbucketfråga stängd', + 'Bitbucket issue reopened' => 'Bitbucketfråga återöppnad', + 'Bitbucket issue assignee change' => 'Bitbucketfråga tilldelningsändring', +); + diff --git a/Locale/th_TH/translations.php b/Locale/th_TH/translations.php new file mode 100644 index 0000000..ffce3ff --- /dev/null +++ b/Locale/th_TH/translations.php @@ -0,0 +1,15 @@ + '', + // 'Bitbucket webhooks' => '', + // 'Help on Bitbucket webhooks' => '', + // 'By @%s on Bitbucket' => '', + // 'Bitbucket Issue' => '', + // 'Commit made by @%s on Bitbucket' => '', + // 'Bitbucket issue opened' => '', + // 'Bitbucket issue closed' => '', + // 'Bitbucket issue reopened' => '', + // 'Bitbucket issue assignee change' => '', +); + diff --git a/Locale/tr_TR/translations.php b/Locale/tr_TR/translations.php new file mode 100644 index 0000000..ffb4a2a --- /dev/null +++ b/Locale/tr_TR/translations.php @@ -0,0 +1,15 @@ + 'Bitbucket commit alındı', + 'Bitbucket webhooks' => 'Bitbucket webhooks', + 'Help on Bitbucket webhooks' => 'Bitbucket webhooks için yardım', + // 'By @%s on Bitbucket' => '', + // 'Bitbucket Issue' => '', + // 'Commit made by @%s on Bitbucket' => '', + // 'Bitbucket issue opened' => '', + // 'Bitbucket issue closed' => '', + // 'Bitbucket issue reopened' => '', + // 'Bitbucket issue assignee change' => '', +); + diff --git a/Locale/zh_CN/translations.php b/Locale/zh_CN/translations.php new file mode 100644 index 0000000..6322613 --- /dev/null +++ b/Locale/zh_CN/translations.php @@ -0,0 +1,15 @@ + '收到Bitbucket提交', + 'Bitbucket webhooks' => 'Bitbucket网络钩子', + 'Help on Bitbucket webhooks' => 'Bitbucket网络钩子帮助', + // 'By @%s on Bitbucket' => '', + // 'Bitbucket Issue' => '', + // 'Commit made by @%s on Bitbucket' => '', + // 'Bitbucket issue opened' => '', + // 'Bitbucket issue closed' => '', + // 'Bitbucket issue reopened' => '', + // 'Bitbucket issue assignee change' => '', +); + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e10760a --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +all: + @ echo "Build archive for plugin ${plugin} version=${version}" + @ git archive HEAD --prefix=${plugin}/ --format=zip -o ${plugin}-${version}.zip diff --git a/Plugin.php b/Plugin.php new file mode 100644 index 0000000..e229661 --- /dev/null +++ b/Plugin.php @@ -0,0 +1,60 @@ +on('app.bootstrap', function($container) { + Translator::load($container['config']->getCurrentLanguage(), __DIR__.'/Locale'); + + $this->eventManager->register(WebhookHandler::EVENT_COMMIT, t('Bitbucket commit received')); + $this->eventManager->register(WebhookHandler::EVENT_ISSUE_OPENED, t('Bitbucket issue opened')); + $this->eventManager->register(WebhookHandler::EVENT_ISSUE_CLOSED, t('Bitbucket issue closed')); + $this->eventManager->register(WebhookHandler::EVENT_ISSUE_REOPENED, t('Bitbucket issue reopened')); + $this->eventManager->register(WebhookHandler::EVENT_ISSUE_ASSIGNEE_CHANGE, t('Bitbucket issue assignee change')); + $this->eventManager->register(WebhookHandler::EVENT_ISSUE_COMMENT, t('Bitbucket issue comment created')); + }); + + $this->actionManager->getAction('\Kanboard\Action\CommentCreation')->addEvent(WebhookHandler::EVENT_ISSUE_COMMENT); + $this->actionManager->getAction('\Kanboard\Action\CommentCreation')->addEvent(WebhookHandler::EVENT_COMMIT); + $this->actionManager->getAction('\Kanboard\Action\TaskAssignUser')->addEvent(WebhookHandler::EVENT_ISSUE_ASSIGNEE_CHANGE); + $this->actionManager->getAction('\Kanboard\Action\TaskClose')->addEvent(WebhookHandler::EVENT_COMMIT); + $this->actionManager->getAction('\Kanboard\Action\TaskClose')->addEvent(WebhookHandler::EVENT_ISSUE_CLOSED); + $this->actionManager->getAction('\Kanboard\Action\TaskCreation')->addEvent(WebhookHandler::EVENT_ISSUE_OPENED); + $this->actionManager->getAction('\Kanboard\Action\TaskOpen')->addEvent(WebhookHandler::EVENT_ISSUE_REOPENED); + + $this->template->hook->attach('template:project:integrations', 'BitbucketWebhook:project/integrations'); + + $this->route->addRoute('/webhook/bitbucket/:project_id/:token', 'webhook', 'handler', 'BitbucketWebhook'); + } + + public function getPluginName() + { + return 'Bitbucket Webhook'; + } + + public function getPluginDescription() + { + return t('Bind Bitbucket webhook events to Kanboard automatic actions'); + } + + public function getPluginAuthor() + { + return 'Frédéric Guillot'; + } + + public function getPluginVersion() + { + return '1.0.0'; + } + + public function getPluginHomepage() + { + return 'https://github.com/kanboard/plugin-bitbucket-webhook'; + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..9617920 --- /dev/null +++ b/README.md @@ -0,0 +1,75 @@ +Bitbucket Webhook +================= + +Bind Bitbucket webhook events to Kanboard automatic actions. + +Author +------ + +- Frederic Guillot +- License MIT + +Requirements +------------ + +- Bitbucket webhooks configured for a project + +Installation +------------ + +- Decompress the archive in the `plugins` folder + +or + +- Create a folder **plugins/BitbucketWebhook** +- Copy all files under this directory + +Documentation +------------- + +### List of supported events + +- Bitbucket commit received +- Bitbucket issue opened +- Bitbucket issue closed +- Bitbucket issue reopened +- Bitbucket issue assignee change +- Bitbucket issue comment created + +### List of supported actions + +- Create a task from an external provider +- Change the assignee based on an external username +- Create a comment from an external provider +- Close a task +- Open a task + +### Configuration + +![Bitbucket configuration](http://kanboard.net/screenshots/documentation/bitbucket-webhooks.png) + +1. On Kanboard, go to the project settings and choose the section **Integrations** +2. Copy the Bitbucket webhook URL +3. On Bitbucket, go to the project settings and go to the section **Webhooks** +4. Choose a title for your webhook and paste the Kanboard URL + +### Examples + +#### Close a Kanboard task when a commit pushed to Bitbucket + +- Choose the event: **Bitbucket commit received** +- Choose action: **Close the task** + +When one or more commits are sent to Bitbucket, Kanboard will receive the information, each commit message with a task number included will be closed. + +Example: + +- Commit message: "Fix bug #1234" +- That will close the Kanboard task #1234 + +#### Add comment when a commit received + +- Choose the event: **Bitbucket commit received** +- Choose action: **Create a comment from an external provider** + +The comment will contain the commit message and the URL to the commit. diff --git a/Template/project/integrations.php b/Template/project/integrations.php new file mode 100644 index 0000000..2ee81d5 --- /dev/null +++ b/Template/project/integrations.php @@ -0,0 +1,5 @@ +

 

+
+
+

+
\ No newline at end of file diff --git a/Test/WebhookHandlerTest.php b/Test/WebhookHandlerTest.php new file mode 100644 index 0000000..3832c74 --- /dev/null +++ b/Test/WebhookHandlerTest.php @@ -0,0 +1,399 @@ +container['dispatcher']->addListener(WebhookHandler::EVENT_COMMIT, array($this, 'onCommit')); + + $tc = new TaskCreation($this->container); + $p = new Project($this->container); + $bw = new WebhookHandler($this->container); + $payload = json_decode(file_get_contents(__DIR__.'/fixtures/bitbucket_push.json'), true); + + $this->assertEquals(1, $p->create(array('name' => 'test'))); + $bw->setProjectId(1); + + // No task + $this->assertFalse($bw->handlePush($payload)); + + // Create task with the wrong id + $this->assertEquals(1, $tc->create(array('title' => 'test1', 'project_id' => 1))); + $this->assertFalse($bw->handlePush($payload)); + + // Create task with the right id + $this->assertEquals(2, $tc->create(array('title' => 'test2', 'project_id' => 1))); + $this->assertTrue($bw->handlePush($payload)); + + $called = $this->container['dispatcher']->getCalledListeners(); + $this->assertArrayHasKey(WebhookHandler::EVENT_COMMIT.'.WebhookHandlerTest::onCommit', $called); + } + + public function testIssueOpened() + { + $this->container['dispatcher']->addListener(WebhookHandler::EVENT_ISSUE_OPENED, array($this, 'onIssueOpened')); + + $p = new Project($this->container); + $this->assertEquals(1, $p->create(array('name' => 'foobar'))); + + $bw = new WebhookHandler($this->container); + $bw->setProjectId(1); + + $this->assertNotFalse($bw->parsePayload( + 'issue:created', + json_decode(file_get_contents(__DIR__.'/fixtures/bitbucket_issue_opened.json'), true) + )); + } + + public function testCommentCreatedWithNoUser() + { + $this->container['dispatcher']->addListener(WebhookHandler::EVENT_ISSUE_COMMENT, array($this, 'onCommentCreatedWithNoUser')); + + $p = new Project($this->container); + $this->assertEquals(1, $p->create(array('name' => 'foobar'))); + + $tc = new TaskCreation($this->container); + $this->assertEquals(1, $tc->create(array('title' => 'boo', 'reference' => 1, 'project_id' => 1))); + + $g = new WebhookHandler($this->container); + $g->setProjectId(1); + + $this->assertNotFalse($g->parsePayload( + 'issue:comment_created', + json_decode(file_get_contents(__DIR__.'/fixtures/bitbucket_comment_created.json'), true) + )); + } + + public function testCommentCreatedWithNotMember() + { + $this->container['dispatcher']->addListener(WebhookHandler::EVENT_ISSUE_COMMENT, array($this, 'onCommentCreatedWithNotMember')); + + $p = new Project($this->container); + $this->assertEquals(1, $p->create(array('name' => 'foobar'))); + + $tc = new TaskCreation($this->container); + $this->assertEquals(1, $tc->create(array('title' => 'boo', 'reference' => 1, 'project_id' => 1))); + + $u = new User($this->container); + $this->assertEquals(2, $u->create(array('username' => 'fguillot'))); + + $g = new WebhookHandler($this->container); + $g->setProjectId(1); + + $this->assertNotFalse($g->parsePayload( + 'issue:comment_created', + json_decode(file_get_contents(__DIR__.'/fixtures/bitbucket_comment_created.json'), true) + )); + } + + public function testCommentCreatedWithUser() + { + $this->container['dispatcher']->addListener(WebhookHandler::EVENT_ISSUE_COMMENT, array($this, 'onCommentCreatedWithUser')); + + $p = new Project($this->container); + $this->assertEquals(1, $p->create(array('name' => 'foobar'))); + + $tc = new TaskCreation($this->container); + $this->assertEquals(1, $tc->create(array('title' => 'boo', 'reference' => 1, 'project_id' => 1))); + + $u = new User($this->container); + $this->assertEquals(2, $u->create(array('username' => 'minicoders'))); + + $pp = new ProjectUserRole($this->container); + $this->assertTrue($pp->addUser(1, 2, Role::PROJECT_MEMBER)); + + $g = new WebhookHandler($this->container); + $g->setProjectId(1); + + $this->assertNotFalse($g->parsePayload( + 'issue:comment_created', + json_decode(file_get_contents(__DIR__.'/fixtures/bitbucket_comment_created.json'), true) + )); + } + + public function testIssueClosed() + { + $this->container['dispatcher']->addListener(WebhookHandler::EVENT_ISSUE_CLOSED, array($this, 'onIssueClosed')); + + $p = new Project($this->container); + $this->assertEquals(1, $p->create(array('name' => 'foobar'))); + + $tc = new TaskCreation($this->container); + $this->assertEquals(1, $tc->create(array('title' => 'boo', 'reference' => 1, 'project_id' => 1))); + + $g = new WebhookHandler($this->container); + $g->setProjectId(1); + + $this->assertNotFalse($g->parsePayload( + 'issue:updated', + json_decode(file_get_contents(__DIR__.'/fixtures/bitbucket_issue_closed.json'), true) + )); + } + + public function testIssueClosedWithNoTask() + { + $this->container['dispatcher']->addListener(WebhookHandler::EVENT_ISSUE_CLOSED, array($this, 'onIssueClosed')); + + $p = new Project($this->container); + $this->assertEquals(1, $p->create(array('name' => 'foobar'))); + + $tc = new TaskCreation($this->container); + $this->assertEquals(1, $tc->create(array('title' => 'boo', 'reference' => 42, 'project_id' => 1))); + + $g = new WebhookHandler($this->container); + $g->setProjectId(1); + + $this->assertFalse($g->parsePayload( + 'issue:updated', + json_decode(file_get_contents(__DIR__.'/fixtures/bitbucket_issue_closed.json'), true) + )); + + $this->assertEmpty($this->container['dispatcher']->getCalledListeners()); + } + + public function testIssueReopened() + { + $this->container['dispatcher']->addListener(WebhookHandler::EVENT_ISSUE_REOPENED, array($this, 'onIssueReopened')); + + $p = new Project($this->container); + $this->assertEquals(1, $p->create(array('name' => 'foobar'))); + + $tc = new TaskCreation($this->container); + $this->assertEquals(1, $tc->create(array('title' => 'boo', 'reference' => 1, 'project_id' => 1))); + + $g = new WebhookHandler($this->container); + $g->setProjectId(1); + + $this->assertNotFalse($g->parsePayload( + 'issue:updated', + json_decode(file_get_contents(__DIR__.'/fixtures/bitbucket_issue_reopened.json'), true) + )); + } + + public function testIssueReopenedWithNoTask() + { + $this->container['dispatcher']->addListener(WebhookHandler::EVENT_ISSUE_REOPENED, array($this, 'onIssueReopened')); + + $p = new Project($this->container); + $this->assertEquals(1, $p->create(array('name' => 'foobar'))); + + $tc = new TaskCreation($this->container); + $this->assertEquals(1, $tc->create(array('title' => 'boo', 'reference' => 42, 'project_id' => 1))); + + $g = new WebhookHandler($this->container); + $g->setProjectId(1); + + $this->assertFalse($g->parsePayload( + 'issue:updated', + json_decode(file_get_contents(__DIR__.'/fixtures/bitbucket_issue_reopened.json'), true) + )); + + $this->assertEmpty($this->container['dispatcher']->getCalledListeners()); + } + + public function testIssueUnassigned() + { + $this->container['dispatcher']->addListener(WebhookHandler::EVENT_ISSUE_ASSIGNEE_CHANGE, array($this, 'onIssueUnassigned')); + + $p = new Project($this->container); + $this->assertEquals(1, $p->create(array('name' => 'foobar'))); + + $tc = new TaskCreation($this->container); + $this->assertEquals(1, $tc->create(array('title' => 'boo', 'reference' => 1, 'project_id' => 1))); + + $g = new WebhookHandler($this->container); + $g->setProjectId(1); + + $this->assertNotFalse($g->parsePayload( + 'issue:updated', + json_decode(file_get_contents(__DIR__.'/fixtures/bitbucket_issue_unassigned.json'), true) + )); + } + + public function testIssueAssigned() + { + $this->container['dispatcher']->addListener(WebhookHandler::EVENT_ISSUE_ASSIGNEE_CHANGE, array($this, 'onIssueAssigned')); + + $p = new Project($this->container); + $this->assertEquals(1, $p->create(array('name' => 'foobar'))); + + $tc = new TaskCreation($this->container); + $this->assertEquals(1, $tc->create(array('title' => 'boo', 'reference' => 1, 'project_id' => 1))); + + $u = new User($this->container); + $this->assertEquals(2, $u->create(array('username' => 'minicoders'))); + + $pp = new ProjectUserRole($this->container); + $this->assertTrue($pp->addUser(1, 2, Role::PROJECT_MEMBER)); + + $g = new WebhookHandler($this->container); + $g->setProjectId(1); + + $this->assertNotFalse($g->parsePayload( + 'issue:updated', + json_decode(file_get_contents(__DIR__.'/fixtures/bitbucket_issue_assigned.json'), true) + )); + + $this->assertNotEmpty($this->container['dispatcher']->getCalledListeners()); + } + + public function testIssueAssignedWithNoPermission() + { + $this->container['dispatcher']->addListener(WebhookHandler::EVENT_ISSUE_ASSIGNEE_CHANGE, function () {}); + + $p = new Project($this->container); + $this->assertEquals(1, $p->create(array('name' => 'foobar'))); + + $tc = new TaskCreation($this->container); + $this->assertEquals(1, $tc->create(array('title' => 'boo', 'reference' => 1, 'project_id' => 1))); + + $u = new User($this->container); + $this->assertEquals(2, $u->create(array('username' => 'minicoders'))); + + $g = new WebhookHandler($this->container); + $g->setProjectId(1); + + $this->assertFalse($g->parsePayload( + 'issue:updated', + json_decode(file_get_contents(__DIR__.'/fixtures/bitbucket_issue_assigned.json'), true) + )); + + $this->assertEmpty($this->container['dispatcher']->getCalledListeners()); + } + + public function testIssueAssignedWithNoUser() + { + $this->container['dispatcher']->addListener(WebhookHandler::EVENT_ISSUE_ASSIGNEE_CHANGE, function () {}); + + $p = new Project($this->container); + $this->assertEquals(1, $p->create(array('name' => 'foobar'))); + + $tc = new TaskCreation($this->container); + $this->assertEquals(1, $tc->create(array('title' => 'boo', 'reference' => 1, 'project_id' => 1))); + + $g = new WebhookHandler($this->container); + $g->setProjectId(1); + + $this->assertFalse($g->parsePayload( + 'issue:updated', + json_decode(file_get_contents(__DIR__.'/fixtures/bitbucket_issue_assigned.json'), true) + )); + + $this->assertEmpty($this->container['dispatcher']->getCalledListeners()); + } + + public function testIssueAssignedWithNoTask() + { + $this->container['dispatcher']->addListener(WebhookHandler::EVENT_ISSUE_ASSIGNEE_CHANGE, function () {}); + + $p = new Project($this->container); + $this->assertEquals(1, $p->create(array('name' => 'foobar'))); + + $tc = new TaskCreation($this->container); + $this->assertEquals(1, $tc->create(array('title' => 'boo', 'reference' => 43, 'project_id' => 1))); + + $g = new WebhookHandler($this->container); + $g->setProjectId(1); + + $this->assertFalse($g->parsePayload( + 'issue:updated', + json_decode(file_get_contents(__DIR__.'/fixtures/bitbucket_issue_assigned.json'), true) + )); + + $this->assertEmpty($this->container['dispatcher']->getCalledListeners()); + } + + public function onCommit($event) + { + $data = $event->getAll(); + $this->assertEquals(1, $data['project_id']); + $this->assertEquals(2, $data['task_id']); + $this->assertEquals('test2', $data['title']); + $this->assertEquals("Test another commit #2\n\n\n[Commit made by @Frederic Guillot on Bitbucket](https://bitbucket.org/minicoders/test-webhook/commits/824059cce7667d3f8d8780cc707391be821e0ea6)", $data['comment']); + $this->assertEquals("Test another commit #2\n", $data['commit_message']); + $this->assertEquals('https://bitbucket.org/minicoders/test-webhook/commits/824059cce7667d3f8d8780cc707391be821e0ea6', $data['commit_url']); + } + + public function onIssueOpened($event) + { + $data = $event->getAll(); + $this->assertEquals(1, $data['project_id']); + $this->assertEquals(1, $data['reference']); + $this->assertEquals('My new issue', $data['title']); + $this->assertEquals("**test**\n\n[Bitbucket Issue](https://bitbucket.org/minicoders/test-webhook/issue/1/my-new-issue)", $data['description']); + } + + public function onCommentCreatedWithNoUser($event) + { + $data = $event->getAll(); + $this->assertEquals(1, $data['project_id']); + $this->assertEquals(1, $data['task_id']); + $this->assertEquals(0, $data['user_id']); + $this->assertEquals(19176252, $data['reference']); + $this->assertEquals("1. step1\n2. step2\n\n[By @Frederic Guillot on Bitbucket](https://bitbucket.org/minicoders/test-webhook/issue/1#comment-19176252)", $data['comment']); + } + + public function onCommentCreatedWithNotMember($event) + { + $data = $event->getAll(); + $this->assertEquals(1, $data['project_id']); + $this->assertEquals(1, $data['task_id']); + $this->assertEquals(0, $data['user_id']); + $this->assertEquals(19176252, $data['reference']); + $this->assertEquals("1. step1\n2. step2\n\n[By @Frederic Guillot on Bitbucket](https://bitbucket.org/minicoders/test-webhook/issue/1#comment-19176252)", $data['comment']); + } + + public function onCommentCreatedWithUser($event) + { + $data = $event->getAll(); + $this->assertEquals(1, $data['project_id']); + $this->assertEquals(1, $data['task_id']); + $this->assertEquals(2, $data['user_id']); + $this->assertEquals(19176252, $data['reference']); + $this->assertEquals("1. step1\n2. step2\n\n[By @Frederic Guillot on Bitbucket](https://bitbucket.org/minicoders/test-webhook/issue/1#comment-19176252)", $data['comment']); + } + + public function onIssueClosed($event) + { + $data = $event->getAll(); + $this->assertEquals(1, $data['project_id']); + $this->assertEquals(1, $data['task_id']); + $this->assertEquals(1, $data['reference']); + } + + public function onIssueReopened($event) + { + $data = $event->getAll(); + $this->assertEquals(1, $data['project_id']); + $this->assertEquals(1, $data['task_id']); + $this->assertEquals(1, $data['reference']); + } + + public function onIssueAssigned($event) + { + $data = $event->getAll(); + $this->assertEquals(1, $data['project_id']); + $this->assertEquals(1, $data['task_id']); + $this->assertEquals(1, $data['reference']); + $this->assertEquals(2, $data['owner_id']); + } + + public function onIssueUnassigned($event) + { + $data = $event->getAll(); + $this->assertEquals(1, $data['project_id']); + $this->assertEquals(1, $data['task_id']); + $this->assertEquals(1, $data['reference']); + $this->assertEquals(0, $data['owner_id']); + } +} diff --git a/Test/fixtures/bitbucket_comment_created.json b/Test/fixtures/bitbucket_comment_created.json new file mode 100644 index 0000000..66a09bb --- /dev/null +++ b/Test/fixtures/bitbucket_comment_created.json @@ -0,0 +1,147 @@ +{ + "actor": { + "username": "minicoders", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}", + "display_name": "Frederic Guillot", + "type": "user", + "links": { + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + } + } + }, + "repository": { + "full_name": "minicoders/test-webhook", + "uuid": "{590fd9c4-0812-425e-8d72-ab08b4fd5735}", + "type": "repository", + "links": { + "avatar": { + "href": "https://bitbucket.org/minicoders/test-webhook/avatar/16/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook" + } + }, + "owner": { + "username": "minicoders", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}", + "display_name": "Frederic Guillot", + "type": "user", + "links": { + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + } + } + }, + "name": "test-webhook" + }, + "comment": { + "content": { + "html": "
    \n
  1. step1
  2. \n
  3. step2
  4. \n
", + "raw": "1. step1\n2. step2", + "markup": "markdown" + }, + "created_on": "2015-06-21T02:51:40.532529+00:00", + "links": { + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook/issue/1#comment-19176252" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/comments/19176252" + } + }, + "id": 19176252, + "user": { + "username": "minicoders", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}", + "display_name": "Frederic Guillot", + "type": "user", + "links": { + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + } + } + }, + "updated_on": null + }, + "issue": { + "updated_on": "2015-06-21T02:51:40.536516+00:00", + "votes": 0, + "assignee": null, + "links": { + "vote": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/vote" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook/issue/1/my-new-issue" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1" + }, + "watch": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/watch" + }, + "comments": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/comments" + }, + "attachments": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/attachments" + } + }, + "priority": "major", + "kind": "bug", + "watches": 1, + "edited_on": null, + "state": "new", + "content": { + "html": "

test

", + "raw": "**test**", + "markup": "markdown" + }, + "component": null, + "milestone": null, + "version": null, + "id": 1, + "created_on": "2015-06-21T02:17:40.990654+00:00", + "type": "issue", + "reporter": { + "username": "minicoders", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}", + "display_name": "Frederic Guillot", + "type": "user", + "links": { + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + } + } + }, + "title": "My new issue" + } +} \ No newline at end of file diff --git a/Test/fixtures/bitbucket_issue_assigned.json b/Test/fixtures/bitbucket_issue_assigned.json new file mode 100644 index 0000000..0324afb --- /dev/null +++ b/Test/fixtures/bitbucket_issue_assigned.json @@ -0,0 +1,209 @@ +{ + "repository": { + "name": "test-webhook", + "type": "repository", + "full_name": "minicoders/test-webhook", + "uuid": "{590fd9c4-0812-425e-8d72-ab08b4fd5735}", + "links": { + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook" + }, + "avatar": { + "href": "https://bitbucket.org/minicoders/test-webhook/avatar/16/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook" + } + }, + "owner": { + "display_name": "Frederic Guillot", + "type": "user", + "links": { + "html": { + "href": "https://bitbucket.org/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + } + }, + "username": "minicoders", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}" + } + }, + "changes": { + "responsible": { + "new": { + "is_system": false, + "name": "Frederic Guillot", + "username": "minicoders", + "id": 1290132, + "billing_external_uuid": null + }, + "old": null + }, + "assignee": { + "new": { + "display_name": "Frederic Guillot", + "type": "user", + "links": { + "html": { + "href": "https://bitbucket.org/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + } + }, + "username": "minicoders", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}" + }, + "old": null + } + }, + "actor": { + "display_name": "Frederic Guillot", + "type": "user", + "links": { + "html": { + "href": "https://bitbucket.org/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + } + }, + "username": "minicoders", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}" + }, + "issue": { + "repository": { + "links": { + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook" + }, + "avatar": { + "href": "https://bitbucket.org/minicoders/test-webhook/avatar/16/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook" + } + }, + "type": "repository", + "full_name": "minicoders/test-webhook", + "name": "test-webhook", + "uuid": "{590fd9c4-0812-425e-8d72-ab08b4fd5735}" + }, + "edited_on": null, + "component": null, + "updated_on": "2015-06-21T15:21:28.023525+00:00", + "reporter": { + "display_name": "Frederic Guillot", + "type": "user", + "links": { + "html": { + "href": "https://bitbucket.org/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + } + }, + "username": "minicoders", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}" + }, + "state": "open", + "content": { + "raw": "**test**", + "markup": "markdown", + "html": "

test

" + }, + "kind": "bug", + "links": { + "attachments": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/attachments" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook/issue/1/my-new-issue" + }, + "watch": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/watch" + }, + "vote": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/vote" + }, + "comments": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/comments" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1" + } + }, + "created_on": "2015-06-21T02:17:40.990654+00:00", + "watches": 1, + "milestone": null, + "version": null, + "assignee": { + "display_name": "Frederic Guillot", + "type": "user", + "links": { + "html": { + "href": "https://bitbucket.org/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + } + }, + "username": "minicoders", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}" + }, + "title": "My new issue", + "priority": "major", + "id": 1, + "votes": 0, + "type": "issue" + }, + "comment": { + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/comments/19181255" + } + }, + "updated_on": null, + "content": { + "raw": null, + "markup": "markdown", + "html": "" + }, + "id": 19181255, + "created_on": "2015-06-21T15:21:28.043980+00:00", + "user": { + "display_name": "Frederic Guillot", + "type": "user", + "links": { + "html": { + "href": "https://bitbucket.org/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + } + }, + "username": "minicoders", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}" + } + } +} \ No newline at end of file diff --git a/Test/fixtures/bitbucket_issue_closed.json b/Test/fixtures/bitbucket_issue_closed.json new file mode 100644 index 0000000..473b516 --- /dev/null +++ b/Test/fixtures/bitbucket_issue_closed.json @@ -0,0 +1,183 @@ +{ + "actor": { + "username": "minicoders", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}", + "type": "user", + "display_name": "Frederic Guillot", + "links": { + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + } + } + }, + "changes": { + "status": { + "old": "new", + "new": "closed" + } + }, + "repository": { + "full_name": "minicoders/test-webhook", + "type": "repository", + "links": { + "avatar": { + "href": "https://bitbucket.org/minicoders/test-webhook/avatar/16/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook" + } + }, + "owner": { + "username": "minicoders", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}", + "type": "user", + "display_name": "Frederic Guillot", + "links": { + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + } + } + }, + "uuid": "{590fd9c4-0812-425e-8d72-ab08b4fd5735}", + "name": "test-webhook" + }, + "comment": { + "content": { + "html": "", + "raw": null, + "markup": "markdown" + }, + "created_on": "2015-06-21T02:54:40.263014+00:00", + "updated_on": null, + "id": 19176265, + "user": { + "username": "minicoders", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}", + "type": "user", + "display_name": "Frederic Guillot", + "links": { + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + } + } + }, + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/comments/19176265" + } + } + }, + "issue": { + "state": "closed", + "votes": 0, + "assignee": { + "username": "minicoders", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}", + "type": "user", + "display_name": "Frederic Guillot", + "links": { + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + } + } + }, + "links": { + "vote": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/vote" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook/issue/1/my-new-issue" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1" + }, + "watch": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/watch" + }, + "comments": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/comments" + }, + "attachments": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/attachments" + } + }, + "priority": "major", + "version": null, + "watches": 1, + "edited_on": null, + "reporter": { + "username": "minicoders", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}", + "type": "user", + "display_name": "Frederic Guillot", + "links": { + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + } + } + }, + "content": { + "html": "

test

", + "raw": "**test**", + "markup": "markdown" + }, + "component": null, + "created_on": "2015-06-21T02:17:40.990654+00:00", + "updated_on": "2015-06-21T02:54:40.249466+00:00", + "kind": "bug", + "id": 1, + "milestone": null, + "type": "issue", + "repository": { + "full_name": "minicoders/test-webhook", + "uuid": "{590fd9c4-0812-425e-8d72-ab08b4fd5735}", + "type": "repository", + "name": "test-webhook", + "links": { + "avatar": { + "href": "https://bitbucket.org/minicoders/test-webhook/avatar/16/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook" + } + } + }, + "title": "My new issue" + } +} \ No newline at end of file diff --git a/Test/fixtures/bitbucket_issue_opened.json b/Test/fixtures/bitbucket_issue_opened.json new file mode 100644 index 0000000..7891c23 --- /dev/null +++ b/Test/fixtures/bitbucket_issue_opened.json @@ -0,0 +1,112 @@ +{ + "issue": { + "type": "issue", + "links": { + "attachments": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/attachments" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook/issue/1/my-new-issue" + }, + "comments": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/comments" + }, + "vote": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/vote" + }, + "watch": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/watch" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1" + } + }, + "component": null, + "updated_on": "2015-06-21T02:17:40.990654+00:00", + "reporter": { + "links": { + "html": { + "href": "https://bitbucket.org/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + } + }, + "username": "minicoders", + "type": "user", + "display_name": "Frederic Guillot", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}" + }, + "watches": 1, + "kind": "bug", + "edited_on": null, + "created_on": "2015-06-21T02:17:40.990654+00:00", + "milestone": null, + "version": null, + "state": "new", + "assignee": null, + "content": { + "raw": "**test**", + "markup": "markdown", + "html": "

test

" + }, + "priority": "major", + "id": 1, + "votes": 0, + "title": "My new issue" + }, + "repository": { + "links": { + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook" + }, + "avatar": { + "href": "https://bitbucket.org/minicoders/test-webhook/avatar/16/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook" + } + }, + "type": "repository", + "full_name": "minicoders/test-webhook", + "uuid": "{590fd9c4-0812-425e-8d72-ab08b4fd5735}", + "name": "test-webhook", + "owner": { + "links": { + "html": { + "href": "https://bitbucket.org/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + } + }, + "username": "minicoders", + "type": "user", + "display_name": "Frederic Guillot", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}" + } + }, + "actor": { + "links": { + "html": { + "href": "https://bitbucket.org/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + } + }, + "username": "minicoders", + "type": "user", + "display_name": "Frederic Guillot", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}" + } +} \ No newline at end of file diff --git a/Test/fixtures/bitbucket_issue_reopened.json b/Test/fixtures/bitbucket_issue_reopened.json new file mode 100644 index 0000000..bb95091 --- /dev/null +++ b/Test/fixtures/bitbucket_issue_reopened.json @@ -0,0 +1,183 @@ +{ + "issue": { + "edited_on": null, + "watches": 1, + "created_on": "2015-06-21T02:17:40.990654+00:00", + "reporter": { + "username": "minicoders", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + } + }, + "display_name": "Frederic Guillot", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}", + "type": "user" + }, + "content": { + "markup": "markdown", + "raw": "**test**", + "html": "

test

" + }, + "id": 1, + "milestone": null, + "repository": { + "full_name": "minicoders/test-webhook", + "type": "repository", + "uuid": "{590fd9c4-0812-425e-8d72-ab08b4fd5735}", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook" + }, + "avatar": { + "href": "https://bitbucket.org/minicoders/test-webhook/avatar/16/" + } + }, + "name": "test-webhook" + }, + "component": null, + "version": null, + "votes": 0, + "priority": "major", + "type": "issue", + "state": "open", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1" + }, + "comments": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/comments" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook/issue/1/my-new-issue" + }, + "watch": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/watch" + }, + "attachments": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/attachments" + }, + "vote": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/vote" + } + }, + "kind": "bug", + "updated_on": "2015-06-21T14:56:49.739063+00:00", + "assignee": { + "username": "minicoders", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + } + }, + "display_name": "Frederic Guillot", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}", + "type": "user" + }, + "title": "My new issue" + }, + "comment": { + "id": 19181022, + "created_on": "2015-06-21T14:56:49.749362+00:00", + "content": { + "markup": "markdown", + "raw": null, + "html": "" + }, + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/comments/19181022" + } + }, + "user": { + "username": "minicoders", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + } + }, + "display_name": "Frederic Guillot", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}", + "type": "user" + }, + "updated_on": null + }, + "repository": { + "name": "test-webhook", + "owner": { + "username": "minicoders", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + } + }, + "display_name": "Frederic Guillot", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}", + "type": "user" + }, + "uuid": "{590fd9c4-0812-425e-8d72-ab08b4fd5735}", + "type": "repository", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook" + }, + "avatar": { + "href": "https://bitbucket.org/minicoders/test-webhook/avatar/16/" + } + }, + "full_name": "minicoders/test-webhook" + }, + "changes": { + "status": { + "new": "open", + "old": "closed" + } + }, + "actor": { + "username": "minicoders", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + } + }, + "display_name": "Frederic Guillot", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}", + "type": "user" + } +} \ No newline at end of file diff --git a/Test/fixtures/bitbucket_issue_unassigned.json b/Test/fixtures/bitbucket_issue_unassigned.json new file mode 100644 index 0000000..3cbab2e --- /dev/null +++ b/Test/fixtures/bitbucket_issue_unassigned.json @@ -0,0 +1,193 @@ +{ + "comment": { + "updated_on": null, + "content": { + "html": "", + "markup": "markdown", + "raw": null + }, + "created_on": "2015-06-21T15:07:45.787623+00:00", + "user": { + "display_name": "Frederic Guillot", + "username": "minicoders", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + } + }, + "type": "user", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}" + }, + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/comments/19181143" + } + }, + "id": 19181143 + }, + "issue": { + "state": "open", + "content": { + "html": "

test

", + "markup": "markdown", + "raw": "**test**" + }, + "milestone": null, + "type": "issue", + "version": null, + "title": "My new issue", + "assignee": null, + "kind": "bug", + "component": null, + "priority": "major", + "reporter": { + "display_name": "Frederic Guillot", + "username": "minicoders", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + } + }, + "type": "user", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}" + }, + "created_on": "2015-06-21T02:17:40.990654+00:00", + "edited_on": null, + "updated_on": "2015-06-21T15:07:45.775705+00:00", + "id": 1, + "votes": 0, + "repository": { + "full_name": "minicoders/test-webhook", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook" + }, + "avatar": { + "href": "https://bitbucket.org/minicoders/test-webhook/avatar/16/" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook" + } + }, + "type": "repository", + "uuid": "{590fd9c4-0812-425e-8d72-ab08b4fd5735}", + "name": "test-webhook" + }, + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1" + }, + "watch": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/watch" + }, + "vote": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/vote" + }, + "comments": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/comments" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook/issue/1/my-new-issue" + }, + "attachments": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/issues/1/attachments" + } + }, + "watches": 1 + }, + "actor": { + "display_name": "Frederic Guillot", + "username": "minicoders", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + } + }, + "type": "user", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}" + }, + "repository": { + "full_name": "minicoders/test-webhook", + "owner": { + "display_name": "Frederic Guillot", + "username": "minicoders", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + } + }, + "type": "user", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}" + }, + "type": "repository", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook" + }, + "avatar": { + "href": "https://bitbucket.org/minicoders/test-webhook/avatar/16/" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook" + } + }, + "name": "test-webhook", + "uuid": "{590fd9c4-0812-425e-8d72-ab08b4fd5735}" + }, + "changes": { + "responsible": { + "old": { + "is_system": false, + "username": "minicoders", + "name": "Frederic Guillot", + "billing_external_uuid": null, + "id": 1290132 + }, + "new": null + }, + "assignee": { + "old": { + "display_name": "Frederic Guillot", + "username": "minicoders", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + } + }, + "type": "user", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}" + }, + "new": null + } + } +} \ No newline at end of file diff --git a/Test/fixtures/bitbucket_push.json b/Test/fixtures/bitbucket_push.json new file mode 100644 index 0000000..f480b07 --- /dev/null +++ b/Test/fixtures/bitbucket_push.json @@ -0,0 +1,182 @@ +{ + "push": { + "changes": [ + { + "forced": false, + "old": { + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/refs/branches/master" + }, + "commits": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/commits/master" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook/branch/master" + } + }, + "name": "master", + "target": { + "date": "2015-06-21T00:50:37+00:00", + "hash": "b6b46580eb9b20a06396f5f697ea1a55cf170e69", + "message": "test edited online with Bitbucket for task #5", + "type": "commit", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/commit/b6b46580eb9b20a06396f5f697ea1a55cf170e69" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook/commits/b6b46580eb9b20a06396f5f697ea1a55cf170e69" + } + }, + "parents": [ + { + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/commit/7251db4b505cbfca3f845ebcff0ec0ddc4003ed8" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook/commits/7251db4b505cbfca3f845ebcff0ec0ddc4003ed8" + } + }, + "type": "commit", + "hash": "7251db4b505cbfca3f845ebcff0ec0ddc4003ed8" + } + ], + "author": { + "raw": "Frederic Guillot ", + "user": { + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + } + }, + "type": "user", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}", + "username": "minicoders", + "display_name": "Frederic Guillot" + } + } + }, + "type": "branch" + }, + "created": false, + "links": { + "diff": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/diff/824059cce7667d3f8d8780cc707391be821e0ea6..b6b46580eb9b20a06396f5f697ea1a55cf170e69" + }, + "commits": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/commits?include=824059cce7667d3f8d8780cc707391be821e0ea6exclude=b6b46580eb9b20a06396f5f697ea1a55cf170e69" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook/branches/compare/824059cce7667d3f8d8780cc707391be821e0ea6..b6b46580eb9b20a06396f5f697ea1a55cf170e69" + } + }, + "new": { + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/refs/branches/master" + }, + "commits": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/commits/master" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook/branch/master" + } + }, + "name": "master", + "target": { + "date": "2015-06-21T03:15:08+00:00", + "hash": "824059cce7667d3f8d8780cc707391be821e0ea6", + "message": "Test another commit #2\n", + "type": "commit", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/commit/824059cce7667d3f8d8780cc707391be821e0ea6" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook/commits/824059cce7667d3f8d8780cc707391be821e0ea6" + } + }, + "parents": [ + { + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook/commit/24aa9d82bbb6f9a60f743fe538deb0a44622fc98" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook/commits/24aa9d82bbb6f9a60f743fe538deb0a44622fc98" + } + }, + "type": "commit", + "hash": "24aa9d82bbb6f9a60f743fe538deb0a44622fc98" + } + ], + "author": { + "raw": "Frederic Guillot " + } + }, + "type": "branch" + }, + "closed": false + } + ] + }, + "repository": { + "name": "test-webhook", + "owner": { + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + } + }, + "type": "user", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}", + "username": "minicoders", + "display_name": "Frederic Guillot" + }, + "uuid": "{590fd9c4-0812-425e-8d72-ab08b4fd5735}", + "type": "repository", + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/repositories/minicoders/test-webhook" + }, + "html": { + "href": "https://bitbucket.org/minicoders/test-webhook" + }, + "avatar": { + "href": "https://bitbucket.org/minicoders/test-webhook/avatar/16/" + } + }, + "full_name": "minicoders/test-webhook" + }, + "actor": { + "links": { + "self": { + "href": "https://bitbucket.org/api/2.0/users/minicoders" + }, + "html": { + "href": "https://bitbucket.org/minicoders" + }, + "avatar": { + "href": "https://bitbucket.org/account/minicoders/avatar/32/" + } + }, + "type": "user", + "uuid": "{fc59b45a-f68b-4fc1-ad1f-a17d4f17cd2c}", + "username": "minicoders", + "display_name": "Frederic Guillot" + } +} \ No newline at end of file diff --git a/WebhookHandler.php b/WebhookHandler.php new file mode 100644 index 0000000..cd3b1e1 --- /dev/null +++ b/WebhookHandler.php @@ -0,0 +1,312 @@ +project_id = $project_id; + } + + /** + * Parse incoming events + * + * @access public + * @param string $type Bitbucket event type + * @param array $payload Bitbucket event + * @return boolean + */ + public function parsePayload($type, array $payload) + { + switch ($type) { + case 'issue:comment_created': + return $this->handleCommentCreated($payload); + case 'issue:created': + return $this->handleIssueOpened($payload); + case 'issue:updated': + return $this->handleIssueUpdated($payload); + case 'repo:push': + return $this->handlePush($payload); + } + + return false; + } + + /** + * Parse comment issue events + * + * @access public + * @param array $payload + * @return boolean + */ + public function handleCommentCreated(array $payload) + { + $task = $this->taskFinder->getByReference($this->project_id, $payload['issue']['id']); + + if (! empty($task)) { + $user = $this->user->getByUsername($payload['actor']['username']); + + if (! empty($user) && ! $this->projectPermission->isAssignable($this->project_id, $user['id'])) { + $user = array(); + } + + $event = array( + 'project_id' => $this->project_id, + 'reference' => $payload['comment']['id'], + 'comment' => $payload['comment']['content']['raw']."\n\n[".t('By @%s on Bitbucket', $payload['actor']['display_name']).']('.$payload['comment']['links']['html']['href'].')', + 'user_id' => ! empty($user) ? $user['id'] : 0, + 'task_id' => $task['id'], + ); + + $this->dispatcher->dispatch( + self::EVENT_ISSUE_COMMENT, + new GenericEvent($event) + ); + + return true; + } + + return false; + } + + /** + * Handle new issues + * + * @access public + * @param array $payload + * @return boolean + */ + public function handleIssueOpened(array $payload) + { + $event = array( + 'project_id' => $this->project_id, + 'reference' => $payload['issue']['id'], + 'title' => $payload['issue']['title'], + 'description' => $payload['issue']['content']['raw']."\n\n[".t('Bitbucket Issue').']('.$payload['issue']['links']['html']['href'].')', + ); + + $this->dispatcher->dispatch( + self::EVENT_ISSUE_OPENED, + new GenericEvent($event) + ); + + return true; + } + + /** + * Handle issue updates + * + * @access public + * @param array $payload + * @return boolean + */ + public function handleIssueUpdated(array $payload) + { + $task = $this->taskFinder->getByReference($this->project_id, $payload['issue']['id']); + + if (empty($task)) { + return false; + } + + if (isset($payload['changes']['status'])) { + return $this->handleStatusChange($task, $payload); + } elseif (isset($payload['changes']['assignee'])) { + return $this->handleAssigneeChange($task, $payload); + } + + return false; + } + + /** + * Handle issue status change + * + * @access public + * @param array $task + * @param array $payload + * @return boolean + */ + public function handleStatusChange(array $task, array $payload) + { + $event = array( + 'project_id' => $this->project_id, + 'task_id' => $task['id'], + 'reference' => $payload['issue']['id'], + ); + + switch ($payload['issue']['state']) { + case 'closed': + $this->dispatcher->dispatch(self::EVENT_ISSUE_CLOSED, new GenericEvent($event)); + return true; + case 'open': + $this->dispatcher->dispatch(self::EVENT_ISSUE_REOPENED, new GenericEvent($event)); + return true; + } + + return false; + } + + /** + * Handle issue assignee change + * + * @access public + * @param array $task + * @param array $payload + * @return boolean + */ + public function handleAssigneeChange(array $task, array $payload) + { + if (empty($payload['issue']['assignee'])) { + return $this->handleIssueUnassigned($task, $payload); + } + + return $this->handleIssueAssigned($task, $payload); + } + + /** + * Handle issue assigned + * + * @access public + * @param array $task + * @param array $payload + * @return boolean + */ + public function handleIssueAssigned(array $task, array $payload) + { + $user = $this->user->getByUsername($payload['issue']['assignee']['username']); + + if (empty($user)) { + return false; + } + + if (! $this->projectPermission->isAssignable($this->project_id, $user['id'])) { + return false; + } + + $event = array( + 'project_id' => $this->project_id, + 'task_id' => $task['id'], + 'owner_id' => $user['id'], + 'reference' => $payload['issue']['id'], + ); + + $this->dispatcher->dispatch(self::EVENT_ISSUE_ASSIGNEE_CHANGE, new GenericEvent($event)); + + return true; + } + + /** + * Handle issue unassigned + * + * @access public + * @param array $task + * @param array $payload + * @return boolean + */ + public function handleIssueUnassigned(array $task, array $payload) + { + $event = array( + 'project_id' => $this->project_id, + 'task_id' => $task['id'], + 'owner_id' => 0, + 'reference' => $payload['issue']['id'], + ); + + $this->dispatcher->dispatch(self::EVENT_ISSUE_ASSIGNEE_CHANGE, new GenericEvent($event)); + + return true; + } + + /** + * Parse push events + * + * @access public + * @param array $payload + * @return boolean + */ + public function handlePush(array $payload) + { + if (isset($payload['push']['changes'])) { + foreach ($payload['push']['changes'] as $change) { + if (isset($change['new']['target']) && $this->handleCommit($change['new']['target'], $payload['actor'])) { + return true; + } + } + } + + return false; + } + + /** + * Parse commit + * + * @access public + * @param array $commit Bitbucket commit + * @param array $actor Bitbucket actor + * @return boolean + */ + public function handleCommit(array $commit, array $actor) + { + $task_id = $this->task->getTaskIdFromText($commit['message']); + + if (empty($task_id)) { + return false; + } + + $task = $this->taskFinder->getById($task_id); + + if (empty($task)) { + return false; + } + + if ($task['project_id'] != $this->project_id) { + return false; + } + + $this->dispatcher->dispatch( + self::EVENT_COMMIT, + new GenericEvent(array( + 'task_id' => $task_id, + 'commit_message' => $commit['message'], + 'commit_url' => $commit['links']['html']['href'], + 'comment' => $commit['message']."\n\n[".t('Commit made by @%s on Bitbucket', $actor['display_name']).']('.$commit['links']['html']['href'].')', + ) + $task) + ); + + return true; + } +}