Class: WebHook

Inherits:
ApplicationRecord show all
Includes:
Sortable
Defined in:
app/models/hooks/web_hook.rb

Direct Known Subclasses

ProjectHook, ServiceHook, SystemHook

Constant Summary collapse

MAX_FAILURES =
100
FAILURE_THRESHOLD =

three strikes

3
INITIAL_BACKOFF =
10.minutes
MAX_BACKOFF =
1.day
BACKOFF_GROWTH_FACTOR =
2.0

Constants inherited from ApplicationRecord

ApplicationRecord::MAX_PLUCK

Instance Method Summary collapse

Methods inherited from ApplicationRecord

cached_column_list, #create_or_load_association, declarative_enum, default_select_columns, id_in, id_not_in, iid_in, pluck_primary_key, primary_key_in, #readable_by?, safe_ensure_unique, safe_find_or_create_by, safe_find_or_create_by!, #to_ability_name, underscore, where_exists, where_not_exists, with_fast_read_statement_timeout, without_order

Methods included from SensitiveSerializableHash

#serializable_hash

Instance Method Details

#active_state(ignore_flag: false) ⇒ Object


109
110
111
112
113
114
# File 'app/models/hooks/web_hook.rb', line 109

def active_state(ignore_flag: false)
  return :permanently_disabled if permanently_disabled?(ignore_flag: ignore_flag)
  return :temporarily_disabled if temporarily_disabled?(ignore_flag: ignore_flag)

  :enabled
end

#allow_local_requests?Boolean

Allow urls pointing localhost and the local network

Returns:

  • (Boolean)

66
67
68
# File 'app/models/hooks/web_hook.rb', line 66

def allow_local_requests?
  Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services?
end

#application_contextObject

Custom attributes to be included in the worker context.


139
140
141
# File 'app/models/hooks/web_hook.rb', line 139

def application_context
  { related_class: type }
end

#async_execute(data, hook_name) ⇒ Object

rubocop: disable CodeReuse/ServiceClass


60
61
62
# File 'app/models/hooks/web_hook.rb', line 60

def async_execute(data, hook_name)
  WebHookService.new(self, data, hook_name).async_execute if executable?
end

#backoff!Object


95
96
97
98
99
100
# File 'app/models/hooks/web_hook.rb', line 95

def backoff!
  return if permanently_disabled? || (backoff_count >= MAX_FAILURES && temporarily_disabled?)

  assign_attributes(disabled_until: next_backoff.from_now, backoff_count: backoff_count.succ.clamp(0, MAX_FAILURES))
  save(validate: false)
end

#disable!Object


82
83
84
85
86
# File 'app/models/hooks/web_hook.rb', line 82

def disable!
  return if permanently_disabled?

  update_attribute(:recent_failures, FAILURE_THRESHOLD + 1)
end

#enable!Object


88
89
90
91
92
93
# File 'app/models/hooks/web_hook.rb', line 88

def enable!
  return if recent_failures == 0 && disabled_until.nil? && backoff_count == 0

  assign_attributes(recent_failures: 0, disabled_until: nil, backoff_count: 0)
  save(validate: false)
end

#executable?Boolean

Returns:

  • (Boolean)

36
37
38
# File 'app/models/hooks/web_hook.rb', line 36

def executable?
  !temporarily_disabled? && !permanently_disabled?
end

#execute(data, hook_name, force: false) ⇒ Object

rubocop: disable CodeReuse/ServiceClass


53
54
55
56
# File 'app/models/hooks/web_hook.rb', line 53

def execute(data, hook_name, force: false)
  # hook.executable? is checked in WebHookService#execute
  WebHookService.new(self, data, hook_name, force: force).execute
end

#failed!Object


102
103
104
105
106
107
# File 'app/models/hooks/web_hook.rb', line 102

def failed!
  return unless recent_failures < MAX_FAILURES

  assign_attributes(recent_failures: recent_failures + 1)
  save(validate: false)
end

#help_pathObject


70
71
72
# File 'app/models/hooks/web_hook.rb', line 70

def help_path
  'user/project/integrations/webhooks'
end

#next_backoffObject


74
75
76
77
78
79
80
# File 'app/models/hooks/web_hook.rb', line 74

def next_backoff
  return MAX_BACKOFF if backoff_count >= 8 # optimization to prevent expensive exponentiation and possible overflows

  (INITIAL_BACKOFF * (BACKOFF_GROWTH_FACTOR**backoff_count))
    .clamp(INITIAL_BACKOFF, MAX_BACKOFF)
    .seconds
end

#parentObject

Returns the associated Project or Group for the WebHook if one exists. Overridden by inheriting classes.


135
136
# File 'app/models/hooks/web_hook.rb', line 135

def parent
end

#permanently_disabled?(ignore_flag: false) ⇒ Boolean

Returns:

  • (Boolean)

46
47
48
49
50
# File 'app/models/hooks/web_hook.rb', line 46

def permanently_disabled?(ignore_flag: false)
  return false unless ignore_flag || web_hooks_disable_failed?

  recent_failures > FAILURE_THRESHOLD
end

#rate_limitObject

Threshold for the rate-limit. Overridden in ProjectHook and GroupHook, other WebHooks are not rate-limited.


129
130
131
# File 'app/models/hooks/web_hook.rb', line 129

def rate_limit
  nil
end

#rate_limited?Boolean

Returns Whether or not the WebHook is currently throttled.

Returns:

  • (Boolean)

    Whether or not the WebHook is currently throttled.


117
118
119
120
121
122
123
124
125
# File 'app/models/hooks/web_hook.rb', line 117

def rate_limited?
  return false unless rate_limit

  Gitlab::ApplicationRateLimiter.peek(
    :web_hook_calls,
    scope: [self],
    threshold: rate_limit
  )
end

#temporarily_disabled?(ignore_flag: false) ⇒ Boolean

Returns:

  • (Boolean)

40
41
42
43
44
# File 'app/models/hooks/web_hook.rb', line 40

def temporarily_disabled?(ignore_flag: false)
  return false unless ignore_flag || web_hooks_disable_failed?

  disabled_until.present? && disabled_until >= Time.current
end