├── .gitignore
├── Gemfile
├── Gemfile.lock
├── README.md
├── app
├── controllers
│ └── webhook_settings_controller.rb
├── models
│ └── webhook.rb
└── views
│ └── webhook_settings
│ └── _show.html.erb
├── config
├── locales
│ ├── en.yml
│ └── ja.yml
└── routes.rb
├── db
└── migrate
│ └── 001_create_webhooks.rb
├── init.rb
├── lib
├── redmine_webhook.rb
└── redmine_webhook
│ ├── author_wrapper.rb
│ ├── custom_field_value_wrapper.rb
│ ├── issue_wrapper.rb
│ ├── journal_detail_wrapper.rb
│ ├── journal_wrapper.rb
│ ├── priority_wrapper.rb
│ ├── project_wrapper.rb
│ ├── projects_helper_patch.rb
│ ├── status_wrapper.rb
│ ├── tracker_wrapper.rb
│ └── webhook_listener.rb
└── test
├── test_helper.rb
└── unit
└── webhook_test.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gem 'faraday'
4 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | specs:
3 | faraday (0.8.8)
4 | multipart-post (~> 1.2.0)
5 | multipart-post (1.2.0)
6 |
7 | PLATFORMS
8 | ruby
9 |
10 | DEPENDENCIES
11 | faraday
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Redmine WebHook Plugin
2 | ======================
3 |
4 | A Redmine plugin posts webhook on creating and updating tickets.
5 |
6 | Author
7 | ------------------------------
8 | * @suer
9 |
10 | Install
11 | ------------------------------
12 | Type below commands:
13 |
14 | $ cd $RAILS_ROOT/plugins
15 | $ git clone https://github.com/suer/redmine_webhook.git
16 | $ rake redmine:plugins:migrate RAILS_ENV=production
17 |
18 | Then, restart your redmine.
19 |
20 | Post Data Example
21 | ------------------------------
22 |
23 | ### Issue opened
24 |
25 | {
26 | "payload": {
27 | "issue": {
28 | "author": {
29 | "icon_url": "http://www.gravatar.com/avatar/example",
30 | "identity_url": null,
31 | "lastname": "user",
32 | "firstname": "test",
33 | "mail": "test@example.com",
34 | "login": "test",
35 | "id": 3
36 | },
37 | "assignee": {
38 | "icon_url": "http://www.gravatar.com/avatar/example",
39 | "identity_url": null,
40 | "lastname": "user",
41 | "firstname": "test",
42 | "mail": "test@example.com",
43 | "login": "test",
44 | "id": 3
45 | },
46 | "priority": {
47 | "name": "normal",
48 | "id": 2
49 | },
50 | "tracker": {
51 | "name": "bug",
52 | "id": 1
53 | },
54 | "parent_id": null,
55 | "root_id": 191,
56 | "closed_on": null,
57 | "updated_on": "2014-03-01T15:17:48Z",
58 | "created_on": "2014-03-01T15:17:48Z",
59 | "description": "I'm having a problem with this.",
60 | "subject": "Found a bug",
61 | "id": 191,
62 | "done_ratio": 0,
63 | "start_date": "2014-03-02",
64 | "due_date": null,
65 | "estimated_hours": null,
66 | "is_private": false,
67 | "lock_version": 0,
68 | "project": {
69 | "homepage": "",
70 | "created_on": "2013-01-12T11:50:26Z",
71 | "description": "",
72 | "name": "Test Project",
73 | "identifier": "test",
74 | "id": 4
75 | },
76 | "status": {
77 | "name": "new",
78 | "id": 1
79 | }
80 | },
81 | "action": "opened",
82 | "url": "https://example.com"
83 | }
84 | }
85 |
86 | ### Issue updated
87 |
88 | {
89 | "payload": {
90 | "url": "https://example.com",
91 | "journal": {
92 | "details": [],
93 | "author": {
94 | "icon_url": "http://www.gravatar.com/avatar/example",
95 | "identity_url": null,
96 | "lastname": "user",
97 | "firstname": "test",
98 | "mail": "test@example.com",
99 | "login": "test",
100 | "id": 3
101 | },
102 | "assignee": {
103 | "icon_url": "http://www.gravatar.com/avatar/example",
104 | "identity_url": null,
105 | "lastname": "user",
106 | "firstname": "test",
107 | "mail": "test@example.com",
108 | "login": "test",
109 | "id": 3
110 | },
111 | "private_notes": false,
112 | "created_on": "2014-03-01T16:22:46Z",
113 | "notes": "Fixed",
114 | "id": 195
115 | },
116 | "issue": {
117 | "author": {
118 | "icon_url": "http://www.gravatar.com/avatar/example",
119 | "identity_url": null,
120 | "lastname": "user",
121 | "firstname": "test",
122 | "mail": "test@example.com",
123 | "login": "test",
124 | "id": 3
125 | },
126 | "priority": {
127 | "name": "normal",
128 | "id": 2
129 | },
130 | "tracker": {
131 | "name": "bug",
132 | "id": 1
133 | },
134 | "parent_id": null,
135 | "root_id": 196,
136 | "closed_on": null,
137 | "updated_on": "2014-03-01T16:22:46Z",
138 | "created_on": "2014-03-01T15:44:22Z",
139 | "description": "test",
140 | "subject": "Found a bug",
141 | "id": 196,
142 | "done_ratio": 0,
143 | "start_date": "2014-03-02",
144 | "due_date": null,
145 | "estimated_hours": null,
146 | "is_private": false,
147 | "lock_version": 2,
148 | "project": {
149 | "homepage": "",
150 | "created_on": "2013-01-12T11:50:26Z",
151 | "description": "",
152 | "name": "Test Project",
153 | "identifier": "test",
154 | "id": 4
155 | },
156 | "status": {
157 | "name": "normal",
158 | "id": 1
159 | }
160 | },
161 | "action": "updated"
162 | }
163 | }
164 |
165 | Requirements
166 | ------------------------------
167 | * Redmine 4.0 or later
168 |
169 |
170 | Skipping webhooks
171 | ------------------------------
172 | When a webhook triggers a change via REST API, this would trigger another webhook.
173 | If you need to prevent this, the API request can include the `X-Skip-Webhooks` header, which will prevent webhooks being triggered by that request.
174 |
175 |
176 | Known Limitations
177 | ------------------------------
178 |
179 | An update from context menu doesn't call a webhook event.
180 | It is caused by a lack of functionality hooking in Redmine.
181 | Please see https://github.com/suer/redmine_webhook/issues/4 for details.
182 |
183 | This limitation has been affected on all Redmine versions includes 2.4, 2.6,
184 | and 3.0. It is not fixed in end of April, 2015.
185 |
186 |
187 | License
188 | ------------------------------
189 | The MIT License (MIT)
190 | Copyright (c) suer
191 |
192 | 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:
193 |
194 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
195 |
196 | 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.
197 |
--------------------------------------------------------------------------------
/app/controllers/webhook_settings_controller.rb:
--------------------------------------------------------------------------------
1 | class WebhookSettingsController < ApplicationController
2 | before_action :find_project, :authorize
3 | accept_api_auth :index, :create, :update, :destroy
4 |
5 | def index
6 | render :json => Webhook.where(:project_id => @project.id).as_json(:only => [:id, :url])
7 | end
8 |
9 | def create
10 | webhook = Webhook.new(:project_id => @project.id)
11 | webhook.url = params[:url]
12 | if webhook.save
13 | flash[:notice] = l(:notice_successful_create_webhook)
14 | else
15 | flash[:error] = l(:notice_fail_create_webhook)
16 | end
17 | redirect_to settings_project_path(@project, :tab => 'webhook')
18 | end
19 | def update
20 | id = params[:webhook_id]
21 | webhook = Webhook.where(:project_id => @project.id).where(:id => id).first
22 | webhook.url = params[:url]
23 | if webhook.url.blank? ? webhook.destroy : webhook.save
24 | flash[:notice] = l(:notice_successful_update_webhook)
25 | else
26 | flash[:error] = l(:notice_fail_update_webhook)
27 | end
28 | redirect_to settings_project_path(@project, :tab => 'webhook')
29 | end
30 | def destroy
31 | id = params[:webhook_id]
32 | webhook = Webhook.where(:project_id => @project.id).where(:id => id).first
33 | if webhook.destroy
34 | flash[:notice] = l(:notice_successful_delete_webhook)
35 | else
36 | flash[:error] = l(:notice_fail_delete_webhook)
37 | end
38 | redirect_to settings_project_path(@project, :tab => 'webhook')
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/app/models/webhook.rb:
--------------------------------------------------------------------------------
1 | class Webhook < ActiveRecord::Base
2 | unloadable
3 | belongs_to :project
4 | end
5 |
--------------------------------------------------------------------------------
/app/views/webhook_settings/_show.html.erb:
--------------------------------------------------------------------------------
1 | <% if Webhook.where(:project_id => @project.id).first%>
2 |
3 | <% Webhook.where(:project_id => @project.id).each do |webhook|%>
4 | <%= form_tag(update_webhook_path(@project, webhook.id), :method => :put, :class => "tabular") do %>
5 |
6 | URL
7 | <%= text_field_tag :url, webhook.url, :size => 80 %>
8 | <%= submit_tag l(:button_update) %>
9 |
10 | <% end %>
11 | <%= link_to l(:button_delete), delete_webhook_path(@project, webhook.id), :class => "icon icon-del", :method => :delete, :confirm => l(:text_are_you_sure) %>
12 |
13 | <% end %>
14 |
15 | <% end %>
16 |
17 | <%= form_tag(create_webhook_path(@project), :method => :post, :class => "tabular") do %>
18 |
19 |
20 | URL
21 | <%= text_field_tag :url, '', :size => 80 %>
22 | <%= submit_tag l(:button_add) %>
23 |
24 |
25 | <% end %>
26 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # English strings go here for Rails i18n
2 | en:
3 | webhook: WebHook
4 | notice_fail_create_webhook: Webhook creation failed.
5 | notice_successful_create_webhook: Webhook created successfully.
6 | notice_fail_update_webhook: Webhook update failed.
7 | notice_successful_update_webhook : Webhook updated successfully.
8 | notice_fail_delete_webhook: Webhook delete failed.
9 | notice_successful_delete_webhook : Webhook deleted successfully.
10 |
11 |
--------------------------------------------------------------------------------
/config/locales/ja.yml:
--------------------------------------------------------------------------------
1 | ja:
2 | webhook: WebHook
3 | notice_fail_create_webhook: Webhook の作成に失敗しました
4 | notice_successful_create_webhook: Webhook の作成に成功しました
5 | notice_fail_update_webhook: Webhook の更新に失敗しました
6 | notice_successful_update_webhook : Webhook の更新に成功しました
7 | notice_fail_delete_webhook: Webhook の削除に失敗しました
8 | notice_successful_delete_webhook : Webhook の削除に成功しました
9 |
10 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | get 'projects/:id/webhook_settings', :controller => 'webhook_settings', :action => 'index', :as => :list_webhook_settings
2 | get 'projects/:id/webhook_settings/show', :controller => 'webhook_settings', :action => 'show', :as => :show_webhook_settings
3 | post 'projects/:id/webhook_settings/create', :controller => 'webhook_settings', :action => 'create', :as => :create_webhook
4 | put 'projects/:id/webhook_settings/:webhook_id', :controller => 'webhook_settings', :action => 'update', :as => :update_webhook
5 | delete 'projects/:id/webhook_settings/:webhook_id', :controller => 'webhook_settings', :action => 'destroy', :as => :delete_webhook
6 |
--------------------------------------------------------------------------------
/db/migrate/001_create_webhooks.rb:
--------------------------------------------------------------------------------
1 | class CreateWebhooks < ActiveRecord::Migration[4.2]
2 | def change
3 | create_table :webhooks do |t|
4 | t.string :url
5 | t.integer :project_id
6 | end
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/init.rb:
--------------------------------------------------------------------------------
1 | if Rails.try(:autoloaders).try(:zeitwerk_enabled?)
2 | Rails.autoloaders.main.push_dir File.dirname(__FILE__) + '/lib/redmine_webhook'
3 | RedmineWebhook::ProjectsHelperPatch
4 | RedmineWebhook::WebhookListener
5 | else
6 | require "redmine_webhook"
7 | end
8 |
9 | Redmine::Plugin.register :redmine_webhook do
10 | name 'Redmine Webhook plugin'
11 | author 'suer'
12 | description 'A Redmine plugin posts webhook on creating and updating tickets'
13 | version '0.0.5'
14 | url 'https://github.com/suer/redmine_webhook'
15 | author_url 'http://d.hatena.ne.jp/suer'
16 | project_module :webhooks do
17 | permission :manage_hook, {:webhook_settings => [:index, :show, :update, :create, :destroy]}, :require => :member
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/redmine_webhook.rb:
--------------------------------------------------------------------------------
1 | unless Rails.try(:autoloaders).try(:zeitwerk_enabled?)
2 | require 'redmine_webhook/projects_helper_patch'
3 | require 'redmine_webhook/issue_wrapper'
4 | require 'redmine_webhook/webhook_listener'
5 | end
6 |
7 | module RedmineWebhook
8 | end
9 |
--------------------------------------------------------------------------------
/lib/redmine_webhook/author_wrapper.rb:
--------------------------------------------------------------------------------
1 | module RedmineWebhook
2 | class AuthorWrapper
3 | include GravatarHelper::PublicMethods
4 | include ERB::Util
5 |
6 | def initialize(author)
7 | @author = author
8 | end
9 |
10 | def to_hash
11 | return nil unless @author
12 | {
13 | :id => @author.id,
14 | :login => @author.login,
15 | :mail => @author.mail,
16 | :firstname => @author.firstname,
17 | :lastname => @author.lastname,
18 | :identity_url => @author.try(:identity_url),
19 | :icon_url => icon_url
20 | }
21 | end
22 |
23 | def icon_url
24 | if @author.mail.blank?
25 | icon_url = nil
26 | else
27 | icon_url = gravatar_url(@author.mail)
28 | end
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/lib/redmine_webhook/custom_field_value_wrapper.rb:
--------------------------------------------------------------------------------
1 | class RedmineWebhook::CustomFieldValueWrapper
2 | def initialize(custom_field_value)
3 | @custom_field_value = custom_field_value
4 | end
5 |
6 | def to_hash
7 | {
8 | custom_field_id: @custom_field_value.custom_field_id,
9 | custom_field_name: @custom_field_value.custom_field.name,
10 | value: @custom_field_value.value
11 | }
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/redmine_webhook/issue_wrapper.rb:
--------------------------------------------------------------------------------
1 | module RedmineWebhook
2 | class IssueWrapper
3 | def initialize(issue)
4 | @issue = issue
5 | end
6 |
7 | def to_hash
8 | {
9 | :id => @issue.id,
10 | :subject => @issue.subject,
11 | :description => @issue.description,
12 | :created_on => @issue.created_on,
13 | :updated_on => @issue.updated_on,
14 | :closed_on => @issue.closed_on,
15 | :root_id => @issue.root_id,
16 | :parent_id => @issue.parent_id,
17 | :done_ratio => @issue.done_ratio,
18 | :start_date => @issue.start_date,
19 | :due_date => @issue.due_date,
20 | :estimated_hours => @issue.estimated_hours,
21 | :is_private => @issue.is_private,
22 | :lock_version => @issue.lock_version,
23 | :custom_field_values => @issue.custom_field_values.collect { |value| RedmineWebhook::CustomFieldValueWrapper.new(value).to_hash },
24 | :project => RedmineWebhook::ProjectWrapper.new(@issue.project).to_hash,
25 | :status => RedmineWebhook::StatusWrapper.new(@issue.status).to_hash,
26 | :tracker => RedmineWebhook::TrackerWrapper.new(@issue.tracker).to_hash,
27 | :priority => RedmineWebhook::PriorityWrapper.new(@issue.priority).to_hash,
28 | :author => RedmineWebhook::AuthorWrapper.new(@issue.author).to_hash,
29 | :assignee => RedmineWebhook::AuthorWrapper.new(@issue.assigned_to).to_hash,
30 | :watchers => @issue.watcher_users.collect{|u| RedmineWebhook::AuthorWrapper.new(u).to_hash}
31 | }
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/lib/redmine_webhook/journal_detail_wrapper.rb:
--------------------------------------------------------------------------------
1 | module RedmineWebhook
2 | class JournalDetailWrapper
3 | def initialize(journal_detail)
4 | @journal_detail = journal_detail
5 | end
6 |
7 | def to_hash
8 | {
9 | :id => @journal_detail.id,
10 | :value => @journal_detail.value,
11 | :old_value => @journal_detail.old_value,
12 | :prop_key => @journal_detail.prop_key,
13 | :property => @journal_detail.property
14 | }
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/lib/redmine_webhook/journal_wrapper.rb:
--------------------------------------------------------------------------------
1 | module RedmineWebhook
2 | class JournalWrapper
3 | def initialize(journal)
4 | @journal = journal
5 | end
6 |
7 | def to_hash
8 | {
9 | :id => @journal.id,
10 | :notes => @journal.notes,
11 | :created_on => @journal.created_on,
12 | :private_notes => @journal.private_notes,
13 | :author => RedmineWebhook::AuthorWrapper.new(@journal.user).to_hash,
14 | :details => @journal.details.map {|detail| RedmineWebhook::JournalDetailWrapper.new(detail).to_hash }
15 | }
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/lib/redmine_webhook/priority_wrapper.rb:
--------------------------------------------------------------------------------
1 | module RedmineWebhook
2 | class PriorityWrapper
3 | def initialize(priority)
4 | @priority = priority
5 | end
6 |
7 | def to_hash
8 | {
9 | :id => @priority.id,
10 | :name => @priority.name
11 | }
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/redmine_webhook/project_wrapper.rb:
--------------------------------------------------------------------------------
1 | class RedmineWebhook::ProjectWrapper
2 | def initialize(project)
3 | @project = project
4 | end
5 |
6 | def to_hash
7 | {
8 | :id => @project.id,
9 | :identifier => @project.identifier,
10 | :name => @project.name,
11 | :description => @project.description,
12 | :created_on => @project.created_on,
13 | :homepage => @project.homepage,
14 | }
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/lib/redmine_webhook/projects_helper_patch.rb:
--------------------------------------------------------------------------------
1 | module RedmineWebhook
2 | module ProjectsHelperPatch
3 | def project_settings_tabs
4 | tabs = super
5 | webhook_tab = {
6 | :name => 'webhook',
7 | :controller => 'webhook_settings',
8 | :action => :show,
9 | :partial => 'webhook_settings/show',
10 | :label => :webhook
11 | }
12 | tabs << webhook_tab if User.current.allowed_to?(:manage_hook, @project)
13 | tabs
14 | end
15 | end
16 | end
17 |
18 | ProjectsController.send(:helper, RedmineWebhook::ProjectsHelperPatch)
19 |
--------------------------------------------------------------------------------
/lib/redmine_webhook/status_wrapper.rb:
--------------------------------------------------------------------------------
1 | module RedmineWebhook
2 | class StatusWrapper
3 | def initialize(status)
4 | @status = status
5 | end
6 |
7 | def to_hash
8 | {
9 | :id => @status.id,
10 | :name => @status.name
11 | }
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/redmine_webhook/tracker_wrapper.rb:
--------------------------------------------------------------------------------
1 | module RedmineWebhook
2 | class TrackerWrapper
3 | def initialize(tracker)
4 | @tracker = tracker
5 | end
6 |
7 | def to_hash
8 | {
9 | :id => @tracker.id,
10 | :name => @tracker.name
11 | }
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/redmine_webhook/webhook_listener.rb:
--------------------------------------------------------------------------------
1 | module RedmineWebhook
2 | class WebhookListener < Redmine::Hook::Listener
3 |
4 | def skip_webhooks(context)
5 | return true unless context[:request]
6 | return true if context[:request].headers['X-Skip-Webhooks']
7 |
8 | false
9 | end
10 |
11 | def controller_issues_new_after_save(context = {})
12 | return if skip_webhooks(context)
13 | issue = context[:issue]
14 | controller = context[:controller]
15 | project = issue.project
16 | webhooks = Webhook.where(:project_id => project.project.id)
17 | webhooks = Webhook.where(:project_id => 0) unless webhooks && webhooks.length > 0
18 | return unless webhooks
19 | post(webhooks, issue_to_json(issue, controller))
20 | end
21 |
22 | def controller_issues_edit_after_save(context = {})
23 | return if skip_webhooks(context)
24 | journal = context[:journal]
25 | controller = context[:controller]
26 | issue = context[:issue]
27 | project = issue.project
28 | webhooks = Webhook.where(:project_id => project.project.id)
29 | webhooks = Webhook.where(:project_id => 0) unless webhooks && webhooks.length > 0
30 | return unless webhooks
31 | post(webhooks, journal_to_json(issue, journal, controller))
32 | end
33 |
34 | def controller_issues_bulk_edit_after_save(context = {})
35 | return if skip_webhooks(context)
36 | journal = context[:journal]
37 | controller = context[:controller]
38 | issue = context[:issue]
39 | project = issue.project
40 | webhooks = Webhook.where(:project_id => project.project.id)
41 | webhooks = Webhook.where(:project_id => 0) unless webhooks && webhooks.length > 0
42 | return unless webhooks
43 | post(webhooks, journal_to_json(issue, journal, controller))
44 | end
45 |
46 | def model_changeset_scan_commit_for_issue_ids_pre_issue_update(context = {})
47 | issue = context[:issue]
48 | journal = issue.current_journal
49 | webhooks = Webhook.where(:project_id => issue.project.project.id)
50 | webhooks = Webhook.where(:project_id => 0) unless webhooks && webhooks.length > 0
51 | return unless webhooks
52 | post(webhooks, journal_to_json(issue, journal, nil))
53 | end
54 |
55 | private
56 | def issue_to_json(issue, controller)
57 | {
58 | :payload => {
59 | :action => 'opened',
60 | :issue => RedmineWebhook::IssueWrapper.new(issue).to_hash,
61 | :url => controller.issue_url(issue)
62 | }
63 | }.to_json
64 | end
65 |
66 | def journal_to_json(issue, journal, controller)
67 | {
68 | :payload => {
69 | :action => 'updated',
70 | :issue => RedmineWebhook::IssueWrapper.new(issue).to_hash,
71 | :journal => RedmineWebhook::JournalWrapper.new(journal).to_hash,
72 | :url => controller.nil? ? 'not yet implemented' : controller.issue_url(issue)
73 | }
74 | }.to_json
75 | end
76 |
77 | def post(webhooks, request_body)
78 | Thread.start do
79 | webhooks.each do |webhook|
80 | begin
81 | Faraday.post do |req|
82 | req.url webhook.url
83 | req.headers['Content-Type'] = 'application/json'
84 | req.body = request_body
85 | end
86 | rescue => e
87 | Rails.logger.error e
88 | end
89 | end
90 | end
91 | end
92 | end
93 | end
94 |
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | # Load the Redmine helper
2 | require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper')
3 |
--------------------------------------------------------------------------------
/test/unit/webhook_test.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../../test_helper', __FILE__)
2 |
3 | class WebhookTest < ActiveSupport::TestCase
4 |
5 | # Replace this with your real tests.
6 | def test_truth
7 | assert true
8 | end
9 | end
10 |
--------------------------------------------------------------------------------