Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-10-20 11:43:02 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-10-20 11:43:02 +0300
commitd9ab72d6080f594d0b3cae15f14b3ef2c6c638cb (patch)
tree2341ef426af70ad1e289c38036737e04b0aa5007 /app/presenters
parentd6e514dd13db8947884cd58fe2a9c2a063400a9b (diff)
Add latest changes from gitlab-org/gitlab@14-4-stable-eev14.4.0-rc42
Diffstat (limited to 'app/presenters')
-rw-r--r--app/presenters/README.md95
-rw-r--r--app/presenters/alert_management/alert_presenter.rb6
-rw-r--r--app/presenters/award_emoji_presenter.rb2
-rw-r--r--app/presenters/blob_presenter.rb2
-rw-r--r--app/presenters/blobs/unfold_presenter.rb2
-rw-r--r--app/presenters/board_presenter.rb2
-rw-r--r--app/presenters/ci/bridge_presenter.rb3
-rw-r--r--app/presenters/ci/build_metadata_presenter.rb3
-rw-r--r--app/presenters/ci/build_presenter.rb2
-rw-r--r--app/presenters/ci/group_variable_presenter.rb2
-rw-r--r--app/presenters/ci/legacy_stage_presenter.rb2
-rw-r--r--app/presenters/ci/pipeline_presenter.rb7
-rw-r--r--app/presenters/ci/processable_presenter.rb1
-rw-r--r--app/presenters/ci/runner_presenter.rb5
-rw-r--r--app/presenters/ci/stage_presenter.rb2
-rw-r--r--app/presenters/ci/trigger_presenter.rb3
-rw-r--r--app/presenters/ci/variable_presenter.rb2
-rw-r--r--app/presenters/clusterable_presenter.rb2
-rw-r--r--app/presenters/clusters/cluster_presenter.rb41
-rw-r--r--app/presenters/clusters/integration_presenter.rb2
-rw-r--r--app/presenters/commit_presenter.rb2
-rw-r--r--app/presenters/commit_status_presenter.rb23
-rw-r--r--app/presenters/dev_ops_report/metric_presenter.rb2
-rw-r--r--app/presenters/environment_presenter.rb2
-rw-r--r--app/presenters/event_presenter.rb3
-rw-r--r--app/presenters/gitlab/blame_presenter.rb2
-rw-r--r--app/presenters/group_clusterable_presenter.rb5
-rw-r--r--app/presenters/group_member_presenter.rb2
-rw-r--r--app/presenters/instance_clusterable_presenter.rb5
-rw-r--r--app/presenters/invitation_presenter.rb2
-rw-r--r--app/presenters/issue_presenter.rb3
-rw-r--r--app/presenters/label_presenter.rb4
-rw-r--r--app/presenters/member_presenter.rb2
-rw-r--r--app/presenters/members_presenter.rb2
-rw-r--r--app/presenters/merge_request_presenter.rb6
-rw-r--r--app/presenters/milestone_presenter.rb2
-rw-r--r--app/presenters/pages_domain_presenter.rb4
-rw-r--r--app/presenters/project_clusterable_presenter.rb5
-rw-r--r--app/presenters/project_hook_presenter.rb2
-rw-r--r--app/presenters/project_member_presenter.rb2
-rw-r--r--app/presenters/project_presenter.rb5
-rw-r--r--app/presenters/projects/import_export/project_export_presenter.rb10
-rw-r--r--app/presenters/projects/settings/deploy_keys_presenter.rb2
-rw-r--r--app/presenters/prometheus_alert_presenter.rb2
-rw-r--r--app/presenters/release_presenter.rb6
-rw-r--r--app/presenters/releases/evidence_presenter.rb2
-rw-r--r--app/presenters/search_service_presenter.rb3
-rw-r--r--app/presenters/sentry_error_presenter.rb2
-rw-r--r--app/presenters/service_hook_presenter.rb2
-rw-r--r--app/presenters/snippet_blob_presenter.rb2
-rw-r--r--app/presenters/snippet_presenter.rb5
-rw-r--r--app/presenters/terraform/modules_presenter.rb2
-rw-r--r--app/presenters/todo_presenter.rb2
-rw-r--r--app/presenters/tree_entry_presenter.rb2
-rw-r--r--app/presenters/user_presenter.rb2
-rw-r--r--app/presenters/web_hook_log_presenter.rb2
56 files changed, 221 insertions, 96 deletions
diff --git a/app/presenters/README.md b/app/presenters/README.md
index 62aec4fc8a2..dfd1818f97d 100644
--- a/app/presenters/README.md
+++ b/app/presenters/README.md
@@ -66,14 +66,15 @@ we gain the following benefits:
### Presenter definition
-Every presenter should inherit from `Gitlab::View::Presenter::Simple`, which
-provides a `.presents` the method which allows you to define an accessor for the
+If you need a presenter class that has only necessary interfaces for the view-related context,
+inherit from `Gitlab::View::Presenter::Simple`.
+It provides a `.presents` the method which allows you to define an accessor for the
presented object. It also includes common helpers like `Gitlab::Routing` and
`Gitlab::Allowable`.
```ruby
class LabelPresenter < Gitlab::View::Presenter::Simple
- presents :label
+ presents ::Label, as: :label
def text_color
label.color.to_s
@@ -85,13 +86,14 @@ class LabelPresenter < Gitlab::View::Presenter::Simple
end
```
-In some cases, it can be more practical to transparently delegate all missing
-method calls to the presented object, in these cases, you can make your
-presenter inherit from `Gitlab::View::Presenter::Delegated`:
+If you need a presenter class that delegates missing method calls to the presented object,
+inherit from `Gitlab::View::Presenter::Delegated`.
+This is more like an "extension" in the sense that the produced object is going to have
+all of interfaces of the presented object **AND** all of the interfaces in the presenter class:
```ruby
class LabelPresenter < Gitlab::View::Presenter::Delegated
- presents :label
+ presents ::Label, as: :label
def text_color
# color is delegated to label
@@ -152,3 +154,82 @@ You can also present the model in the view:
%div{ class: label.text_color }
= render partial: label, label: label
```
+
+### Validate accidental overrides
+
+We use presenters in many places, such as Controller, Haml, GraphQL/Rest API,
+it's very handy to extend the core/backend logic of Active Record models,
+however, there is a risk that it accidentally overrides important logic.
+
+For example, [this production incident](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/5498)
+was caused by [including `ActionView::Helpers::UrlHelper` in a presenter](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69537/diffs#4b581cff00ef3cc9780efd23682af383de302e7d_3_3).
+The `tag` accesor in `Ci::Build` was accidentally overridden by `ActionView::Helpers::TagHelper#tag`,
+and as a conseuqence, a wrong `tag` value was persited into database.
+
+Starting from GitLab 14.4, we validate the presenters (specifically all of the subclasses of `Gitlab::View::Presenter::Delegated`)
+that they do not accidentally override core/backend logic. In such case, a pipeline in merge requests fails with an error message,
+here is an example:
+
+```plaintext
+We've detected that a presetner is overriding a specific method(s) on a subject model.
+There is a risk that it accidentally modifies the backend/core logic that leads to production incident.
+Please follow https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/presenters/README.md#validate-accidental-overrides
+to resolve this error with caution.
+
+Here are the conflict details.
+
+- Ci::PipelinePresenter#tag is overriding Ci::Pipeline#tag. delegator_location: /devkitkat/services/rails/cache/ruby/2.7.0/gems/actionview-6.1.3.2/lib/action_view/helpers/tag_helper.rb:271 original_location: /devkitkat/services/rails/cache/ruby/2.7.0/gems/activemodel-6.1.3.2/lib/active_model/attribute_methods.rb:254
+```
+
+Here are the potential solutions:
+
+- If the conflict happens on an instance method in the presenter:
+ - If you intend to override the core/backend logic, define `delegator_override <method-name>` on top of the conflicted method.
+ This explicitly adds the method to an allowlist.
+ - If you do NOT intend to override the core/backend logic, rename the method name in the presenter.
+- If the conflict happens on an included module in the presenter, remove the module from the presenter and find a workaround.
+
+### How to use the `Gitlab::Utils::DelegatorOverride` validator
+
+If a presenter class inhertis from `Gitlab::View::Presenter::Delegated`,
+you should define what object class is presented:
+
+```ruby
+class WebHookLogPresenter < Gitlab::View::Presenter::Delegated
+ presents ::WebHookLog, as: :web_hook_log # This defines that the presenter presents `WebHookLog` Active Record model.
+```
+
+These presenters are validated not to accidentaly override the methods in the presented object.
+You can run the validation locally with:
+
+```shell
+bundle exec rake lint:static_verification
+```
+
+To add a method to an allowlist, use `delegator_override`. For example:
+
+```ruby
+class VulnerabilityPresenter < Gitlab::View::Presenter::Delegated
+ presents ::Vulnerability, as: :vulnerability
+
+ delegator_override :description # This adds the `description` method to an allowlist that the override is intentional.
+ def description
+ vulnerability.description || finding.description
+ end
+```
+
+To add methods of a module to an allowlist, use `delegator_override_with`. For example:
+
+```ruby
+module Ci
+ class PipelinePresenter < Gitlab::View::Presenter::Delegated
+ include Gitlab::Utils::StrongMemoize
+ include ActionView::Helpers::UrlHelper
+
+ delegator_override_with Gitlab::Utils::StrongMemoize # TODO: Remove `Gitlab::Utils::StrongMemoize` inclusion as it's duplicate
+ delegator_override_with ActionView::Helpers::TagHelper # TODO: Remove `ActionView::Helpers::UrlHelper` inclusion as it overrides `Ci::Pipeline#tag`
+```
+
+Keep in mind that if you use `delegator_override_with`,
+there is a high chance that you're doing **something wrong**.
+Read the [Validate Accidental Overrides](#validate-accidental-overrides) for more information.
diff --git a/app/presenters/alert_management/alert_presenter.rb b/app/presenters/alert_management/alert_presenter.rb
index c6c6fe837a0..86fe9859271 100644
--- a/app/presenters/alert_management/alert_presenter.rb
+++ b/app/presenters/alert_management/alert_presenter.rb
@@ -2,10 +2,12 @@
module AlertManagement
class AlertPresenter < Gitlab::View::Presenter::Delegated
- include Gitlab::Utils::StrongMemoize
include IncidentManagement::Settings
include ActionView::Helpers::UrlHelper
+ presents ::AlertManagement::Alert
+ delegator_override_with Gitlab::Utils::StrongMemoize # TODO: Remove `Gitlab::Utils::StrongMemoize` inclusion as it's duplicate
+
MARKDOWN_LINE_BREAK = " \n"
HORIZONTAL_LINE = "\n\n---\n\n"
@@ -30,6 +32,7 @@ module AlertManagement
started_at&.strftime('%d %B %Y, %-l:%M%p (%Z)')
end
+ delegator_override :details_url
def details_url
details_project_alert_management_url(project, alert.iid)
end
@@ -65,6 +68,7 @@ module AlertManagement
private
attr_reader :alert, :project
+
delegate :alert_markdown, :full_query, to: :parsed_payload
def issue_summary_markdown
diff --git a/app/presenters/award_emoji_presenter.rb b/app/presenters/award_emoji_presenter.rb
index 98713855d35..8a7b58e0aba 100644
--- a/app/presenters/award_emoji_presenter.rb
+++ b/app/presenters/award_emoji_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class AwardEmojiPresenter < Gitlab::View::Presenter::Delegated
- presents :award_emoji
+ presents ::AwardEmoji, as: :award_emoji
def description
as_emoji['description']
diff --git a/app/presenters/blob_presenter.rb b/app/presenters/blob_presenter.rb
index ecc16e2840c..c198859aa4c 100644
--- a/app/presenters/blob_presenter.rb
+++ b/app/presenters/blob_presenter.rb
@@ -7,7 +7,7 @@ class BlobPresenter < Gitlab::View::Presenter::Delegated
include TreeHelper
include ChecksCollaboration
- presents :blob
+ presents ::Blob, as: :blob
def highlight(to: nil, plain: nil)
load_all_blob_data
diff --git a/app/presenters/blobs/unfold_presenter.rb b/app/presenters/blobs/unfold_presenter.rb
index 487c6fe0757..b921b5bf670 100644
--- a/app/presenters/blobs/unfold_presenter.rb
+++ b/app/presenters/blobs/unfold_presenter.rb
@@ -6,6 +6,8 @@ module Blobs
include ActiveModel::AttributeAssignment
include Gitlab::Utils::StrongMemoize
+ presents ::Blob
+
attribute :full, :boolean, default: false
attribute :since, :integer, default: 1
attribute :to, :integer, default: 1
diff --git a/app/presenters/board_presenter.rb b/app/presenters/board_presenter.rb
index d7cecd44dd7..bb3e96b8faf 100644
--- a/app/presenters/board_presenter.rb
+++ b/app/presenters/board_presenter.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
class BoardPresenter < Gitlab::View::Presenter::Delegated
- presents :board
+ presents ::Board, as: :board
end
diff --git a/app/presenters/ci/bridge_presenter.rb b/app/presenters/ci/bridge_presenter.rb
index 724e10c26c3..a62d7cdbbd4 100644
--- a/app/presenters/ci/bridge_presenter.rb
+++ b/app/presenters/ci/bridge_presenter.rb
@@ -2,6 +2,9 @@
module Ci
class BridgePresenter < ProcessablePresenter
+ presents ::Ci::Bridge
+
+ delegator_override :detailed_status
def detailed_status
@detailed_status ||= subject.detailed_status(user)
end
diff --git a/app/presenters/ci/build_metadata_presenter.rb b/app/presenters/ci/build_metadata_presenter.rb
index 4871bb3a919..2f559adf77d 100644
--- a/app/presenters/ci/build_metadata_presenter.rb
+++ b/app/presenters/ci/build_metadata_presenter.rb
@@ -9,8 +9,9 @@ module Ci
job_timeout_source: 'job'
}.freeze
- presents :metadata
+ presents ::Ci::BuildMetadata, as: :metadata
+ delegator_override :timeout_source
def timeout_source
return unless metadata.timeout_source?
diff --git a/app/presenters/ci/build_presenter.rb b/app/presenters/ci/build_presenter.rb
index 06ed6791bb7..65e1c80085f 100644
--- a/app/presenters/ci/build_presenter.rb
+++ b/app/presenters/ci/build_presenter.rb
@@ -2,6 +2,8 @@
module Ci
class BuildPresenter < ProcessablePresenter
+ presents ::Ci::Build
+
def erased_by_user?
# Build can be erased through API, therefore it does not have
# `erased_by` user assigned in that case.
diff --git a/app/presenters/ci/group_variable_presenter.rb b/app/presenters/ci/group_variable_presenter.rb
index 99011150c84..dea9a42b622 100644
--- a/app/presenters/ci/group_variable_presenter.rb
+++ b/app/presenters/ci/group_variable_presenter.rb
@@ -2,7 +2,7 @@
module Ci
class GroupVariablePresenter < Gitlab::View::Presenter::Delegated
- presents :variable
+ presents ::Ci::GroupVariable, as: :variable
def placeholder
'GROUP_VARIABLE'
diff --git a/app/presenters/ci/legacy_stage_presenter.rb b/app/presenters/ci/legacy_stage_presenter.rb
index d5c21baba28..c803abfab6a 100644
--- a/app/presenters/ci/legacy_stage_presenter.rb
+++ b/app/presenters/ci/legacy_stage_presenter.rb
@@ -2,7 +2,7 @@
module Ci
class LegacyStagePresenter < Gitlab::View::Presenter::Delegated
- presents :legacy_stage
+ presents ::Ci::LegacyStage, as: :legacy_stage
def latest_ordered_statuses
preload_statuses(legacy_stage.statuses.latest_ordered)
diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb
index 82f00f74692..e0cb899c9d3 100644
--- a/app/presenters/ci/pipeline_presenter.rb
+++ b/app/presenters/ci/pipeline_presenter.rb
@@ -5,6 +5,9 @@ module Ci
include Gitlab::Utils::StrongMemoize
include ActionView::Helpers::UrlHelper
+ delegator_override_with Gitlab::Utils::StrongMemoize # TODO: Remove `Gitlab::Utils::StrongMemoize` inclusion as it's duplicate
+ delegator_override_with ActionView::Helpers::TagHelper # TODO: Remove `ActionView::Helpers::UrlHelper` inclusion as it overrides `Ci::Pipeline#tag`
+
# We use a class method here instead of a constant, allowing EE to redefine
# the returned `Hash` more easily.
def self.failure_reasons
@@ -20,8 +23,9 @@ module Ci
user_blocked: 'The user who created this pipeline is blocked.' }
end
- presents :pipeline
+ presents ::Ci::Pipeline, as: :pipeline
+ delegator_override :failed_builds
def failed_builds
return [] unless can?(current_user, :read_build, pipeline)
@@ -30,6 +34,7 @@ module Ci
end
end
+ delegator_override :failure_reason
def failure_reason
return unless pipeline.failure_reason?
diff --git a/app/presenters/ci/processable_presenter.rb b/app/presenters/ci/processable_presenter.rb
index 5a8a6649071..9f3a83a00f3 100644
--- a/app/presenters/ci/processable_presenter.rb
+++ b/app/presenters/ci/processable_presenter.rb
@@ -2,5 +2,6 @@
module Ci
class ProcessablePresenter < CommitStatusPresenter
+ presents ::Ci::Processable
end
end
diff --git a/app/presenters/ci/runner_presenter.rb b/app/presenters/ci/runner_presenter.rb
index 273328afc53..ad889d444f8 100644
--- a/app/presenters/ci/runner_presenter.rb
+++ b/app/presenters/ci/runner_presenter.rb
@@ -2,11 +2,14 @@
module Ci
class RunnerPresenter < Gitlab::View::Presenter::Delegated
- presents :runner
+ presents ::Ci::Runner, as: :runner
+ delegator_override :locked?
def locked?
read_attribute(:locked) && project_type?
end
+
+ delegator_override :locked
alias_method :locked, :locked?
end
end
diff --git a/app/presenters/ci/stage_presenter.rb b/app/presenters/ci/stage_presenter.rb
index 21bda86cded..bd5bf08abbc 100644
--- a/app/presenters/ci/stage_presenter.rb
+++ b/app/presenters/ci/stage_presenter.rb
@@ -2,7 +2,7 @@
module Ci
class StagePresenter < Gitlab::View::Presenter::Delegated
- presents :stage
+ presents ::Ci::Stage, as: :stage
PRELOADED_RELATIONS = [:pipeline, :metadata, :tags, :job_artifacts_archive, :downstream_pipeline].freeze
diff --git a/app/presenters/ci/trigger_presenter.rb b/app/presenters/ci/trigger_presenter.rb
index 605c8f328a4..5f0bd4b3a8b 100644
--- a/app/presenters/ci/trigger_presenter.rb
+++ b/app/presenters/ci/trigger_presenter.rb
@@ -2,12 +2,13 @@
module Ci
class TriggerPresenter < Gitlab::View::Presenter::Delegated
- presents :trigger
+ presents ::Ci::Trigger, as: :trigger
def has_token_exposed?
can?(current_user, :admin_trigger, trigger)
end
+ delegator_override :token
def token
if has_token_exposed?
trigger.token
diff --git a/app/presenters/ci/variable_presenter.rb b/app/presenters/ci/variable_presenter.rb
index f027f3aa560..ec04dd5e9ff 100644
--- a/app/presenters/ci/variable_presenter.rb
+++ b/app/presenters/ci/variable_presenter.rb
@@ -2,7 +2,7 @@
module Ci
class VariablePresenter < Gitlab::View::Presenter::Delegated
- presents :variable
+ presents ::Ci::Variable, as: :variable
def placeholder
'PROJECT_VARIABLE'
diff --git a/app/presenters/clusterable_presenter.rb b/app/presenters/clusterable_presenter.rb
index 03e26b92922..4b645510b51 100644
--- a/app/presenters/clusterable_presenter.rb
+++ b/app/presenters/clusterable_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class ClusterablePresenter < Gitlab::View::Presenter::Delegated
- presents :clusterable
+ presents ::Project, ::Group, ::Clusters::Instance, as: :clusterable
def self.fabricate(clusterable, **attributes)
presenter_class = "#{clusterable.class.name}ClusterablePresenter".constantize
diff --git a/app/presenters/clusters/cluster_presenter.rb b/app/presenters/clusters/cluster_presenter.rb
index eb4bd8532af..ce060476cfd 100644
--- a/app/presenters/clusters/cluster_presenter.rb
+++ b/app/presenters/clusters/cluster_presenter.rb
@@ -3,21 +3,10 @@
module Clusters
class ClusterPresenter < Gitlab::View::Presenter::Delegated
include ::Gitlab::Utils::StrongMemoize
- include ActionView::Helpers::SanitizeHelper
- include ActionView::Helpers::UrlHelper
- include IconsHelper
- presents :cluster
+ delegator_override_with ::Gitlab::Utils::StrongMemoize # TODO: Remove `::Gitlab::Utils::StrongMemoize` inclusion as it's duplicate
- # We do not want to show the group path for clusters belonging to the
- # clusterable, only for the ancestor clusters.
- def item_link(clusterable_presenter, *html_options)
- if cluster.group_type? && clusterable != clusterable_presenter.subject
- contracted_group_name(cluster.group) + ' / ' + link_to_cluster
- else
- link_to_cluster(*html_options)
- end
- end
+ presents ::Clusters::Cluster, as: :cluster
def provider_label
if aws?
@@ -39,16 +28,6 @@ module Clusters
can?(current_user, :read_cluster, cluster)
end
- def cluster_type_description
- if cluster.project_type?
- s_("ClusterIntegration|Project cluster")
- elsif cluster.group_type?
- s_("ClusterIntegration|Group cluster")
- elsif cluster.instance_type?
- s_("ClusterIntegration|Instance cluster")
- end
- end
-
def show_path(params: {})
if cluster.project_type?
project_cluster_path(project, cluster, params)
@@ -107,7 +86,7 @@ module Clusters
private
def image_path(path)
- ActionController::Base.helpers.image_path(path)
+ ApplicationController.helpers.image_path(path)
end
# currently log explorer is only available in the scope of the project
@@ -127,20 +106,6 @@ module Clusters
cluster.project
end
end
-
- def contracted_group_name(group)
- sanitize(group.full_name)
- .sub(%r{\/.*\/}, "/ #{contracted_icon} /")
- .html_safe
- end
-
- def contracted_icon
- sprite_icon('ellipsis_h', size: 12, css_class: 'vertical-align-middle')
- end
-
- def link_to_cluster(html_options: {})
- link_to_if(can_read_cluster?, cluster.name, show_path, html_options)
- end
end
end
diff --git a/app/presenters/clusters/integration_presenter.rb b/app/presenters/clusters/integration_presenter.rb
index 57608be29b1..f7be59f00f3 100644
--- a/app/presenters/clusters/integration_presenter.rb
+++ b/app/presenters/clusters/integration_presenter.rb
@@ -2,7 +2,7 @@
module Clusters
class IntegrationPresenter < Gitlab::View::Presenter::Delegated
- presents :integration
+ presents ::Clusters::Integrations::Prometheus, ::Clusters::Integrations::ElasticStack, as: :integration
def application_type
integration.class.name.demodulize.underscore
diff --git a/app/presenters/commit_presenter.rb b/app/presenters/commit_presenter.rb
index c14dcab6000..7df45ca03bb 100644
--- a/app/presenters/commit_presenter.rb
+++ b/app/presenters/commit_presenter.rb
@@ -3,7 +3,7 @@
class CommitPresenter < Gitlab::View::Presenter::Delegated
include GlobalID::Identification
- presents :commit
+ presents ::Commit, as: :commit
def status_for(ref)
return unless can?(current_user, :read_commit_status, commit.project)
diff --git a/app/presenters/commit_status_presenter.rb b/app/presenters/commit_status_presenter.rb
index 3c39470b730..7919e501bf0 100644
--- a/app/presenters/commit_status_presenter.rb
+++ b/app/presenters/commit_status_presenter.rb
@@ -28,19 +28,36 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
ci_quota_exceeded: 'No more CI minutes available',
no_matching_runner: 'No matching runner available',
trace_size_exceeded: 'The job log size limit was reached',
- builds_disabled: 'The CI/CD is disabled for this project'
+ builds_disabled: 'The CI/CD is disabled for this project',
+ environment_creation_failure: 'This job could not be executed because it would create an environment with an invalid parameter.'
+ }.freeze
+
+ TROUBLESHOOTING_DOC = {
+ environment_creation_failure: { path: 'ci/environments/index', anchor: 'a-deployment-job-failed-with-this-job-could-not-be-executed-because-it-would-create-an-environment-with-an-invalid-parameter-error' }
}.freeze
private_constant :CALLOUT_FAILURE_MESSAGES
- presents :build
+ presents ::CommitStatus, as: :build
def self.callout_failure_messages
CALLOUT_FAILURE_MESSAGES
end
def callout_failure_message
- self.class.callout_failure_messages.fetch(failure_reason.to_sym)
+ message = self.class.callout_failure_messages.fetch(failure_reason.to_sym)
+
+ if doc = TROUBLESHOOTING_DOC[failure_reason.to_sym]
+ message += " #{help_page_link(doc[:path], doc[:anchor])}"
+ end
+
+ message
+ end
+
+ private
+
+ def help_page_link(path, anchor)
+ ActionController::Base.helpers.link_to('How do I fix it?', help_page_path(path, anchor: anchor))
end
end
diff --git a/app/presenters/dev_ops_report/metric_presenter.rb b/app/presenters/dev_ops_report/metric_presenter.rb
index 4d7ac1cd3ec..55326f8f678 100644
--- a/app/presenters/dev_ops_report/metric_presenter.rb
+++ b/app/presenters/dev_ops_report/metric_presenter.rb
@@ -2,6 +2,8 @@
module DevOpsReport
class MetricPresenter < Gitlab::View::Presenter::Simple
+ presents ::DevOpsReport::Metric
+
delegate :created_at, to: :subject
def cards
diff --git a/app/presenters/environment_presenter.rb b/app/presenters/environment_presenter.rb
index 6f67bbe2a5a..6c8da86187c 100644
--- a/app/presenters/environment_presenter.rb
+++ b/app/presenters/environment_presenter.rb
@@ -3,7 +3,7 @@
class EnvironmentPresenter < Gitlab::View::Presenter::Delegated
include ActionView::Helpers::UrlHelper
- presents :environment
+ presents ::Environment, as: :environment
def path
project_environment_path(project, self)
diff --git a/app/presenters/event_presenter.rb b/app/presenters/event_presenter.rb
index c37721f7213..4c787b88e20 100644
--- a/app/presenters/event_presenter.rb
+++ b/app/presenters/event_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class EventPresenter < Gitlab::View::Presenter::Delegated
- presents :event
+ presents ::Event, as: :event
def initialize(subject, **attributes)
super
@@ -10,6 +10,7 @@ class EventPresenter < Gitlab::View::Presenter::Delegated
end
# Caching `visible_to_user?` method in the presenter beause it might be called multiple times.
+ delegator_override :visible_to_user?
def visible_to_user?(user = nil)
@visible_to_user_cache.fetch(user&.id) { super(user) }
end
diff --git a/app/presenters/gitlab/blame_presenter.rb b/app/presenters/gitlab/blame_presenter.rb
index 1f2445b04a1..e9340a42e51 100644
--- a/app/presenters/gitlab/blame_presenter.rb
+++ b/app/presenters/gitlab/blame_presenter.rb
@@ -12,7 +12,7 @@ module Gitlab
include TreeHelper
include IconsHelper
- presents :blame
+ presents nil, as: :blame
CommitData = Struct.new(
:author_avatar,
diff --git a/app/presenters/group_clusterable_presenter.rb b/app/presenters/group_clusterable_presenter.rb
index 34e7084ab02..c51cd415029 100644
--- a/app/presenters/group_clusterable_presenter.rb
+++ b/app/presenters/group_clusterable_presenter.rb
@@ -2,7 +2,8 @@
class GroupClusterablePresenter < ClusterablePresenter
extend ::Gitlab::Utils::Override
- include ActionView::Helpers::UrlHelper
+
+ presents ::Group
override :cluster_status_cluster_path
def cluster_status_cluster_path(cluster, params = {})
@@ -31,7 +32,7 @@ class GroupClusterablePresenter < ClusterablePresenter
override :learn_more_link
def learn_more_link
- link_to(s_('ClusterIntegration|Learn more about group Kubernetes clusters'), help_page_path('user/group/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
+ ApplicationController.helpers.link_to(s_('ClusterIntegration|Learn more about group Kubernetes clusters'), help_page_path('user/group/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
end
def metrics_dashboard_path(cluster)
diff --git a/app/presenters/group_member_presenter.rb b/app/presenters/group_member_presenter.rb
index 5ab4b51f472..88facc3608d 100644
--- a/app/presenters/group_member_presenter.rb
+++ b/app/presenters/group_member_presenter.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class GroupMemberPresenter < MemberPresenter
+ presents ::GroupMember
+
private
def admin_member_permission
diff --git a/app/presenters/instance_clusterable_presenter.rb b/app/presenters/instance_clusterable_presenter.rb
index 56d91f90b2e..f2550eb17e3 100644
--- a/app/presenters/instance_clusterable_presenter.rb
+++ b/app/presenters/instance_clusterable_presenter.rb
@@ -2,7 +2,8 @@
class InstanceClusterablePresenter < ClusterablePresenter
extend ::Gitlab::Utils::Override
- include ActionView::Helpers::UrlHelper
+
+ presents ::Clusters::Instance
def self.fabricate(clusterable, **attributes)
attributes_with_presenter_class = attributes.merge(presenter_class: InstanceClusterablePresenter)
@@ -69,7 +70,7 @@ class InstanceClusterablePresenter < ClusterablePresenter
override :learn_more_link
def learn_more_link
- link_to(s_('ClusterIntegration|Learn more about instance Kubernetes clusters'), help_page_path('user/instance/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
+ ApplicationController.helpers.link_to(s_('ClusterIntegration|Learn more about instance Kubernetes clusters'), help_page_path('user/instance/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
end
def metrics_dashboard_path(cluster)
diff --git a/app/presenters/invitation_presenter.rb b/app/presenters/invitation_presenter.rb
index d8c07f327dd..ada8227a477 100644
--- a/app/presenters/invitation_presenter.rb
+++ b/app/presenters/invitation_presenter.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
class InvitationPresenter < Gitlab::View::Presenter::Delegated
- presents :invitation
+ presents nil, as: :invitation
end
diff --git a/app/presenters/issue_presenter.rb b/app/presenters/issue_presenter.rb
index b7f4ac0555d..72fe14d224d 100644
--- a/app/presenters/issue_presenter.rb
+++ b/app/presenters/issue_presenter.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
class IssuePresenter < Gitlab::View::Presenter::Delegated
- presents :issue
+ presents ::Issue, as: :issue
def issue_path
url_builder.build(issue, only_path: true)
end
+ delegator_override :subscribed?
def subscribed?
issue.subscribed?(current_user, issue.project)
end
diff --git a/app/presenters/label_presenter.rb b/app/presenters/label_presenter.rb
index 9e51e6fa4ba..fafade2828f 100644
--- a/app/presenters/label_presenter.rb
+++ b/app/presenters/label_presenter.rb
@@ -1,9 +1,11 @@
# frozen_string_literal: true
class LabelPresenter < Gitlab::View::Presenter::Delegated
- presents :label
+ presents ::Label, as: :label
delegate :name, :full_name, to: :label_subject, prefix: :subject
+ delegator_override :subject # TODO: Fix `Gitlab::View::Presenter::Delegated#subject` not to override `Label#subject`.
+
def edit_path
case label
when GroupLabel then edit_group_label_path(label.group, label)
diff --git a/app/presenters/member_presenter.rb b/app/presenters/member_presenter.rb
index b37a43bf251..67d044dd01c 100644
--- a/app/presenters/member_presenter.rb
+++ b/app/presenters/member_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class MemberPresenter < Gitlab::View::Presenter::Delegated
- presents :member
+ presents ::Member, as: :member
def access_level_roles
member.class.access_level_roles
diff --git a/app/presenters/members_presenter.rb b/app/presenters/members_presenter.rb
index 03ebea36d49..b572cf96235 100644
--- a/app/presenters/members_presenter.rb
+++ b/app/presenters/members_presenter.rb
@@ -3,7 +3,7 @@
class MembersPresenter < Gitlab::View::Presenter::Delegated
include Enumerable
- presents :members
+ presents nil, as: :members
def to_ary
to_a
diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb
index fc8a290f5f7..d19d4964524 100644
--- a/app/presenters/merge_request_presenter.rb
+++ b/app/presenters/merge_request_presenter.rb
@@ -8,9 +8,11 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
include ChecksCollaboration
include Gitlab::Utils::StrongMemoize
+ delegator_override_with Gitlab::Utils::StrongMemoize # TODO: Remove `Gitlab::Utils::StrongMemoize` inclusion as it's duplicate
+
APPROVALS_WIDGET_BASE_TYPE = 'base'
- presents :merge_request
+ presents ::MergeRequest, as: :merge_request
def ci_status
if pipeline
@@ -183,6 +185,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
.can_push_to_branch?(source_branch)
end
+ delegator_override :can_remove_source_branch?
def can_remove_source_branch?
source_branch_exists? && merge_request.can_remove_source_branch?(current_user)
end
@@ -202,6 +205,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
end
end
+ delegator_override :subscribed?
def subscribed?
merge_request.subscribed?(current_user, merge_request.target_project)
end
diff --git a/app/presenters/milestone_presenter.rb b/app/presenters/milestone_presenter.rb
index 6bf8203702f..4084c8740f0 100644
--- a/app/presenters/milestone_presenter.rb
+++ b/app/presenters/milestone_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class MilestonePresenter < Gitlab::View::Presenter::Delegated
- presents :milestone
+ presents ::Milestone, as: :milestone
def milestone_path
url_builder.build(milestone, only_path: true)
diff --git a/app/presenters/pages_domain_presenter.rb b/app/presenters/pages_domain_presenter.rb
index 6ef89760bec..0523f702416 100644
--- a/app/presenters/pages_domain_presenter.rb
+++ b/app/presenters/pages_domain_presenter.rb
@@ -1,7 +1,9 @@
# frozen_string_literal: true
class PagesDomainPresenter < Gitlab::View::Presenter::Delegated
- presents :pages_domain
+ presents ::PagesDomain, as: :pages_domain
+
+ delegator_override :subject # TODO: Fix `Gitlab::View::Presenter::Delegated#subject` not to override `PagesDomain#subject`.
def needs_verification?
Gitlab::CurrentSettings.pages_domain_verification_enabled? && unverified?
diff --git a/app/presenters/project_clusterable_presenter.rb b/app/presenters/project_clusterable_presenter.rb
index 920304e743e..6c4d1143c0f 100644
--- a/app/presenters/project_clusterable_presenter.rb
+++ b/app/presenters/project_clusterable_presenter.rb
@@ -2,7 +2,8 @@
class ProjectClusterablePresenter < ClusterablePresenter
extend ::Gitlab::Utils::Override
- include ActionView::Helpers::UrlHelper
+
+ presents ::Project
override :cluster_status_cluster_path
def cluster_status_cluster_path(cluster, params = {})
@@ -26,7 +27,7 @@ class ProjectClusterablePresenter < ClusterablePresenter
override :learn_more_link
def learn_more_link
- link_to(s_('ClusterIntegration|Learn more about Kubernetes'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
+ ApplicationController.helpers.link_to(s_('ClusterIntegration|Learn more about Kubernetes'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
end
def metrics_dashboard_path(cluster)
diff --git a/app/presenters/project_hook_presenter.rb b/app/presenters/project_hook_presenter.rb
index a65c7221b5a..a696e9fd0ec 100644
--- a/app/presenters/project_hook_presenter.rb
+++ b/app/presenters/project_hook_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class ProjectHookPresenter < Gitlab::View::Presenter::Delegated
- presents :project_hook
+ presents ::ProjectHook, as: :project_hook
def logs_details_path(log)
project_hook_hook_log_path(project, self, log)
diff --git a/app/presenters/project_member_presenter.rb b/app/presenters/project_member_presenter.rb
index 17947266ed7..91d3ae96877 100644
--- a/app/presenters/project_member_presenter.rb
+++ b/app/presenters/project_member_presenter.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class ProjectMemberPresenter < MemberPresenter
+ presents ::ProjectMember
+
private
def admin_member_permission
diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb
index 066f4786cff..bbd8c715f5c 100644
--- a/app/presenters/project_presenter.rb
+++ b/app/presenters/project_presenter.rb
@@ -12,7 +12,10 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
include Gitlab::Utils::StrongMemoize
include Gitlab::Experiment::Dsl
- presents :project
+ delegator_override_with GitlabRoutingHelper # TODO: Remove `GitlabRoutingHelper` inclusion as it's duplicate
+ delegator_override_with Gitlab::Utils::StrongMemoize # TODO: Remove `Gitlab::Utils::StrongMemoize` inclusion as it's duplicate
+
+ presents ::Project, as: :project
AnchorData = Struct.new(:is_link, :label, :link, :class_modifier, :icon, :itemprop, :data)
MAX_TOPICS_TO_SHOW = 3
diff --git a/app/presenters/projects/import_export/project_export_presenter.rb b/app/presenters/projects/import_export/project_export_presenter.rb
index f56760b55df..7b2ffb6d755 100644
--- a/app/presenters/projects/import_export/project_export_presenter.rb
+++ b/app/presenters/projects/import_export/project_export_presenter.rb
@@ -5,16 +5,24 @@ module Projects
class ProjectExportPresenter < Gitlab::View::Presenter::Delegated
include ActiveModel::Serializers::JSON
- presents :project
+ presents ::Project, as: :project
+ # TODO: Remove `ActiveModel::Serializers::JSON` inclusion as it's duplicate
+ delegator_override_with ActiveModel::Serializers::JSON
+ delegator_override_with ActiveModel::Naming
+ delegator_override :include_root_in_json, :include_root_in_json?
+
+ delegator_override :project_members
def project_members
super + converted_group_members
end
+ delegator_override :description
def description
self.respond_to?(:override_description) ? override_description : super
end
+ delegator_override :protected_branches
def protected_branches
project.exported_protected_branches
end
diff --git a/app/presenters/projects/settings/deploy_keys_presenter.rb b/app/presenters/projects/settings/deploy_keys_presenter.rb
index 13290a8e632..e3323b75188 100644
--- a/app/presenters/projects/settings/deploy_keys_presenter.rb
+++ b/app/presenters/projects/settings/deploy_keys_presenter.rb
@@ -5,7 +5,7 @@ module Projects
class DeployKeysPresenter < Gitlab::View::Presenter::Simple
include Gitlab::Utils::StrongMemoize
- presents :project
+ presents ::Project, as: :project
delegate :size, to: :enabled_keys, prefix: true
delegate :size, to: :available_project_keys, prefix: true
delegate :size, to: :available_public_keys, prefix: true
diff --git a/app/presenters/prometheus_alert_presenter.rb b/app/presenters/prometheus_alert_presenter.rb
index 99e24bdcdb9..714329ede71 100644
--- a/app/presenters/prometheus_alert_presenter.rb
+++ b/app/presenters/prometheus_alert_presenter.rb
@@ -3,7 +3,7 @@
class PrometheusAlertPresenter < Gitlab::View::Presenter::Delegated
include ActionView::Helpers::UrlHelper
- presents :prometheus_alert
+ presents ::PrometheusAlert, as: :prometheus_alert
def humanized_text
operator_text =
diff --git a/app/presenters/release_presenter.rb b/app/presenters/release_presenter.rb
index ac27e997b41..c919c7f4c60 100644
--- a/app/presenters/release_presenter.rb
+++ b/app/presenters/release_presenter.rb
@@ -3,8 +3,10 @@
class ReleasePresenter < Gitlab::View::Presenter::Delegated
include ActionView::Helpers::UrlHelper
- presents :release
+ presents ::Release, as: :release
+ # TODO: Remove `delegate` as it's redundant due to SimpleDelegator.
+ delegator_override :tag, :project
delegate :project, :tag, to: :release
def commit_path
@@ -51,6 +53,7 @@ class ReleasePresenter < Gitlab::View::Presenter::Delegated
edit_project_release_url(project, release)
end
+ delegator_override :assets_count
def assets_count
if can_download_code?
release.assets_count
@@ -59,6 +62,7 @@ class ReleasePresenter < Gitlab::View::Presenter::Delegated
end
end
+ delegator_override :name
def name
can_download_code? ? release.name : "Release-#{release.id}"
end
diff --git a/app/presenters/releases/evidence_presenter.rb b/app/presenters/releases/evidence_presenter.rb
index a00cbacb7d8..bdc053a303b 100644
--- a/app/presenters/releases/evidence_presenter.rb
+++ b/app/presenters/releases/evidence_presenter.rb
@@ -4,7 +4,7 @@ module Releases
class EvidencePresenter < Gitlab::View::Presenter::Delegated
include ActionView::Helpers::UrlHelper
- presents :evidence
+ presents ::Releases::Evidence, as: :evidence
def filepath
release = evidence.release
diff --git a/app/presenters/search_service_presenter.rb b/app/presenters/search_service_presenter.rb
index ab43800b9f2..72f967b8beb 100644
--- a/app/presenters/search_service_presenter.rb
+++ b/app/presenters/search_service_presenter.rb
@@ -3,7 +3,7 @@
class SearchServicePresenter < Gitlab::View::Presenter::Delegated
include RendersCommits
- presents :search_service
+ presents ::SearchService, as: :search_service
SCOPE_PRELOAD_METHOD = {
projects: :with_web_entity_associations,
@@ -18,6 +18,7 @@ class SearchServicePresenter < Gitlab::View::Presenter::Delegated
SORT_ENABLED_SCOPES = %w(issues merge_requests epics).freeze
+ delegator_override :search_objects
def search_objects
@search_objects ||= begin
objects = search_service.search_objects(SCOPE_PRELOAD_METHOD[scope.to_sym])
diff --git a/app/presenters/sentry_error_presenter.rb b/app/presenters/sentry_error_presenter.rb
index 669bcb68b7c..5862e54dfc7 100644
--- a/app/presenters/sentry_error_presenter.rb
+++ b/app/presenters/sentry_error_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class SentryErrorPresenter < Gitlab::View::Presenter::Delegated
- presents :error
+ presents nil, as: :error
FrequencyStruct = Struct.new(:time, :count, keyword_init: true)
diff --git a/app/presenters/service_hook_presenter.rb b/app/presenters/service_hook_presenter.rb
index 8f2ba1a905f..91911eb3dff 100644
--- a/app/presenters/service_hook_presenter.rb
+++ b/app/presenters/service_hook_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class ServiceHookPresenter < Gitlab::View::Presenter::Delegated
- presents :service_hook
+ presents ::ServiceHook, as: :service_hook
def logs_details_path(log)
project_service_hook_log_path(integration.project, integration, log)
diff --git a/app/presenters/snippet_blob_presenter.rb b/app/presenters/snippet_blob_presenter.rb
index ab8fc0f905b..4072696eb89 100644
--- a/app/presenters/snippet_blob_presenter.rb
+++ b/app/presenters/snippet_blob_presenter.rb
@@ -3,6 +3,8 @@
class SnippetBlobPresenter < BlobPresenter
include GitlabRoutingHelper
+ presents ::SnippetBlob
+
def rich_data
return unless blob.rich_viewer
diff --git a/app/presenters/snippet_presenter.rb b/app/presenters/snippet_presenter.rb
index 695aa266e2c..8badbe7f54a 100644
--- a/app/presenters/snippet_presenter.rb
+++ b/app/presenters/snippet_presenter.rb
@@ -1,16 +1,18 @@
# frozen_string_literal: true
class SnippetPresenter < Gitlab::View::Presenter::Delegated
- presents :snippet
+ presents ::Snippet, as: :snippet
def raw_url
url_builder.build(snippet, raw: true)
end
+ delegator_override :ssh_url_to_repo
def ssh_url_to_repo
snippet.ssh_url_to_repo if snippet.repository_exists?
end
+ delegator_override :http_url_to_repo
def http_url_to_repo
snippet.http_url_to_repo if snippet.repository_exists?
end
@@ -31,6 +33,7 @@ class SnippetPresenter < Gitlab::View::Presenter::Delegated
snippet.submittable_as_spam_by?(current_user)
end
+ delegator_override :blob
def blob
return snippet.blob if snippet.empty_repo?
diff --git a/app/presenters/terraform/modules_presenter.rb b/app/presenters/terraform/modules_presenter.rb
index 608f69e2019..9e9c6a5cd2b 100644
--- a/app/presenters/terraform/modules_presenter.rb
+++ b/app/presenters/terraform/modules_presenter.rb
@@ -4,7 +4,7 @@ module Terraform
class ModulesPresenter < Gitlab::View::Presenter::Simple
attr_accessor :packages, :system
- presents :modules
+ presents nil, as: :modules
def initialize(packages, system)
@packages = packages
diff --git a/app/presenters/todo_presenter.rb b/app/presenters/todo_presenter.rb
index 291be7848e2..cb8d37be22d 100644
--- a/app/presenters/todo_presenter.rb
+++ b/app/presenters/todo_presenter.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
class TodoPresenter < Gitlab::View::Presenter::Delegated
- presents :todo
+ presents ::Todo, as: :todo
end
diff --git a/app/presenters/tree_entry_presenter.rb b/app/presenters/tree_entry_presenter.rb
index 216b3b0d4c9..0b313d81360 100644
--- a/app/presenters/tree_entry_presenter.rb
+++ b/app/presenters/tree_entry_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class TreeEntryPresenter < Gitlab::View::Presenter::Delegated
- presents :tree
+ presents nil, as: :tree
def web_url
Gitlab::Routing.url_helpers.project_tree_url(tree.repository.project, File.join(tree.commit_id, tree.path))
diff --git a/app/presenters/user_presenter.rb b/app/presenters/user_presenter.rb
index 7cd94082bac..5a99f10b6e7 100644
--- a/app/presenters/user_presenter.rb
+++ b/app/presenters/user_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class UserPresenter < Gitlab::View::Presenter::Delegated
- presents :user
+ presents ::User, as: :user
def group_memberships
should_be_private? ? GroupMember.none : user.group_members
diff --git a/app/presenters/web_hook_log_presenter.rb b/app/presenters/web_hook_log_presenter.rb
index fca03ddb5d7..a5166589073 100644
--- a/app/presenters/web_hook_log_presenter.rb
+++ b/app/presenters/web_hook_log_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class WebHookLogPresenter < Gitlab::View::Presenter::Delegated
- presents :web_hook_log
+ presents ::WebHookLog, as: :web_hook_log
def details_path
web_hook.present.logs_details_path(self)