Skip to content
Snippets Groups Projects
Commit e0ab5618 authored by Alexander Randa's avatar Alexander Randa Committed by Rémy Coutable
Browse files

Wrong data type when testing webhooks

parent 020b6a0b
No related branches found
No related tags found
No related merge requests found
Showing
with 234 additions and 81 deletions
Loading
Loading
@@ -10,9 +10,9 @@ class Admin::HookLogsController < Admin::ApplicationController
end
 
def retry
status, message = hook.execute(hook_log.request_data, hook_log.trigger)
result = hook.execute(hook_log.request_data, hook_log.trigger)
 
set_hook_execution_notice(status, message)
set_hook_execution_notice(result)
 
redirect_to edit_admin_hook_path(@hook)
end
Loading
Loading
Loading
Loading
@@ -38,9 +38,9 @@ class Admin::HooksController < Admin::ApplicationController
end
 
def test
status, message = hook.execute(sample_hook_data, 'system_hooks')
result = TestHooks::SystemService.new(hook, current_user, params[:trigger]).execute
 
set_hook_execution_notice(status, message)
set_hook_execution_notice(result)
 
redirect_back_or_default
end
Loading
Loading
@@ -66,15 +66,4 @@ class Admin::HooksController < Admin::ApplicationController
:url
)
end
def sample_hook_data
{
event_name: "project_create",
name: "Ruby",
path: "ruby",
project_id: 1,
owner_name: "Someone",
owner_email: "example@gitlabhq.com"
}
end
end
Loading
Loading
@@ -3,11 +3,14 @@ module HooksExecution
 
private
 
def set_hook_execution_notice(status, message)
if status && status >= 200 && status < 400
flash[:notice] = "Hook executed successfully: HTTP #{status}"
elsif status
flash[:alert] = "Hook executed successfully but returned HTTP #{status} #{message}"
def set_hook_execution_notice(result)
http_status = result[:http_status]
message = result[:message]
if http_status && http_status >= 200 && http_status < 400
flash[:notice] = "Hook executed successfully: HTTP #{http_status}"
elsif http_status
flash[:alert] = "Hook executed successfully but returned HTTP #{http_status} #{message}"
else
flash[:alert] = "Hook execution failed: #{message}"
end
Loading
Loading
Loading
Loading
@@ -14,9 +14,9 @@ class Projects::HookLogsController < Projects::ApplicationController
end
 
def retry
status, message = hook.execute(hook_log.request_data, hook_log.trigger)
result = hook.execute(hook_log.request_data, hook_log.trigger)
 
set_hook_execution_notice(status, message)
set_hook_execution_notice(result)
 
redirect_to edit_project_hook_path(@project, @hook)
end
Loading
Loading
Loading
Loading
@@ -9,6 +9,10 @@ class Projects::HooksController < Projects::ApplicationController
 
layout "project_settings"
 
def index
redirect_to project_settings_integrations_path(@project)
end
def create
@hook = @project.hooks.new(hook_params)
@hook.save
Loading
Loading
@@ -33,13 +37,9 @@ class Projects::HooksController < Projects::ApplicationController
end
 
def test
if !@project.empty_repo?
status, message = TestHookService.new.execute(hook, current_user)
result = TestHooks::ProjectService.new(hook, current_user, params[:trigger]).execute
 
set_hook_execution_notice(status, message)
else
flash[:alert] = 'Hook execution failed. Ensure the project has commits.'
end
set_hook_execution_notice(result)
 
redirect_back_or_default(default: { action: 'index' })
end
Loading
Loading
module HooksHelper
def link_to_test_hook(hook, trigger)
path = case hook
when ProjectHook
project = hook.project
test_project_hook_path(project, hook, trigger: trigger)
when SystemHook
test_admin_hook_path(hook, trigger: trigger)
end
trigger_human_name = trigger.to_s.tr('_', ' ').camelize
link_to path, rel: 'nofollow' do
content_tag(:span, trigger_human_name)
end
end
end
class ProjectHook < WebHook
belongs_to :project
TRIGGERS = {
push_hooks: :push_events,
tag_push_hooks: :tag_push_events,
issue_hooks: :issues_events,
confidential_issue_hooks: :confidential_issues_events,
note_hooks: :note_events,
merge_request_hooks: :merge_requests_events,
job_hooks: :job_events,
pipeline_hooks: :pipeline_events,
wiki_page_hooks: :wiki_page_events
}.freeze
TRIGGERS.each do |trigger, event|
scope trigger, -> { where(event => true) }
end
 
scope :issue_hooks, -> { where(issues_events: true) }
scope :confidential_issue_hooks, -> { where(confidential_issues_events: true) }
scope :note_hooks, -> { where(note_events: true) }
scope :merge_request_hooks, -> { where(merge_requests_events: true) }
scope :job_hooks, -> { where(job_events: true) }
scope :pipeline_hooks, -> { where(pipeline_events: true) }
scope :wiki_page_hooks, -> { where(wiki_page_events: true) }
belongs_to :project
validates :project, presence: true
end
class ServiceHook < WebHook
belongs_to :service
validates :service, presence: true
 
def execute(data)
WebHookService.new(self, data, 'service_hook').execute
Loading
Loading
class SystemHook < WebHook
scope :repository_update_hooks, -> { where(repository_update_events: true) }
TRIGGERS = {
repository_update_hooks: :repository_update_events,
push_hooks: :push_events,
tag_push_hooks: :tag_push_events
}.freeze
TRIGGERS.each do |trigger, event|
scope trigger, -> { where(event => true) }
end
 
default_value_for :push_events, false
default_value_for :repository_update_events, true
Loading
Loading
class WebHook < ActiveRecord::Base
include Sortable
 
default_value_for :push_events, true
default_value_for :issues_events, false
default_value_for :confidential_issues_events, false
default_value_for :note_events, false
default_value_for :merge_requests_events, false
default_value_for :tag_push_events, false
default_value_for :job_events, false
default_value_for :pipeline_events, false
default_value_for :repository_update_events, false
default_value_for :enable_ssl_verification, true
has_many :web_hook_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
 
scope :push_hooks, -> { where(push_events: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true) }
validates :url, presence: true, url: true
 
def execute(data, hook_name)
Loading
Loading
Loading
Loading
@@ -4,7 +4,7 @@ class SystemHooksService
end
 
def execute_hooks(data, hooks_scope = :all)
SystemHook.send(hooks_scope).each do |hook|
SystemHook.public_send(hooks_scope).find_each do |hook|
hook.async_execute(data, 'system_hooks')
end
end
Loading
Loading
class TestHookService
def execute(hook, current_user)
data = Gitlab::DataBuilder::Push.build_sample(hook.project, current_user)
hook.execute(data, 'push_hooks')
end
end
module TestHooks
class BaseService
attr_accessor :hook, :current_user, :trigger
def initialize(hook, current_user, trigger)
@hook = hook
@current_user = current_user
@trigger = trigger
end
def execute
trigger_data_method = "#{trigger}_data"
if !self.respond_to?(trigger_data_method, true) ||
!hook.class::TRIGGERS.value?(trigger.to_sym)
return error('Testing not available for this hook')
end
error_message = catch(:validation_error) do
sample_data = self.__send__(trigger_data_method)
return hook.execute(sample_data, trigger)
end
error(error_message)
end
private
def error(message, http_status = nil)
result = {
message: message,
status: :error
}
result[:http_status] = http_status if http_status
result
end
end
end
module TestHooks
class ProjectService < TestHooks::BaseService
private
def project
@project ||= hook.project
end
def push_events_data
throw(:validation_error, 'Ensure the project has at least one commit.') if project.empty_repo?
Gitlab::DataBuilder::Push.build_sample(project, current_user)
end
alias_method :tag_push_events_data, :push_events_data
def note_events_data
note = project.notes.first
throw(:validation_error, 'Ensure the project has notes.') unless note.present?
Gitlab::DataBuilder::Note.build(note, current_user)
end
def issues_events_data
issue = project.issues.first
throw(:validation_error, 'Ensure the project has issues.') unless issue.present?
issue.to_hook_data(current_user)
end
alias_method :confidential_issues_events_data, :issues_events_data
def merge_requests_events_data
merge_request = project.merge_requests.first
throw(:validation_error, 'Ensure the project has merge requests.') unless merge_request.present?
merge_request.to_hook_data(current_user)
end
def job_events_data
build = project.builds.first
throw(:validation_error, 'Ensure the project has CI jobs.') unless build.present?
Gitlab::DataBuilder::Build.build(build)
end
def pipeline_events_data
pipeline = project.pipelines.first
throw(:validation_error, 'Ensure the project has CI pipelines.') unless pipeline.present?
Gitlab::DataBuilder::Pipeline.build(pipeline)
end
def wiki_page_events_data
page = project.wiki.pages.first
if !project.wiki_enabled? || page.blank?
throw(:validation_error, 'Ensure the wiki is enabled and has pages.')
end
Gitlab::DataBuilder::WikiPage.build(page, current_user, 'create')
end
end
end
module TestHooks
class SystemService < TestHooks::BaseService
private
def project
@project ||= begin
project = Project.first
throw(:validation_error, 'Ensure that at least one project exists.') unless project
project
end
end
def push_events_data
if project.empty_repo?
throw(:validation_error, "Ensure project \"#{project.human_name}\" has commits.")
end
Gitlab::DataBuilder::Push.build_sample(project, current_user)
end
def tag_push_events_data
if project.repository.tags.empty?
throw(:validation_error, "Ensure project \"#{project.human_name}\" has tags.")
end
Gitlab::DataBuilder::Push.build_sample(project, current_user)
end
def repository_update_events_data
commit = project.commit
ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{project.default_branch}"
unless commit
throw(:validation_error, "Ensure project \"#{project.human_name}\" has commits.")
end
change = Gitlab::DataBuilder::Repository.single_change(
commit.parent_id || Gitlab::Git::BLANK_SHA,
commit.id,
ref
)
Gitlab::DataBuilder::Repository.update(project, current_user, [change], [ref])
end
end
end
Loading
Loading
@@ -39,7 +39,11 @@ class WebHookService
execution_duration: Time.now - start_time
)
 
[response.code, response.to_s]
{
status: :success,
http_status: response.code,
message: response.to_s
}
rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e
log_execution(
trigger: hook_name,
Loading
Loading
@@ -52,7 +56,10 @@ class WebHookService
 
Rails.logger.error("WebHook Error => #{e}")
 
[nil, e.to_s]
{
status: :error,
message: e.to_s
}
end
 
def async_execute
Loading
Loading
module WikiPages
class BaseService < ::BaseService
def hook_data(page, action)
hook_data = {
object_kind: page.class.name.underscore,
user: current_user.hook_attrs,
project: @project.hook_attrs,
wiki: @project.wiki.hook_attrs,
object_attributes: page.hook_attrs
}
page_url = Gitlab::UrlBuilder.build(page)
hook_data[:object_attributes].merge!(url: page_url, action: action)
hook_data
end
private
 
def execute_hooks(page, action = 'create')
page_data = hook_data(page, action)
page_data = Gitlab::DataBuilder::WikiPage.build(page, current_user, action)
@project.execute_hooks(page_data, :wiki_page_hooks)
@project.execute_services(page_data, :wiki_page_hooks)
end
Loading
Loading
Loading
Loading
@@ -12,7 +12,7 @@
= render partial: 'form', locals: { form: f, hook: @hook }
.form-actions
= f.submit 'Save changes', class: 'btn btn-create'
= link_to 'Test hook', test_admin_hook_path(@hook), class: 'btn btn-default'
= render 'shared/web_hooks/test_button', triggers: SystemHook::TRIGGERS, hook: @hook
= link_to 'Remove', admin_hook_path(@hook), method: :delete, class: 'btn btn-remove pull-right', data: { confirm: 'Are you sure?' }
 
%hr
Loading
Loading
Loading
Loading
@@ -22,12 +22,12 @@
- @hooks.each do |hook|
%li
.controls
= link_to 'Test hook', test_admin_hook_path(hook), class: 'btn btn-sm'
= render 'shared/web_hooks/test_button', triggers: SystemHook::TRIGGERS, hook: hook, button_class: 'btn-small'
= link_to 'Edit', edit_admin_hook_path(hook), class: 'btn btn-sm'
= link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: 'btn btn-remove btn-sm'
.monospace= hook.url
%div
- %w(repository_update_events push_events tag_push_events issues_events note_events merge_requests_events job_events).each do |trigger|
- if hook.send(trigger)
%span.label.label-gray= trigger.titleize
- SystemHook::TRIGGERS.each_value do |event|
- if hook.public_send(event)
%span.label.label-gray= event.to_s.titleize
%span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? 'enabled' : 'disabled'}
Loading
Loading
@@ -13,9 +13,10 @@
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
 
= f.submit 'Save changes', class: 'btn btn-create'
= link_to 'Test hook', test_project_hook_path(@project, @hook), class: 'btn btn-default'
= render 'shared/web_hooks/test_button', triggers: ProjectHook::TRIGGERS, hook: @hook
= link_to 'Remove', project_hook_path(@project, @hook), method: :delete, class: 'btn btn-remove pull-right', data: { confirm: 'Are you sure?' }
 
%hr
 
= render partial: 'projects/hook_logs/index', locals: { hook: @hook, hook_logs: @hook_logs, project: @project }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment