Skip to content
Snippets Groups Projects
Unverified Commit f2935998 authored by Max Woolf's avatar Max Woolf
Browse files

Fire webhook payload for ExternalApprovalRules

For each external approval rule in a project,
send a standard webhook payload to the endpoint.

This is fire-and-forget and does not provide a
mechanism for the 3rd party to feed-back directly
to the MR in question.
parent 69b760c0
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -22,6 +22,7 @@ def hook_data(merge_request, action, old_rev: nil, old_associations: {})
def execute_hooks(merge_request, action = 'open', old_rev: nil, old_associations: {})
merge_data = hook_data(merge_request, action, old_rev: old_rev, old_associations: old_associations)
merge_request.project.execute_hooks(merge_data, :merge_request_hooks)
merge_request.project.execute_external_compliance_hooks(merge_data)
merge_request.project.execute_services(merge_data, :merge_request_hooks)
 
enqueue_jira_connect_messages_for(merge_request)
Loading
Loading
Loading
Loading
@@ -104,6 +104,8 @@ def make_request_with_auth
end
 
def log_execution(trigger:, url:, request_data:, response:, execution_duration:, error_message: nil)
return unless hook.is_a?(WebHook)
WebHookLog.create(
web_hook: hook,
trigger: trigger,
Loading
Loading
Loading
Loading
@@ -36,6 +36,8 @@
- 1
- - analytics_usage_trends_counter_job
- 1
- - approval_rules_external_approval_rule_payload
- 1
- - approve_blocked_pending_approval_users
- 1
- - authorized_keys
Loading
Loading
Loading
Loading
@@ -23,6 +23,26 @@ This provides a consistent mechanism for reviewers to approve merge requests, an
maintainers know a change is ready to merge. Approvals in Free are optional, and do
not prevent a merge request from being merged when there is no approval.
 
## External Approvals **(ULTIMATE)**
> - [Introduced](link-to-issue) in GitLab Ultimate 13.10.
> - It's [deployed behind a feature flag](../../feature_flags.md), disabled by default.
> - It's disabled on GitLab.com.
> - It's not recommended for production use.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](../../../api/merge_request_approvals.html#enable-or-disable-external-project-level-mr-approvals). **(ULTIMATE SELF)**
WARNING:
This feature might not be available to you. Check the **version history** note above for details.
External approvals allow triggering a webhook payload to a custom HTTP endpoint which can respond asynchronously to approve a merge request.
External approvals enable a use case to allow externally hosted services to programatically approve merge requests.
NOTE:
The lack of an external approval will not block a merge request from being merged.
The administration of external approval rules is currently available through the [REST API](../../../api/merge_request_approvals.html#external-project-level-mr-approvals).
## Required Approvals **(PREMIUM)**
 
> - [Introduced](https://about.gitlab.com/releases/2015/06/22/gitlab-7-12-released/#merge-request-approvers-ee-only) in GitLab Enterprise Edition 7.12.
Loading
Loading
Loading
Loading
@@ -10,5 +10,25 @@ class ExternalApprovalRule < ApplicationRecord
 
validates :external_url, presence: true, uniqueness: { scope: :project_id }, addressable_url: true
validates :name, uniqueness: { scope: :project_id }, presence: true
def allow_local_requests?
false
end
def url
external_url
end
def token
nil
end
def enable_ssl_verification
true
end
def async_execute(data)
ApprovalRules::ExternalApprovalRulePayloadWorker.perform_async(self.id, data)
end
end
end
Loading
Loading
@@ -427,6 +427,12 @@ def has_group_hooks?(hooks_scope = :push_hooks)
group_hooks.hooks_for(hooks_scope).any?
end
 
def execute_external_compliance_hooks(data)
external_approval_rules.each do |approval_rule|
approval_rule.async_execute(data)
end
end
def execute_hooks(data, hooks_scope = :push_hooks)
super
 
Loading
Loading
Loading
Loading
@@ -691,6 +691,14 @@
:weight: 1
:idempotent: true
:tags: []
- :name: approval_rules_external_approval_rule_payload
:feature_category: :source_code_management
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent: true
:tags: []
- :name: ci_batch_reset_minutes
:feature_category: :continuous_integration
:has_external_dependencies:
Loading
Loading
# frozen_string_literal: true
class ApprovalRules::ExternalApprovalRulePayloadWorker
include ApplicationWorker
idempotent!
feature_category :source_code_management
def perform(rule_id, data)
rule = ApprovalRules::ExternalApprovalRule.find(rule_id)
WebHookService.new(rule, data, "GitLab-ExternalApprovalRule-#{rule.name}").execute
end
end
Loading
Loading
@@ -15,4 +15,22 @@
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id) }
it { is_expected.to validate_uniqueness_of(:external_url).scoped_to(:project_id) }
end
describe 'allow_local_requests?' do
it 'is always false' do
expect(subject.allow_local_requests?).to be false
end
end
describe 'token' do
it 'is always nil' do
expect(subject.token).to be_nil
end
end
describe 'enable_ssl_verification' do
it 'is always true' do
expect(subject.enable_ssl_verification).to be true
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ApprovalRules::ExternalApprovalRulePayloadWorker do
let_it_be(:rule) { create(:external_approval_rule, external_url: 'https://example.com/callback') }
subject(:worker) { described_class.new }
describe "#perform" do
it 'executes a WebHookService' do
stub_outbound_request
expect(subject.perform(rule.id, {})).to eq({ status: :success, http_status: 200, message: '' })
end
end
private
def stub_outbound_request
stub_request(:post, "https://example.com/callback")
.with(
body: "{}",
headers: {
'Content-Type': 'application/json',
'User-Agent': 'GitLab/13.10.0-pre',
'X-Gitlab-Event': 'GitLab External Approval Rule Rule 1'
})
.to_return(status: 200, body: "", headers: {})
end
end
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