diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-20 12:26:25 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-20 12:26:25 +0000 |
commit | a09983ae35713f5a2bbb100981116d31ce99826e (patch) | |
tree | 2ee2af7bd104d57086db360a7e6d8c9d5d43667a /app/presenters | |
parent | 18c5ab32b738c0b6ecb4d0df3994000482f34bd8 (diff) | |
download | gitlab-ce-a09983ae35713f5a2bbb100981116d31ce99826e.tar.gz |
Add latest changes from gitlab-org/gitlab@13-2-stable-ee
Diffstat (limited to 'app/presenters')
25 files changed, 1060 insertions, 37 deletions
diff --git a/app/presenters/alert_management/alert_presenter.rb b/app/presenters/alert_management/alert_presenter.rb new file mode 100644 index 00000000000..a515c70152d --- /dev/null +++ b/app/presenters/alert_management/alert_presenter.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +module AlertManagement + class AlertPresenter < Gitlab::View::Presenter::Delegated + include Gitlab::Utils::StrongMemoize + include IncidentManagement::Settings + + MARKDOWN_LINE_BREAK = " \n".freeze + + def initialize(alert, _attributes = {}) + super + + @alert = alert + @project = alert.project + end + + def issue_description + horizontal_line = "\n\n---\n\n" + + [ + issue_summary_markdown, + alert_markdown, + incident_management_setting.issue_template_content + ].compact.join(horizontal_line) + end + + def start_time + started_at&.strftime('%d %B %Y, %-l:%M%p (%Z)') + end + + def issue_summary_markdown + <<~MARKDOWN.chomp + #### Summary + + #{metadata_list} + #{alert_details}#{metric_embed_for_alert} + MARKDOWN + end + + def metrics_dashboard_url; end + + private + + attr_reader :alert, :project + + def alerting_alert + strong_memoize(:alerting_alert) do + Gitlab::Alerting::Alert.new(project: project, payload: alert.payload).present + end + end + + def alert_markdown; end + + def metadata_list + metadata = [] + + metadata << list_item('Start time', start_time) + metadata << list_item('Severity', severity) + metadata << list_item('full_query', backtick(full_query)) if full_query + metadata << list_item('Service', service) if service + metadata << list_item('Monitoring tool', monitoring_tool) if monitoring_tool + metadata << list_item('Hosts', host_links) if hosts.any? + metadata << list_item('Description', description) if description.present? + + metadata.join(MARKDOWN_LINE_BREAK) + end + + def alert_details + if details.present? + <<~MARKDOWN.chomp + + #### Alert Details + + #{details_list} + MARKDOWN + end + end + + def details_list + alert.details + .map { |label, value| list_item(label, value) } + .join(MARKDOWN_LINE_BREAK) + end + + def metric_embed_for_alert; end + + def full_query; end + + def list_item(key, value) + "**#{key}:** #{value}".strip + end + + def backtick(value) + "`#{value}`" + end + + def host_links + hosts.join(' ') + end + end +end diff --git a/app/presenters/alert_management/prometheus_alert_presenter.rb b/app/presenters/alert_management/prometheus_alert_presenter.rb new file mode 100644 index 00000000000..3bcc98e6784 --- /dev/null +++ b/app/presenters/alert_management/prometheus_alert_presenter.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module AlertManagement + class PrometheusAlertPresenter < AlertManagement::AlertPresenter + def metrics_dashboard_url + alerting_alert.metrics_dashboard_url + end + + private + + def alert_markdown + alerting_alert.alert_markdown + end + + def details_list + alerting_alert.annotation_list + end + + def metric_embed_for_alert + alerting_alert.metric_embed_for_alert + end + + def full_query + alerting_alert.full_query + end + end +end diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb index 395eaeea8de..da610f13899 100644 --- a/app/presenters/ci/pipeline_presenter.rb +++ b/app/presenters/ci/pipeline_presenter.rb @@ -110,6 +110,17 @@ module Ci merge_request_presenter&.target_branch_link end + def downloadable_path_for_report_type(file_type) + if (job_artifact = batch_lookup_report_artifact_for_file_type(file_type)) && + can?(current_user, :read_build, job_artifact.job) + download_project_job_artifacts_path( + job_artifact.project, + job_artifact.job, + file_type: file_type, + proxy: true) + end + end + private def plain_ref_name diff --git a/app/presenters/clusterable_presenter.rb b/app/presenters/clusterable_presenter.rb index 5e669ff2e50..efb3cf7f348 100644 --- a/app/presenters/clusterable_presenter.rb +++ b/app/presenters/clusterable_presenter.rb @@ -13,8 +13,7 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated end def can_add_cluster? - can?(current_user, :add_cluster, clusterable) && - (has_no_clusters? || multiple_clusters_available?) + can?(current_user, :add_cluster, clusterable) end def can_create_cluster? @@ -65,7 +64,11 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated raise NotImplementedError end - # Will be overidden in EE + def metrics_dashboard_path(cluster) + raise NotImplementedError + end + + # Will be overridden in EE def environments_cluster_path(cluster) nil end @@ -81,17 +84,6 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated def learn_more_link raise NotImplementedError end - - private - - # Overridden on EE module - def multiple_clusters_available? - false - end - - def has_no_clusters? - clusterable.clusters.empty? - end end ClusterablePresenter.prepend_if_ee('EE::ClusterablePresenter') diff --git a/app/presenters/clusters/cluster_presenter.rb b/app/presenters/clusters/cluster_presenter.rb index c4e3393cac9..c0da5310ca4 100644 --- a/app/presenters/clusters/cluster_presenter.rb +++ b/app/presenters/clusters/cluster_presenter.rb @@ -2,6 +2,7 @@ module Clusters class ClusterPresenter < Gitlab::View::Presenter::Delegated + include ::Gitlab::Utils::StrongMemoize include ActionView::Helpers::SanitizeHelper include ActionView::Helpers::UrlHelper include IconsHelper @@ -60,12 +61,53 @@ module Clusters end end + def gitlab_managed_apps_logs_path + return unless logs_project && can_read_cluster? + + if cluster.application_elastic_stack&.available? + elasticsearch_project_logs_path(logs_project, cluster_id: cluster.id, format: :json) + else + k8s_project_logs_path(logs_project, cluster_id: cluster.id, format: :json) + end + end + def read_only_kubernetes_platform_fields? !cluster.provided_by_user? end + def health_data(clusterable) + { + 'clusters-path': clusterable.index_path, + 'dashboard-endpoint': clusterable.metrics_dashboard_path(cluster), + 'documentation-path': help_page_path('user/project/clusters/index', anchor: 'monitoring-your-kubernetes-cluster-ultimate'), + 'add-dashboard-documentation-path': help_page_path('user/project/integrations/prometheus.md', anchor: 'adding-a-new-dashboard-to-your-project'), + 'empty-getting-started-svg-path': image_path('illustrations/monitoring/getting_started.svg'), + 'empty-loading-svg-path': image_path('illustrations/monitoring/loading.svg'), + 'empty-no-data-svg-path': image_path('illustrations/monitoring/no_data.svg'), + 'empty-no-data-small-svg-path': image_path('illustrations/chart-empty-state-small.svg'), + 'empty-unable-to-connect-svg-path': image_path('illustrations/monitoring/unable_to_connect.svg'), + 'settings-path': '', + 'project-path': '', + 'tags-path': '' + } + end + private + def image_path(path) + ActionController::Base.helpers.image_path(path) + end + + # currently log explorer is only available in the scope of the project + # for group and instance level cluster selected project does not affects + # fetching logs from gitlab managed apps namespace, therefore any project + # available to user will be sufficient. + def logs_project + strong_memoize(:logs_project) do + cluster.all_projects.first + end + end + def clusterable if cluster.group_type? cluster.group diff --git a/app/presenters/group_clusterable_presenter.rb b/app/presenters/group_clusterable_presenter.rb index 21db2f6f96b..dfe8e315f94 100644 --- a/app/presenters/group_clusterable_presenter.rb +++ b/app/presenters/group_clusterable_presenter.rb @@ -43,6 +43,10 @@ class GroupClusterablePresenter < ClusterablePresenter 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') end + + def metrics_dashboard_path(cluster) + metrics_dashboard_group_cluster_path(clusterable, cluster) + end end GroupClusterablePresenter.prepend_if_ee('EE::GroupClusterablePresenter') diff --git a/app/presenters/instance_clusterable_presenter.rb b/app/presenters/instance_clusterable_presenter.rb index 41071bc7bc7..7704e6b59c1 100644 --- a/app/presenters/instance_clusterable_presenter.rb +++ b/app/presenters/instance_clusterable_presenter.rb @@ -81,6 +81,10 @@ class InstanceClusterablePresenter < ClusterablePresenter 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') end + + def metrics_dashboard_path(cluster) + metrics_dashboard_admin_cluster_path(cluster) + end end InstanceClusterablePresenter.prepend_if_ee('EE::InstanceClusterablePresenter') diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb index af98a6ee36a..bccf0340749 100644 --- a/app/presenters/merge_request_presenter.rb +++ b/app/presenters/merge_request_presenter.rb @@ -8,6 +8,8 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated include ChecksCollaboration include Gitlab::Utils::StrongMemoize + APPROVALS_WIDGET_BASE_TYPE = 'base' + presents :merge_request def ci_status @@ -224,6 +226,22 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated end end + def api_approvals_path + expose_path(api_v4_projects_merge_requests_approvals_path(id: project.id, merge_request_iid: merge_request.iid)) + end + + def api_approve_path + expose_path(api_v4_projects_merge_requests_approve_path(id: project.id, merge_request_iid: merge_request.iid)) + end + + def api_unapprove_path + expose_path(api_v4_projects_merge_requests_unapprove_path(id: project.id, merge_request_iid: merge_request.iid)) + end + + def approvals_widget_type + APPROVALS_WIDGET_BASE_TYPE + end + private def cached_can_be_reverted? diff --git a/app/presenters/packages/composer/packages_presenter.rb b/app/presenters/packages/composer/packages_presenter.rb new file mode 100644 index 00000000000..84f266989e9 --- /dev/null +++ b/app/presenters/packages/composer/packages_presenter.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +module Packages + module Composer + class PackagesPresenter + include API::Helpers::RelatedResourcesHelpers + + def initialize(group, packages) + @group = group + @packages = packages + end + + def root + path = api_v4_group___packages_composer_package_name_path({ id: @group.id, package_name: '%package%', format: '.json' }, true) + { 'packages' => [], 'provider-includes' => { 'p/%hash%.json' => { 'sha256' => provider_sha } }, 'providers-url' => path } + end + + def provider + { 'providers' => providers_map } + end + + def package_versions(packages = @packages) + { 'packages' => { packages.first.name => package_versions_map(packages) } } + end + + private + + def package_versions_map(packages) + packages.each_with_object({}) do |package, map| + map[package.version] = package_metadata(package) + end + end + + def package_metadata(package) + json = package.composer_metadatum.composer_json + + json.merge('dist' => package_dist(package), 'uid' => package.id, 'version' => package.version) + end + + def package_dist(package) + sha = package.composer_metadatum.target_sha + archive_api_path = api_v4_projects_packages_composer_archives_package_name_path({ id: package.project_id, package_name: package.name, format: '.zip' }, true) + + { + 'type' => 'zip', + 'url' => expose_url(archive_api_path) + "?sha=#{sha}", + 'reference' => sha, + 'shasum' => '' + } + end + + def providers_map + map = {} + + @packages.group_by(&:name).each_pair do |package_name, packages| + map[package_name] = { 'sha256' => package_versions_sha(packages) } + end + + map + end + + def package_versions_sha(packages) + Digest::SHA256.hexdigest(package_versions(packages).to_json) + end + + def provider_sha + Digest::SHA256.hexdigest(provider.to_json) + end + end + end +end diff --git a/app/presenters/packages/conan/package_presenter.rb b/app/presenters/packages/conan/package_presenter.rb new file mode 100644 index 00000000000..5141c450412 --- /dev/null +++ b/app/presenters/packages/conan/package_presenter.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +module Packages + module Conan + class PackagePresenter + include API::Helpers::RelatedResourcesHelpers + include Gitlab::Utils::StrongMemoize + + attr_reader :params + + def initialize(recipe, user, project, params = {}) + @recipe = recipe + @user = user + @project = project + @params = params + end + + def recipe_urls + map_package_files do |package_file| + build_recipe_file_url(package_file) if package_file.conan_file_metadatum.recipe_file? + end + end + + def recipe_snapshot + map_package_files do |package_file| + package_file.file_md5 if package_file.conan_file_metadatum.recipe_file? + end + end + + def package_urls + map_package_files do |package_file| + next unless package_file.conan_file_metadatum.package_file? && matching_reference?(package_file) + + build_package_file_url(package_file) + end + end + + def package_snapshot + map_package_files do |package_file| + next unless package_file.conan_file_metadatum.package_file? && matching_reference?(package_file) + + package_file.file_md5 + end + end + + private + + def build_recipe_file_url(package_file) + expose_url( + api_v4_packages_conan_v1_files_export_path( + package_name: package.name, + package_version: package.version, + package_username: package.conan_metadatum.package_username, + package_channel: package.conan_metadatum.package_channel, + recipe_revision: package_file.conan_file_metadatum.recipe_revision, + file_name: package_file.file_name + ) + ) + end + + def build_package_file_url(package_file) + expose_url( + api_v4_packages_conan_v1_files_package_path( + package_name: package.name, + package_version: package.version, + package_username: package.conan_metadatum.package_username, + package_channel: package.conan_metadatum.package_channel, + recipe_revision: package_file.conan_file_metadatum.recipe_revision, + conan_package_reference: package_file.conan_file_metadatum.conan_package_reference, + package_revision: package_file.conan_file_metadatum.package_revision, + file_name: package_file.file_name + ) + ) + end + + def map_package_files + package_files.to_a.map do |package_file| + key = package_file.file_name + value = yield(package_file) + next unless key && value + + [key, value] + end.compact.to_h + end + + def package_files + return unless package + + @package_files ||= package.package_files.preload_conan_file_metadata + end + + def package + strong_memoize(:package) do + name, version = @recipe.split('@')[0].split('/') + + @project.packages + .conan + .with_name(name) + .with_version(version) + .order_created + .last + end + end + + def matching_reference?(package_file) + package_file.conan_file_metadatum.conan_package_reference == conan_package_reference + end + + def conan_package_reference + params[:conan_package_reference] + end + end + end +end diff --git a/app/presenters/packages/detail/package_presenter.rb b/app/presenters/packages/detail/package_presenter.rb new file mode 100644 index 00000000000..f6e068302c1 --- /dev/null +++ b/app/presenters/packages/detail/package_presenter.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +module Packages + module Detail + class PackagePresenter + def initialize(package) + @package = package + end + + def detail_view + package_detail = { + id: @package.id, + created_at: @package.created_at, + name: @package.name, + package_files: @package.package_files.map { |pf| build_package_file_view(pf) }, + package_type: @package.package_type, + project_id: @package.project_id, + tags: @package.tags.as_json, + updated_at: @package.updated_at, + version: @package.version + } + + package_detail[:maven_metadatum] = @package.maven_metadatum if @package.maven_metadatum + package_detail[:nuget_metadatum] = @package.nuget_metadatum if @package.nuget_metadatum + package_detail[:dependency_links] = @package.dependency_links.map(&method(:build_dependency_links)) + package_detail[:pipeline] = build_pipeline_info(@package.build_info.pipeline) if @package.build_info + + package_detail + end + + private + + def build_package_file_view(package_file) + { + created_at: package_file.created_at, + download_path: package_file.download_path, + file_name: package_file.file_name, + size: package_file.size + } + end + + def build_pipeline_info(pipeline_info) + { + created_at: pipeline_info.created_at, + id: pipeline_info.id, + sha: pipeline_info.sha, + ref: pipeline_info.ref, + git_commit_message: pipeline_info.git_commit_message, + user: build_user_info(pipeline_info.user), + project: { + name: pipeline_info.project.name, + web_url: pipeline_info.project.web_url + } + } + end + + def build_user_info(user) + return unless user + + { + avatar_url: user.avatar_url, + name: user.name + } + end + + def build_dependency_links(link) + { + name: link.dependency.name, + version_pattern: link.dependency.version_pattern, + target_framework: link.nuget_metadatum&.target_framework + }.compact + end + end + end +end diff --git a/app/presenters/packages/go/module_version_presenter.rb b/app/presenters/packages/go/module_version_presenter.rb new file mode 100644 index 00000000000..4c86eae46cd --- /dev/null +++ b/app/presenters/packages/go/module_version_presenter.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Packages + module Go + class ModuleVersionPresenter + def initialize(version) + @version = version + end + + def name + @version.name + end + + def time + @version.commit.committed_date + end + end + end +end diff --git a/app/presenters/packages/npm/package_presenter.rb b/app/presenters/packages/npm/package_presenter.rb new file mode 100644 index 00000000000..a3ab10d3913 --- /dev/null +++ b/app/presenters/packages/npm/package_presenter.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +module Packages + module Npm + class PackagePresenter + include API::Helpers::RelatedResourcesHelpers + + attr_reader :name, :packages + + NPM_VALID_DEPENDENCY_TYPES = %i[dependencies devDependencies bundleDependencies peerDependencies].freeze + + def initialize(name, packages) + @name = name + @packages = packages + end + + def versions + package_versions = {} + + packages.each do |package| + package_file = package.package_files.last + + next unless package_file + + package_versions[package.version] = build_package_version(package, package_file) + end + + package_versions + end + + def dist_tags + build_package_tags.tap { |t| t["latest"] ||= sorted_versions.last } + end + + private + + def build_package_tags + Hash[ + package_tags.map { |tag| [tag.name, tag.package.version] } + ] + end + + def build_package_version(package, package_file) + { + name: package.name, + version: package.version, + dist: { + shasum: package_file.file_sha1, + tarball: tarball_url(package, package_file) + } + }.tap do |package_version| + package_version.merge!(build_package_dependencies(package)) + end + end + + def tarball_url(package, package_file) + expose_url "#{api_v4_projects_path(id: package.project_id)}" \ + "/packages/npm/#{package.name}" \ + "/-/#{package_file.file_name}" + end + + def build_package_dependencies(package) + dependencies = Hash.new { |h, key| h[key] = {} } + dependency_links = package.dependency_links + .with_dependency_type(NPM_VALID_DEPENDENCY_TYPES) + .includes_dependency + + dependency_links.find_each do |dependency_link| + dependency = dependency_link.dependency + dependencies[dependency_link.dependency_type][dependency.name] = dependency.version_pattern + end + + dependencies + end + + def sorted_versions + versions = packages.map(&:version).compact + VersionSorter.sort(versions) + end + + def package_tags + Packages::Tag.for_packages(packages) + .preload_package + end + end + end +end diff --git a/app/presenters/packages/nuget/package_metadata_presenter.rb b/app/presenters/packages/nuget/package_metadata_presenter.rb new file mode 100644 index 00000000000..500fc982e11 --- /dev/null +++ b/app/presenters/packages/nuget/package_metadata_presenter.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Packages + module Nuget + class PackageMetadataPresenter + include Packages::Nuget::PresenterHelpers + + def initialize(package) + @package = package + end + + def json_url + json_url_for(@package) + end + + def archive_url + archive_url_for(@package) + end + + def catalog_entry + catalog_entry_for(@package) + end + end + end +end diff --git a/app/presenters/packages/nuget/packages_metadata_presenter.rb b/app/presenters/packages/nuget/packages_metadata_presenter.rb new file mode 100644 index 00000000000..5f22d5dd8a1 --- /dev/null +++ b/app/presenters/packages/nuget/packages_metadata_presenter.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +module Packages + module Nuget + class PackagesMetadataPresenter + include Packages::Nuget::PresenterHelpers + include Gitlab::Utils::StrongMemoize + + COUNT = 1.freeze + + def initialize(packages) + @packages = packages + end + + def count + COUNT + end + + def items + [summary] + end + + private + + def summary + { + json_url: json_url, + lower_version: lower_version, + upper_version: upper_version, + packages_count: @packages.count, + packages: @packages.map { |pkg| metadata_for(pkg) } + } + end + + def metadata_for(package) + { + json_url: json_url_for(package), + archive_url: archive_url_for(package), + catalog_entry: catalog_entry_for(package) + } + end + + def json_url + json_url_for(@packages.first) + end + + def lower_version + sorted_versions.first + end + + def upper_version + sorted_versions.last + end + + def sorted_versions + strong_memoize(:sorted_versions) do + versions = @packages.map(&:version).compact + VersionSorter.sort(versions) + end + end + end + end +end diff --git a/app/presenters/packages/nuget/packages_versions_presenter.rb b/app/presenters/packages/nuget/packages_versions_presenter.rb new file mode 100644 index 00000000000..7f4ce4dbb2f --- /dev/null +++ b/app/presenters/packages/nuget/packages_versions_presenter.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Packages + module Nuget + class PackagesVersionsPresenter + def initialize(packages) + @packages = packages + end + + def versions + @packages.pluck_versions.sort + end + end + end +end diff --git a/app/presenters/packages/nuget/presenter_helpers.rb b/app/presenters/packages/nuget/presenter_helpers.rb new file mode 100644 index 00000000000..cc7e8619220 --- /dev/null +++ b/app/presenters/packages/nuget/presenter_helpers.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +module Packages + module Nuget + module PresenterHelpers + include ::API::Helpers::RelatedResourcesHelpers + + BLANK_STRING = '' + PACKAGE_DEPENDENCY_GROUP = 'PackageDependencyGroup' + PACKAGE_DEPENDENCY = 'PackageDependency' + + private + + def json_url_for(package) + path = api_v4_projects_packages_nuget_metadata_package_name_package_version_path( + { + id: package.project_id, + package_name: package.name, + package_version: package.version, + format: '.json' + }, + true + ) + + expose_url(path) + end + + def archive_url_for(package) + path = api_v4_projects_packages_nuget_download_package_name_package_version_package_filename_path( + { + id: package.project_id, + package_name: package.name, + package_version: package.version, + package_filename: package.package_files.last&.file_name + }, + true + ) + + expose_url(path) + end + + def catalog_entry_for(package) + { + json_url: json_url_for(package), + authors: BLANK_STRING, + dependency_groups: dependency_groups_for(package), + package_name: package.name, + package_version: package.version, + archive_url: archive_url_for(package), + summary: BLANK_STRING, + tags: tags_for(package), + metadatum: metadatum_for(package) + } + end + + def dependency_groups_for(package) + base_nuget_id = "#{json_url_for(package)}#dependencyGroup" + + dependency_links_grouped_by_target_framework(package).map do |target_framework, dependency_links| + nuget_id = target_framework_nuget_id(base_nuget_id, target_framework) + { + id: nuget_id, + type: PACKAGE_DEPENDENCY_GROUP, + target_framework: target_framework, + dependencies: dependencies_for(nuget_id, dependency_links) + }.compact + end + end + + def dependency_links_grouped_by_target_framework(package) + package + .dependency_links + .includes_dependency + .preload_nuget_metadatum + .group_by { |dependency_link| dependency_link.nuget_metadatum&.target_framework } + end + + def dependencies_for(nuget_id, dependency_links) + return [] if dependency_links.empty? + + dependency_links.map do |dependency_link| + dependency = dependency_link.dependency + { + id: "#{nuget_id}/#{dependency.name.downcase}", + type: PACKAGE_DEPENDENCY, + name: dependency.name, + range: dependency.version_pattern + } + end + end + + def target_framework_nuget_id(base_nuget_id, target_framework) + target_framework.blank? ? base_nuget_id : "#{base_nuget_id}/#{target_framework.downcase}" + end + + def metadatum_for(package) + metadatum = package.nuget_metadatum + return {} unless metadatum + + metadatum.slice(:project_url, :license_url, :icon_url) + .compact + end + + def base_path_for(package) + api_v4_projects_packages_nuget_path(id: package.project_id) + end + + def tags_for(package) + package.tag_names.join(::Packages::Tag::NUGET_TAGS_SEPARATOR) + end + end + end +end diff --git a/app/presenters/packages/nuget/search_results_presenter.rb b/app/presenters/packages/nuget/search_results_presenter.rb new file mode 100644 index 00000000000..96c8fe7dd2a --- /dev/null +++ b/app/presenters/packages/nuget/search_results_presenter.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module Packages + module Nuget + class SearchResultsPresenter + include Packages::Nuget::PresenterHelpers + include Gitlab::Utils::StrongMemoize + + delegate :total_count, to: :@search + + def initialize(search) + @search = search + @package_versions = {} + end + + def data + strong_memoize(:data) do + @search.results.group_by(&:name).map do |package_name, packages| + latest_version = latest_version(packages) + latest_package = packages.find { |pkg| pkg.version == latest_version } + + { + type: 'Package', + authors: '', + name: package_name, + version: latest_version, + versions: build_package_versions(packages), + summary: '', + total_downloads: 0, + verified: true, + tags: tags_for(latest_package), + metadatum: metadatum_for(latest_package) + } + end + end + end + + private + + def build_package_versions(packages) + packages.map do |pkg| + { + json_url: json_url_for(pkg), + downloads: 0, + version: pkg.version + } + end + end + + def latest_version(packages) + versions = packages.map(&:version).compact + VersionSorter.sort(versions).last # rubocop: disable Style/UnneededSort + end + end + end +end diff --git a/app/presenters/packages/nuget/service_index_presenter.rb b/app/presenters/packages/nuget/service_index_presenter.rb new file mode 100644 index 00000000000..ed00b36b362 --- /dev/null +++ b/app/presenters/packages/nuget/service_index_presenter.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +module Packages + module Nuget + class ServiceIndexPresenter + include API::Helpers::RelatedResourcesHelpers + + SERVICE_VERSIONS = { + download: %w[PackageBaseAddress/3.0.0], + search: %w[SearchQueryService SearchQueryService/3.0.0-beta SearchQueryService/3.0.0-rc], + publish: %w[PackagePublish/2.0.0], + metadata: %w[RegistrationsBaseUrl RegistrationsBaseUrl/3.0.0-beta RegistrationsBaseUrl/3.0.0-rc] + }.freeze + + SERVICE_COMMENTS = { + download: 'Get package content (.nupkg).', + search: 'Filter and search for packages by keyword.', + publish: 'Push and delete (or unlist) packages.', + metadata: 'Get package metadata.' + }.freeze + + VERSION = '3.0.0'.freeze + + def initialize(project) + @project = project + end + + def version + VERSION + end + + def resources + [ + build_service(:download), + build_service(:search), + build_service(:publish), + build_service(:metadata) + ].flatten + end + + private + + def build_service(service_type) + url = build_service_url(service_type) + comment = SERVICE_COMMENTS[service_type] + + SERVICE_VERSIONS[service_type].map do |version| + { :@id => url, :@type => version, :comment => comment } + end + end + + def build_service_url(service_type) + base_path = api_v4_projects_packages_nuget_path(id: @project.id) + + full_path = case service_type + when :download + api_v4_projects_packages_nuget_download_package_name_package_version_package_filename_path( + { + id: @project.id, + package_name: nil, + package_version: nil, + package_filename: nil + }, + true + ) + when :search + "#{base_path}/query" + when :metadata + api_v4_projects_packages_nuget_metadata_package_name_package_version_path( + { + id: @project.id, + package_name: nil, + package_version: nil + }, + true + ) + when :publish + base_path + end + + expose_url(full_path) + end + end + end +end diff --git a/app/presenters/packages/pypi/package_presenter.rb b/app/presenters/packages/pypi/package_presenter.rb new file mode 100644 index 00000000000..4192e974645 --- /dev/null +++ b/app/presenters/packages/pypi/package_presenter.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +# Display package version data acording to PyPi +# Simple API: https://warehouse.pypa.io/api-reference/legacy/#simple-project-api +module Packages + module Pypi + class PackagePresenter + include API::Helpers::RelatedResourcesHelpers + + def initialize(packages, project) + @packages = packages + @project = project + end + + # Returns the HTML body for PyPi simple API. + # Basically a list of package download links for a specific + # package + def body + <<-HTML + <!DOCTYPE html> + <html> + <head> + <title>Links for #{escape(name)}</title> + </head> + <body> + <h1>Links for #{escape(name)}</h1> + #{links} + </body> + </html> + HTML + end + + private + + def links + refs = [] + + @packages.map do |package| + package.package_files.each do |file| + url = build_pypi_package_path(file) + + refs << package_link(url, package.pypi_metadatum.required_python, file.file_name) + end + end + + refs.join + end + + def package_link(url, required_python, filename) + "<a href=\"#{url}\" data-requires-python=\"#{escape(required_python)}\">#{filename}</a><br>" + end + + def build_pypi_package_path(file) + expose_url( + api_v4_projects_packages_pypi_files_file_identifier_path( + { + id: @project.id, + sha256: file.file_sha256, + file_identifier: file.file_name + }, + true + ) + ) + "#sha256=#{file.file_sha256}" + end + + def name + @packages.first.name + end + + def escape(str) + ERB::Util.html_escape(str) + end + end + end +end diff --git a/app/presenters/project_clusterable_presenter.rb b/app/presenters/project_clusterable_presenter.rb index 5c56d42ed27..718f653eab1 100644 --- a/app/presenters/project_clusterable_presenter.rb +++ b/app/presenters/project_clusterable_presenter.rb @@ -38,6 +38,10 @@ class ProjectClusterablePresenter < ClusterablePresenter def learn_more_link 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) + metrics_dashboard_project_cluster_path(clusterable, cluster) + end end ProjectClusterablePresenter.prepend_if_ee('EE::ProjectClusterablePresenter') diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb index a663bc555f6..4e8dae1d508 100644 --- a/app/presenters/project_presenter.rb +++ b/app/presenters/project_presenter.rb @@ -16,7 +16,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated MAX_TOPICS_TO_SHOW = 3 def statistic_icon(icon_name = 'plus-square-o') - sprite_icon(icon_name, size: 16, css_class: 'icon append-right-4') + sprite_icon(icon_name, size: 16, css_class: 'icon gl-mr-2') end def statistics_anchors(show_auto_devops_callout:) diff --git a/app/presenters/projects/prometheus/alert_presenter.rb b/app/presenters/projects/prometheus/alert_presenter.rb index 6009ee4c7be..1cf8b202810 100644 --- a/app/presenters/projects/prometheus/alert_presenter.rb +++ b/app/presenters/projects/prometheus/alert_presenter.rb @@ -6,7 +6,7 @@ module Projects RESERVED_ANNOTATIONS = %w(gitlab_incident_markdown gitlab_y_label title).freeze GENERIC_ALERT_SUMMARY_ANNOTATIONS = %w(monitoring_tool service hosts).freeze MARKDOWN_LINE_BREAK = " \n".freeze - INCIDENT_LABEL_NAME = IncidentManagement::CreateIssueService::INCIDENT_LABEL[:title].freeze + INCIDENT_LABEL_NAME = ::IncidentManagement::CreateIncidentLabelService::LABEL_PROPERTIES[:title].freeze METRIC_TIME_WINDOW = 30.minutes def full_title @@ -58,6 +58,25 @@ module Projects MARKDOWN end + def annotation_list + strong_memoize(:annotation_list) do + annotations + .reject { |annotation| annotation.label.in?(RESERVED_ANNOTATIONS | GENERIC_ALERT_SUMMARY_ANNOTATIONS) } + .map { |annotation| list_item(annotation.label, annotation.value) } + .join(MARKDOWN_LINE_BREAK) + end + end + + def metric_embed_for_alert + "\n[](#{metrics_dashboard_url})" if metrics_dashboard_url + end + + def metrics_dashboard_url + strong_memoize(:metrics_dashboard_url) do + embed_url_for_gitlab_alert || embed_url_for_self_managed_alert + end + end + private def alert_title @@ -93,15 +112,6 @@ module Projects end end - def annotation_list - strong_memoize(:annotation_list) do - annotations - .reject { |annotation| annotation.label.in?(RESERVED_ANNOTATIONS | GENERIC_ALERT_SUMMARY_ANNOTATIONS) } - .map { |annotation| list_item(annotation.label, annotation.value) } - .join(MARKDOWN_LINE_BREAK) - end - end - def list_item(key, value) "**#{key}:** #{value}".strip end @@ -120,12 +130,6 @@ module Projects Array(hosts.value).join(' ') end - def metric_embed_for_alert - url = embed_url_for_gitlab_alert || embed_url_for_self_managed_alert - - "\n[](#{url})" if url - end - def embed_url_for_gitlab_alert return unless gitlab_alert @@ -133,6 +137,7 @@ module Projects project, gitlab_alert.prometheus_metric_id, environment_id: environment.id, + embedded: true, **alert_embed_window_params(embed_time) ) end @@ -144,6 +149,7 @@ module Projects project, environment, embed_json: dashboard_for_self_managed_alert.to_json, + embedded: true, **alert_embed_window_params(embed_time) ) end diff --git a/app/presenters/release_presenter.rb b/app/presenters/release_presenter.rb index 7b0a3d1e7b9..4393ca05f48 100644 --- a/app/presenters/release_presenter.rb +++ b/app/presenters/release_presenter.rb @@ -5,7 +5,7 @@ class ReleasePresenter < Gitlab::View::Presenter::Delegated presents :release - delegate :project, :tag, :assets_count, to: :release + delegate :project, :tag, to: :release def commit_path return unless release.commit && can_download_code? @@ -43,6 +43,18 @@ class ReleasePresenter < Gitlab::View::Presenter::Delegated edit_project_release_url(project, release) end + def assets_count + if can_download_code? + release.assets_count + else + release.assets_count(except: [:sources]) + end + end + + def name + can_download_code? ? release.name : "Release-#{release.id}" + end + private def can_download_code? diff --git a/app/presenters/snippet_blob_presenter.rb b/app/presenters/snippet_blob_presenter.rb index ed9c28bbc2c..d27fe751ab7 100644 --- a/app/presenters/snippet_blob_presenter.rb +++ b/app/presenters/snippet_blob_presenter.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class SnippetBlobPresenter < BlobPresenter + include GitlabRoutingHelper + def rich_data return if blob.binary? return unless blob.rich_viewer @@ -15,15 +17,17 @@ class SnippetBlobPresenter < BlobPresenter end def raw_path - if snippet.is_a?(ProjectSnippet) - raw_project_snippet_path(snippet.project, snippet) - else - raw_snippet_path(snippet) - end + return gitlab_raw_snippet_blob_path(blob) if snippet_multiple_files? + + gitlab_raw_snippet_path(snippet) end private + def snippet_multiple_files? + blob.container.repository_exists? && Feature.enabled?(:snippet_multiple_files, current_user) + end + def snippet blob.container end |