Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3f45eb27e9
commit
c6f0b221b7
65 changed files with 1589 additions and 79 deletions
2
Gemfile
2
Gemfile
|
@ -230,7 +230,7 @@ gem 'discordrb-webhooks-blackst0ne', '~> 3.3', require: false
|
|||
gem 'hipchat', '~> 1.5.0'
|
||||
|
||||
# Jira integration
|
||||
gem 'jira-ruby', '~> 1.7'
|
||||
gem 'jira-ruby', '~> 2.0.0'
|
||||
gem 'atlassian-jwt', '~> 0.2.0'
|
||||
|
||||
# Flowdock integration
|
||||
|
|
|
@ -545,7 +545,7 @@ GEM
|
|||
opentracing (~> 0.3)
|
||||
thrift
|
||||
jaro_winkler (1.5.4)
|
||||
jira-ruby (1.7.1)
|
||||
jira-ruby (2.0.0)
|
||||
activesupport
|
||||
atlassian-jwt
|
||||
multipart-post
|
||||
|
@ -1278,7 +1278,7 @@ DEPENDENCIES
|
|||
icalendar
|
||||
influxdb (~> 0.2)
|
||||
invisible_captcha (~> 0.12.1)
|
||||
jira-ruby (~> 1.7)
|
||||
jira-ruby (~> 2.0.0)
|
||||
js_regex (~> 3.1)
|
||||
json-schema (~> 2.8.0)
|
||||
jwt (~> 2.1.0)
|
||||
|
|
|
@ -170,17 +170,17 @@ export default {
|
|||
</span>
|
||||
</template>
|
||||
<span v-else>{{ __('A deleted user') }}</span>
|
||||
<span class="note-headline-light note-headline-meta d-inline-flex align-items-center">
|
||||
<span class="note-headline-light note-headline-meta d-sm-inline-flex align-items-center">
|
||||
<span class="system-note-message"> <slot></slot> </span>
|
||||
<template v-if="createdAt">
|
||||
<span ref="actionText" class="system-note-separator">
|
||||
<span ref="actionText" class="system-note-separator ml-1">
|
||||
<template v-if="actionText">{{ actionText }}</template>
|
||||
</span>
|
||||
<a
|
||||
v-if="noteTimestampLink"
|
||||
ref="noteTimestampLink"
|
||||
:href="noteTimestampLink"
|
||||
class="note-timestamp system-note-separator"
|
||||
class="note-timestamp system-note-separator mr-1"
|
||||
@click="updateTargetNoteHash"
|
||||
>
|
||||
<time-ago-tooltip :time="createdAt" tooltip-placement="bottom" />
|
||||
|
@ -194,7 +194,7 @@ export default {
|
|||
name="eye-slash"
|
||||
:size="14"
|
||||
:title="__('Private comments are accessible by internal staff only')"
|
||||
class="ml-1 gl-text-gray-800"
|
||||
class="mx-1 gl-text-gray-800"
|
||||
/>
|
||||
<slot name="extra-controls"></slot>
|
||||
<i
|
||||
|
|
|
@ -264,7 +264,7 @@ export default {
|
|||
>
|
||||
<slot slot="note-header-info" name="note-header-info"></slot>
|
||||
<span v-if="commit" v-html="actionText"></span>
|
||||
<span v-else class="d-none d-sm-inline mr-1">·</span>
|
||||
<span v-else class="d-none d-sm-inline">·</span>
|
||||
</note-header>
|
||||
<note-actions
|
||||
:author-id="author.id"
|
||||
|
|
|
@ -107,7 +107,7 @@ export default {
|
|||
<span v-html="actionTextHtml"></span>
|
||||
<template v-if="canSeeDescriptionVersion" slot="extra-controls">
|
||||
·
|
||||
<button type="button" class="btn-blank btn-link" @click="toggleDescriptionVersion">
|
||||
<button type="button" class="btn-blank btn-link ml-1" @click="toggleDescriptionVersion">
|
||||
{{ __('Compare with previous version') }}
|
||||
<icon :name="descriptionVersionToggleIcon" :size="12" class="append-left-5" />
|
||||
</button>
|
||||
|
|
|
@ -18,6 +18,7 @@ class ApplicationController < ActionController::Base
|
|||
include Gitlab::Tracking::ControllerConcern
|
||||
include Gitlab::Experimentation::ControllerConcern
|
||||
include InitializesCurrentUserMode
|
||||
include Gitlab::Logging::CloudflareHelper
|
||||
|
||||
before_action :authenticate_user!, except: [:route_not_found]
|
||||
before_action :enforce_terms!, if: :should_enforce_terms?
|
||||
|
@ -151,6 +152,8 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
payload[:queue_duration_s] = request.env[::Gitlab::Middleware::RailsQueueDuration::GITLAB_RAILS_QUEUE_DURATION_KEY]
|
||||
|
||||
store_cloudflare_headers!(payload, request)
|
||||
end
|
||||
|
||||
##
|
||||
|
|
|
@ -40,6 +40,18 @@ module Emails
|
|||
mail_answer_note_thread(@snippet, @note, note_thread_options(recipient_id, reason))
|
||||
end
|
||||
|
||||
def note_design_email(recipient_id, note_id, reason = nil)
|
||||
setup_note_mail(note_id, recipient_id)
|
||||
|
||||
design = @note.noteable
|
||||
@target_url = ::Gitlab::Routing.url_helpers.designs_project_issue_url(
|
||||
@note.resource_parent,
|
||||
design.issue,
|
||||
note_target_url_query_params.merge(vueroute: design.filename)
|
||||
)
|
||||
mail_answer_note_thread(design, @note, note_thread_options(recipient_id, reason))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def note_target_url_options
|
||||
|
|
|
@ -2,17 +2,19 @@
|
|||
|
||||
module AlertManagement
|
||||
class UpdateAlertStatusService
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
# @param alert [AlertManagement::Alert]
|
||||
# @param status [Integer] Must match a value from AlertManagement::Alert::STATUSES
|
||||
def initialize(alert, status)
|
||||
@alert = alert
|
||||
@status = status
|
||||
end
|
||||
|
||||
def execute
|
||||
return error('Invalid status') unless AlertManagement::Alert::STATUSES.key?(status.to_sym)
|
||||
return error('Invalid status') unless status_key
|
||||
|
||||
alert.status_event = AlertManagement::Alert::STATUS_EVENTS[status.to_sym]
|
||||
|
||||
if alert.save
|
||||
if alert.update(status_event: status_event)
|
||||
success
|
||||
else
|
||||
error(alert.errors.full_messages.to_sentence)
|
||||
|
@ -23,6 +25,16 @@ module AlertManagement
|
|||
|
||||
attr_reader :alert, :status
|
||||
|
||||
def status_key
|
||||
strong_memoize(:status_key) do
|
||||
AlertManagement::Alert::STATUSES.key(status)
|
||||
end
|
||||
end
|
||||
|
||||
def status_event
|
||||
AlertManagement::Alert::STATUS_EVENTS[status_key]
|
||||
end
|
||||
|
||||
def success
|
||||
ServiceResponse.success(payload: { alert: alert })
|
||||
end
|
||||
|
|
|
@ -56,7 +56,10 @@ module Projects
|
|||
end
|
||||
|
||||
def exporters
|
||||
[version_saver, avatar_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver, lfs_saver, snippets_repo_saver]
|
||||
[
|
||||
version_saver, avatar_saver, project_tree_saver, uploads_saver,
|
||||
repo_saver, wiki_repo_saver, lfs_saver, snippets_repo_saver, design_repo_saver
|
||||
]
|
||||
end
|
||||
|
||||
def version_saver
|
||||
|
@ -95,6 +98,10 @@ module Projects
|
|||
Gitlab::ImportExport::SnippetsRepoSaver.new(current_user: current_user, project: project, shared: shared)
|
||||
end
|
||||
|
||||
def design_repo_saver
|
||||
Gitlab::ImportExport::DesignRepoSaver.new(project: project, shared: shared)
|
||||
end
|
||||
|
||||
def cleanup
|
||||
FileUtils.rm_rf(shared.archive_path) if shared&.archive_path
|
||||
end
|
||||
|
@ -117,5 +124,3 @@ module Projects
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Projects::ImportExport::ExportService.prepend_if_ee('EE::Projects::ImportExport::ExportService')
|
||||
|
|
1
app/views/notify/note_design_email.html.haml
Normal file
1
app/views/notify/note_design_email.html.haml
Normal file
|
@ -0,0 +1 @@
|
|||
= render 'note_email'
|
1
app/views/notify/note_design_email.text.erb
Normal file
1
app/views/notify/note_design_email.text.erb
Normal file
|
@ -0,0 +1 @@
|
|||
<%= render 'note_email' %>
|
|
@ -1011,6 +1011,13 @@
|
|||
:resource_boundary: :unknown
|
||||
:weight: 1
|
||||
:idempotent:
|
||||
- :name: design_management_new_version
|
||||
:feature_category: :design_management
|
||||
:has_external_dependencies:
|
||||
:urgency: :low
|
||||
:resource_boundary: :memory
|
||||
:weight: 1
|
||||
:idempotent:
|
||||
- :name: detect_repository_languages
|
||||
:feature_category: :source_code_management
|
||||
:has_external_dependencies:
|
||||
|
|
31
app/workers/design_management/new_version_worker.rb
Normal file
31
app/workers/design_management/new_version_worker.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DesignManagement
|
||||
class NewVersionWorker # rubocop:disable Scalability/IdempotentWorker
|
||||
include ApplicationWorker
|
||||
|
||||
feature_category :design_management
|
||||
# Declare this worker as memory bound due to
|
||||
# `GenerateImageVersionsService` resizing designs
|
||||
worker_resource_boundary :memory
|
||||
|
||||
def perform(version_id)
|
||||
version = DesignManagement::Version.find(version_id)
|
||||
|
||||
add_system_note(version)
|
||||
generate_image_versions(version)
|
||||
rescue ActiveRecord::RecordNotFound => e
|
||||
Sidekiq.logger.warn(e)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_system_note(version)
|
||||
SystemNoteService.design_version_added(version)
|
||||
end
|
||||
|
||||
def generate_image_versions(version)
|
||||
DesignManagement::GenerateImageVersionsService.new(version).execute
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Fix 'not enough data' in Value Stream Analytics when low median values are
|
||||
returned
|
||||
merge_request: 31315
|
||||
author:
|
||||
type: fixed
|
5
changelogs/unreleased/217310.yml
Normal file
5
changelogs/unreleased/217310.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix missing space on system notes
|
||||
merge_request: 31598
|
||||
author:
|
||||
type: fixed
|
5
changelogs/unreleased/fix-branch-dot-txt.yml
Normal file
5
changelogs/unreleased/fix-branch-dot-txt.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix API requests for branch names ending in .txt
|
||||
merge_request: 31446
|
||||
author: Daniel Stone
|
||||
type: fixed
|
5
changelogs/unreleased/sh-log-cloudflare-headers.yml
Normal file
5
changelogs/unreleased/sh-log-cloudflare-headers.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Log Cloudflare request headers
|
||||
merge_request: 31532
|
||||
author:
|
||||
type: added
|
|
@ -99,25 +99,29 @@ Set up the Jenkins project you’re going to run your build on.
|
|||
1. Check the following checkboxes:
|
||||
- **Accepted Merge Request Events**
|
||||
- **Closed Merge Request Events**
|
||||
1. If you created a **Freestyle** project, choose Publish build status to GitLab in the Post-build Actions section.
|
||||
If you created a **Pipeline** project, you must use a pipeline script to update the status on
|
||||
GitLab. The following is an example pipeline script:
|
||||
1. Specify how build status is reported to GitLab:
|
||||
- If you created a **Freestyle** project, in the **Post-build Actions** section, choose
|
||||
**Publish build status to GitLab**.
|
||||
- If you created a **Pipeline** project, you must use a Jenkins Pipeline script to update the status on
|
||||
GitLab.
|
||||
|
||||
```plaintext
|
||||
pipeline {
|
||||
agent any
|
||||
Example Jenkins Pipeline script:
|
||||
|
||||
stages {
|
||||
stage('gitlab') {
|
||||
steps {
|
||||
echo 'Notify GitLab'
|
||||
updateGitlabCommitStatus name: 'build', state: 'pending'
|
||||
updateGitlabCommitStatus name: 'build', state: 'success'
|
||||
```groovy
|
||||
pipeline {
|
||||
agent any
|
||||
|
||||
stages {
|
||||
stage('gitlab') {
|
||||
steps {
|
||||
echo 'Notify GitLab'
|
||||
updateGitlabCommitStatus name: 'build', state: 'pending'
|
||||
updateGitlabCommitStatus name: 'build', state: 'success'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
## Configure the GitLab project
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ module API
|
|||
|
||||
BRANCH_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(branch: API::NO_SLASH_URL_PART_REGEX)
|
||||
|
||||
after_validation { content_type "application/json" }
|
||||
|
||||
before do
|
||||
require_repository_enabled!
|
||||
authorize! :download_code, user_project
|
||||
|
|
16
lib/api/entities/design_management/design.rb
Normal file
16
lib/api/entities/design_management/design.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
module DesignManagement
|
||||
class Design < Grape::Entity
|
||||
expose :id
|
||||
expose :project_id
|
||||
expose :filename
|
||||
expose :image_url do |design|
|
||||
::Gitlab::UrlBuilder.build(design)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -31,6 +31,8 @@ module API
|
|||
end
|
||||
|
||||
def todo_target_url(todo)
|
||||
return design_todo_target_url(todo) if todo.for_design?
|
||||
|
||||
target_type = todo.target_type.underscore
|
||||
target_url = "#{todo.resource_parent.class.to_s.underscore}_#{target_type}_url"
|
||||
|
||||
|
@ -42,6 +44,16 @@ module API
|
|||
def todo_target_anchor(todo)
|
||||
"note_#{todo.note_id}" if todo.note_id?
|
||||
end
|
||||
|
||||
def design_todo_target_url(todo)
|
||||
design = todo.target
|
||||
path_options = {
|
||||
anchor: todo_target_anchor(todo),
|
||||
vueroute: design.filename
|
||||
}
|
||||
|
||||
::Gitlab::Routing.url_helpers.designs_project_issue_url(design.project, design.issue, path_options)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,8 +28,24 @@ module Banzai
|
|||
def parent_records(parent, ids)
|
||||
parent.issues.where(iid: ids.to_a)
|
||||
end
|
||||
|
||||
def object_link_text_extras(issue, matches)
|
||||
super + design_link_extras(issue, matches.named_captures['path'])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def design_link_extras(issue, path)
|
||||
if path == '/designs' && read_designs?(issue)
|
||||
['designs']
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def read_designs?(issue)
|
||||
Ability.allowed?(current_user, :read_design, issue)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Banzai::Filter::IssueReferenceFilter.prepend_if_ee('EE::Banzai::Filter::IssueReferenceFilter')
|
||||
|
|
31
lib/banzai/reference_parser/design_parser.rb
Normal file
31
lib/banzai/reference_parser/design_parser.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Banzai
|
||||
module ReferenceParser
|
||||
class DesignParser < BaseParser
|
||||
self.reference_type = :design
|
||||
|
||||
def references_relation
|
||||
DesignManagement::Design
|
||||
end
|
||||
|
||||
def nodes_visible_to_user(user, nodes)
|
||||
issues = issues_for_nodes(nodes)
|
||||
issue_attr = 'data-issue'
|
||||
|
||||
nodes.select do |node|
|
||||
if node.has_attribute?(issue_attr)
|
||||
can?(user, :read_design, issues[node])
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def issues_for_nodes(nodes)
|
||||
relation = Issue.includes(project: [:project_feature])
|
||||
grouped_objects_for_nodes(nodes, relation, 'data-issue')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -15,7 +15,7 @@ module Gitlab
|
|||
@query = @query.select(median_duration_in_seconds.as('median'))
|
||||
result = execute_query(@query).first || {}
|
||||
|
||||
result['median'] ? result['median'].to_i : nil
|
||||
result['median'] || nil
|
||||
end
|
||||
|
||||
def days
|
||||
|
|
18
lib/gitlab/grape_logging/loggers/cloudflare_logger.rb
Normal file
18
lib/gitlab/grape_logging/loggers/cloudflare_logger.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module GrapeLogging
|
||||
module Loggers
|
||||
class CloudflareLogger < ::GrapeLogging::Loggers::Base
|
||||
include ::Gitlab::Logging::CloudflareHelper
|
||||
|
||||
def parameters(request, _response)
|
||||
data = {}
|
||||
store_cloudflare_headers!(data, request)
|
||||
|
||||
data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -42,6 +42,10 @@ module Gitlab
|
|||
"project.wiki.bundle"
|
||||
end
|
||||
|
||||
def design_repo_bundle_filename
|
||||
'project.design.bundle'
|
||||
end
|
||||
|
||||
def snippet_repo_bundle_dir
|
||||
'snippets'
|
||||
end
|
||||
|
|
15
lib/gitlab/import_export/design_repo_restorer.rb
Normal file
15
lib/gitlab/import_export/design_repo_restorer.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module ImportExport
|
||||
class DesignRepoRestorer < RepoRestorer
|
||||
def initialize(project:, shared:, path_to_bundle:)
|
||||
super(project: project, shared: shared, path_to_bundle: path_to_bundle)
|
||||
|
||||
@repository = project.design_repository
|
||||
end
|
||||
|
||||
# `restore` method is handled in super class
|
||||
end
|
||||
end
|
||||
end
|
19
lib/gitlab/import_export/design_repo_saver.rb
Normal file
19
lib/gitlab/import_export/design_repo_saver.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module ImportExport
|
||||
class DesignRepoSaver < RepoSaver
|
||||
def save
|
||||
@repository = project.design_repository
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def bundle_full_path
|
||||
File.join(shared.export_path, ::Gitlab::ImportExport.design_repo_bundle_filename)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -34,7 +34,7 @@ module Gitlab
|
|||
attr_accessor :archive_file, :current_user, :project, :shared
|
||||
|
||||
def restorers
|
||||
[repo_restorer, wiki_restorer, project_tree, avatar_restorer,
|
||||
[repo_restorer, wiki_restorer, project_tree, avatar_restorer, design_repo_restorer,
|
||||
uploads_restorer, lfs_restorer, statistics_restorer, snippets_repo_restorer]
|
||||
end
|
||||
|
||||
|
@ -71,6 +71,12 @@ module Gitlab
|
|||
wiki_enabled: project.wiki_enabled?)
|
||||
end
|
||||
|
||||
def design_repo_restorer
|
||||
Gitlab::ImportExport::DesignRepoRestorer.new(path_to_bundle: design_repo_path,
|
||||
shared: shared,
|
||||
project: project)
|
||||
end
|
||||
|
||||
def uploads_restorer
|
||||
Gitlab::ImportExport::UploadsRestorer.new(project: project, shared: shared)
|
||||
end
|
||||
|
@ -101,6 +107,10 @@ module Gitlab
|
|||
File.join(shared.export_path, Gitlab::ImportExport.wiki_repo_bundle_filename)
|
||||
end
|
||||
|
||||
def design_repo_path
|
||||
File.join(shared.export_path, Gitlab::ImportExport.design_repo_bundle_filename)
|
||||
end
|
||||
|
||||
def remove_import_file
|
||||
upload = project.import_export_upload
|
||||
|
||||
|
@ -141,5 +151,3 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Gitlab::ImportExport::Importer.prepend_if_ee('EE::Gitlab::ImportExport::Importer')
|
||||
|
|
|
@ -29,6 +29,14 @@ tree:
|
|||
- resource_label_events:
|
||||
- label:
|
||||
- :priorities
|
||||
- designs:
|
||||
- notes:
|
||||
- :author
|
||||
- events:
|
||||
- :push_event_payload
|
||||
- design_versions:
|
||||
- actions:
|
||||
- :design # Duplicate export of issues.designs in order to link the record to both Issue and Action
|
||||
- :issue_assignees
|
||||
- :zoom_meetings
|
||||
- :sentry_issue
|
||||
|
@ -287,6 +295,7 @@ excluded_attributes:
|
|||
actions:
|
||||
- :design_id
|
||||
- :version_id
|
||||
- image_v432x230
|
||||
links:
|
||||
- :release_id
|
||||
project_members:
|
||||
|
@ -379,14 +388,6 @@ ee:
|
|||
tree:
|
||||
project:
|
||||
- issues:
|
||||
- designs:
|
||||
- notes:
|
||||
- :author
|
||||
- events:
|
||||
- :push_event_payload
|
||||
- design_versions:
|
||||
- actions:
|
||||
- :design # Duplicate export of issues.designs in order to link the record to both Issue and Action
|
||||
- epic_issue:
|
||||
- :epic
|
||||
- protected_branches:
|
||||
|
@ -394,6 +395,3 @@ ee:
|
|||
- protected_environments:
|
||||
- :deploy_access_levels
|
||||
- :service_desk_setting
|
||||
excluded_attributes:
|
||||
actions:
|
||||
- image_v432x230
|
||||
|
|
|
@ -57,6 +57,8 @@ module Gitlab
|
|||
|
||||
# Returns Arel clause for a particular model or `nil`.
|
||||
def where_clause_for_klass
|
||||
return attrs_to_arel(attributes.slice('filename')).and(table[:issue_id].eq(nil)) if design?
|
||||
|
||||
attrs_to_arel(attributes.slice('iid')) if merge_request?
|
||||
end
|
||||
|
||||
|
@ -95,6 +97,10 @@ module Gitlab
|
|||
klass == Epic
|
||||
end
|
||||
|
||||
def design?
|
||||
klass == DesignManagement::Design
|
||||
end
|
||||
|
||||
# If an existing group milestone used the IID
|
||||
# claim the IID back and set the group milestone to use one available
|
||||
# This is necessary to fix situations like the following:
|
||||
|
@ -115,5 +121,3 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Gitlab::ImportExport::Project::ObjectBuilder.prepend_if_ee('EE::Gitlab::ImportExport::Project::ObjectBuilder')
|
||||
|
|
|
@ -17,6 +17,10 @@ module Gitlab
|
|||
merge_access_levels: 'ProtectedBranch::MergeAccessLevel',
|
||||
push_access_levels: 'ProtectedBranch::PushAccessLevel',
|
||||
create_access_levels: 'ProtectedTag::CreateAccessLevel',
|
||||
design: 'DesignManagement::Design',
|
||||
designs: 'DesignManagement::Design',
|
||||
design_versions: 'DesignManagement::Version',
|
||||
actions: 'DesignManagement::Action',
|
||||
labels: :project_labels,
|
||||
priorities: :label_priorities,
|
||||
auto_devops: :project_auto_devops,
|
||||
|
@ -51,6 +55,7 @@ module Gitlab
|
|||
container_expiration_policy
|
||||
external_pull_request
|
||||
external_pull_requests
|
||||
DesignManagement::Design
|
||||
].freeze
|
||||
|
||||
def create
|
||||
|
|
23
lib/gitlab/logging/cloudflare_helper.rb
Normal file
23
lib/gitlab/logging/cloudflare_helper.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Logging
|
||||
module CloudflareHelper
|
||||
CLOUDFLARE_CUSTOM_HEADERS = { 'Cf-Ray' => :cf_ray, 'Cf-Request-Id' => :cf_request_id }.freeze
|
||||
|
||||
def store_cloudflare_headers!(payload, request)
|
||||
CLOUDFLARE_CUSTOM_HEADERS.each do |header, value|
|
||||
payload[value] = request.headers[header] if valid_cloudflare_header?(request.headers[header])
|
||||
end
|
||||
end
|
||||
|
||||
def valid_cloudflare_header?(value)
|
||||
return false unless value.present?
|
||||
return false if value.length > 64
|
||||
return false if value.index(/[^[:alnum:]]/)
|
||||
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,6 +3,8 @@
|
|||
module Gitlab
|
||||
module Lograge
|
||||
module CustomOptions
|
||||
include ::Gitlab::Logging::CloudflareHelper
|
||||
|
||||
LIMITED_ARRAY_SENTINEL = { key: 'truncated', value: '...' }.freeze
|
||||
IGNORE_PARAMS = Set.new(%w(controller action format)).freeze
|
||||
|
||||
|
@ -31,6 +33,10 @@ module Gitlab
|
|||
payload[:cpu_s] = cpu_s.round(2)
|
||||
end
|
||||
|
||||
CLOUDFLARE_CUSTOM_HEADERS.each do |_, value|
|
||||
payload[value] = event.payload[value] if event.payload[value]
|
||||
end
|
||||
|
||||
# https://github.com/roidrage/lograge#logging-errors--exceptions
|
||||
exception = event.payload[:exception_object]
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ module Gitlab
|
|||
%w(FileUploader Project),
|
||||
%w(PersonalFileUploader Snippet),
|
||||
%w(NamespaceFileUploader Snippet),
|
||||
%w(DesignManagement::DesignV432x230Uploader DesignManagement::Action :image_v432x230),
|
||||
%w(FileUploader MergeRequest)].freeze
|
||||
|
||||
def initialize(args, logger)
|
||||
|
@ -74,5 +75,3 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Gitlab::Uploads::MigrationHelper.prepend_if_ee('EE::Gitlab::Uploads::MigrationHelper')
|
||||
|
|
|
@ -11,6 +11,7 @@ module Gitlab
|
|||
class << self
|
||||
include ActionView::RecordIdentifier
|
||||
|
||||
# rubocop:disable Metrics/CyclomaticComplexity
|
||||
def build(object, **options)
|
||||
# Objects are sometimes wrapped in a BatchLoader instance
|
||||
case object.itself
|
||||
|
@ -38,10 +39,13 @@ module Gitlab
|
|||
wiki_url(object, **options)
|
||||
when WikiPage
|
||||
instance.project_wiki_url(object.wiki.project, object.slug, **options)
|
||||
when ::DesignManagement::Design
|
||||
design_url(object, **options)
|
||||
else
|
||||
raise NotImplementedError.new("No URL builder defined for #{object.inspect}")
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/CyclomaticComplexity
|
||||
|
||||
def commit_url(commit, **options)
|
||||
return '' unless commit.project
|
||||
|
@ -78,6 +82,17 @@ module Gitlab
|
|||
raise NotImplementedError.new("No URL builder defined for #{object.inspect}")
|
||||
end
|
||||
end
|
||||
|
||||
def design_url(design, **options)
|
||||
size, ref = options.values_at(:size, :ref)
|
||||
options.except!(:size, :ref)
|
||||
|
||||
if size
|
||||
instance.project_design_management_designs_resized_image_url(design.project, design, ref, size, **options)
|
||||
else
|
||||
instance.project_design_management_designs_raw_image_url(design.project, design, ref, **options)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -223,7 +223,8 @@ module Gitlab
|
|||
Gitlab::UsageDataCounters::CycleAnalyticsCounter,
|
||||
Gitlab::UsageDataCounters::ProductivityAnalyticsCounter,
|
||||
Gitlab::UsageDataCounters::SourceCodeCounter,
|
||||
Gitlab::UsageDataCounters::MergeRequestCounter
|
||||
Gitlab::UsageDataCounters::MergeRequestCounter,
|
||||
Gitlab::UsageDataCounters::DesignsCounter
|
||||
]
|
||||
end
|
||||
|
||||
|
|
42
lib/gitlab/usage_data_counters/designs_counter.rb
Normal file
42
lib/gitlab/usage_data_counters/designs_counter.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab::UsageDataCounters
|
||||
class DesignsCounter
|
||||
extend Gitlab::UsageDataCounters::RedisCounter
|
||||
|
||||
KNOWN_EVENTS = %w[create update delete].map(&:freeze).freeze
|
||||
|
||||
UnknownEvent = Class.new(StandardError)
|
||||
|
||||
class << self
|
||||
# Each event gets a unique Redis key
|
||||
def redis_key(event)
|
||||
raise UnknownEvent, event unless KNOWN_EVENTS.include?(event.to_s)
|
||||
|
||||
"USAGE_DESIGN_MANAGEMENT_DESIGNS_#{event}".upcase
|
||||
end
|
||||
|
||||
def count(event)
|
||||
increment(redis_key(event))
|
||||
end
|
||||
|
||||
def read(event)
|
||||
total_count(redis_key(event))
|
||||
end
|
||||
|
||||
def totals
|
||||
KNOWN_EVENTS.map { |event| [counter_key(event), read(event)] }.to_h
|
||||
end
|
||||
|
||||
def fallback_totals
|
||||
KNOWN_EVENTS.map { |event| [counter_key(event), -1] }.to_h
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def counter_key(event)
|
||||
"design_management_designs_#{event}".to_sym
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -16804,12 +16804,18 @@ msgstr ""
|
|||
msgid "Promotions|This feature is locked."
|
||||
msgstr ""
|
||||
|
||||
msgid "Promotions|Track activity with Contribution Analytics."
|
||||
msgstr ""
|
||||
|
||||
msgid "Promotions|Upgrade plan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Promotions|Upgrade your plan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Promotions|Upgrade your plan to activate Contribution Analytics."
|
||||
msgstr ""
|
||||
|
||||
msgid "Promotions|Weight"
|
||||
msgstr ""
|
||||
|
||||
|
@ -16819,6 +16825,9 @@ msgstr ""
|
|||
msgid "Promotions|When you have a lot of issues, it can be hard to get an overview. By adding a weight to your issues, you can get a better idea of the effort, cost, required time, or value of each, and so better manage them."
|
||||
msgstr ""
|
||||
|
||||
msgid "Promotions|With Contribution Analytics you can have an overview for the activity of issues, merge requests, and push events of your organization and its members."
|
||||
msgstr ""
|
||||
|
||||
msgid "Prompt users to upload SSH keys"
|
||||
msgstr ""
|
||||
|
||||
|
@ -22284,9 +22293,6 @@ msgstr ""
|
|||
msgid "Tracing"
|
||||
msgstr ""
|
||||
|
||||
msgid "Track activity with Contribution Analytics."
|
||||
msgstr ""
|
||||
|
||||
msgid "Track groups of issues that share a theme, across projects and milestones"
|
||||
msgstr ""
|
||||
|
||||
|
@ -22791,9 +22797,6 @@ msgstr ""
|
|||
msgid "Upgrade your plan to activate Audit Events."
|
||||
msgstr ""
|
||||
|
||||
msgid "Upgrade your plan to activate Contribution Analytics."
|
||||
msgstr ""
|
||||
|
||||
msgid "Upgrade your plan to activate Group Webhooks."
|
||||
msgstr ""
|
||||
|
||||
|
@ -24039,9 +24042,6 @@ msgstr ""
|
|||
msgid "Will deploy to"
|
||||
msgstr ""
|
||||
|
||||
msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
|
||||
msgstr ""
|
||||
|
||||
msgid "Withdraw Access Request"
|
||||
msgstr ""
|
||||
|
||||
|
|
517
spec/fixtures/lib/gitlab/import_export/designs/project.json
vendored
Normal file
517
spec/fixtures/lib/gitlab/import_export/designs/project.json
vendored
Normal file
|
@ -0,0 +1,517 @@
|
|||
{
|
||||
"description":"",
|
||||
"visibility_level":0,
|
||||
"archived":false,
|
||||
"merge_requests_template":null,
|
||||
"merge_requests_rebase_enabled":false,
|
||||
"approvals_before_merge":0,
|
||||
"reset_approvals_on_push":true,
|
||||
"merge_requests_ff_only_enabled":false,
|
||||
"issues_template":null,
|
||||
"shared_runners_enabled":true,
|
||||
"build_coverage_regex":null,
|
||||
"build_allow_git_fetch":true,
|
||||
"build_timeout":3600,
|
||||
"pending_delete":false,
|
||||
"public_builds":true,
|
||||
"last_repository_check_failed":null,
|
||||
"container_registry_enabled":true,
|
||||
"only_allow_merge_if_pipeline_succeeds":false,
|
||||
"has_external_issue_tracker":false,
|
||||
"request_access_enabled":false,
|
||||
"has_external_wiki":false,
|
||||
"ci_config_path":null,
|
||||
"only_allow_merge_if_all_discussions_are_resolved":false,
|
||||
"repository_size_limit":null,
|
||||
"printing_merge_request_link_enabled":true,
|
||||
"auto_cancel_pending_pipelines":"enabled",
|
||||
"service_desk_enabled":null,
|
||||
"delete_error":null,
|
||||
"disable_overriding_approvers_per_merge_request":null,
|
||||
"resolve_outdated_diff_discussions":false,
|
||||
"jobs_cache_index":null,
|
||||
"external_authorization_classification_label":null,
|
||||
"pages_https_only":false,
|
||||
"external_webhook_token":null,
|
||||
"merge_requests_author_approval":null,
|
||||
"merge_requests_disable_committers_approval":null,
|
||||
"require_password_to_approve":null,
|
||||
"labels":[
|
||||
|
||||
],
|
||||
"milestones":[
|
||||
|
||||
],
|
||||
"issues":[
|
||||
{
|
||||
"id":469,
|
||||
"title":"issue 1",
|
||||
"author_id":1,
|
||||
"project_id":30,
|
||||
"created_at":"2019-08-07T03:57:55.007Z",
|
||||
"updated_at":"2019-08-07T03:57:55.007Z",
|
||||
"description":"",
|
||||
"state":"opened",
|
||||
"iid":1,
|
||||
"updated_by_id":null,
|
||||
"weight":null,
|
||||
"confidential":false,
|
||||
"due_date":null,
|
||||
"moved_to_id":null,
|
||||
"lock_version":0,
|
||||
"time_estimate":0,
|
||||
"relative_position":1073742323,
|
||||
"service_desk_reply_to":null,
|
||||
"last_edited_at":null,
|
||||
"last_edited_by_id":null,
|
||||
"discussion_locked":null,
|
||||
"closed_at":null,
|
||||
"closed_by_id":null,
|
||||
"state_id":1,
|
||||
"events":[
|
||||
{
|
||||
"id":1775,
|
||||
"project_id":30,
|
||||
"author_id":1,
|
||||
"target_id":469,
|
||||
"created_at":"2019-08-07T03:57:55.158Z",
|
||||
"updated_at":"2019-08-07T03:57:55.158Z",
|
||||
"target_type":"Issue",
|
||||
"action":1
|
||||
}
|
||||
],
|
||||
"timelogs":[
|
||||
|
||||
],
|
||||
"notes":[
|
||||
|
||||
],
|
||||
"label_links":[
|
||||
|
||||
],
|
||||
"resource_label_events":[
|
||||
|
||||
],
|
||||
"issue_assignees":[
|
||||
|
||||
],
|
||||
"designs":[
|
||||
{
|
||||
"id":38,
|
||||
"project_id":30,
|
||||
"issue_id":469,
|
||||
"filename":"chirrido3.jpg",
|
||||
"notes":[
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"id":39,
|
||||
"project_id":30,
|
||||
"issue_id":469,
|
||||
"filename":"jonathan_richman.jpg",
|
||||
"notes":[
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"id":40,
|
||||
"project_id":30,
|
||||
"issue_id":469,
|
||||
"filename":"mariavontrap.jpeg",
|
||||
"notes":[
|
||||
|
||||
]
|
||||
}
|
||||
],
|
||||
"design_versions":[
|
||||
{
|
||||
"id":24,
|
||||
"sha":"9358d1bac8ff300d3d2597adaa2572a20f7f8703",
|
||||
"issue_id":469,
|
||||
"author_id":1,
|
||||
"actions":[
|
||||
{
|
||||
"design_id":38,
|
||||
"version_id":24,
|
||||
"event":0,
|
||||
"design":{
|
||||
"id":38,
|
||||
"project_id":30,
|
||||
"issue_id":469,
|
||||
"filename":"chirrido3.jpg"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id":25,
|
||||
"sha":"e1a4a501bcb42f291f84e5d04c8f927821542fb6",
|
||||
"issue_id":469,
|
||||
"author_id":2,
|
||||
"actions":[
|
||||
{
|
||||
"design_id":38,
|
||||
"version_id":25,
|
||||
"event":1,
|
||||
"design":{
|
||||
"id":38,
|
||||
"project_id":30,
|
||||
"issue_id":469,
|
||||
"filename":"chirrido3.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"design_id":39,
|
||||
"version_id":25,
|
||||
"event":0,
|
||||
"design":{
|
||||
"id":39,
|
||||
"project_id":30,
|
||||
"issue_id":469,
|
||||
"filename":"jonathan_richman.jpg"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id":26,
|
||||
"sha":"27702d08f5ee021ae938737f84e8fe7c38599e85",
|
||||
"issue_id":469,
|
||||
"author_id":1,
|
||||
"actions":[
|
||||
{
|
||||
"design_id":38,
|
||||
"version_id":26,
|
||||
"event":1,
|
||||
"design":{
|
||||
"id":38,
|
||||
"project_id":30,
|
||||
"issue_id":469,
|
||||
"filename":"chirrido3.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"design_id":39,
|
||||
"version_id":26,
|
||||
"event":2,
|
||||
"design":{
|
||||
"id":39,
|
||||
"project_id":30,
|
||||
"issue_id":469,
|
||||
"filename":"jonathan_richman.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"design_id":40,
|
||||
"version_id":26,
|
||||
"event":0,
|
||||
"design":{
|
||||
"id":40,
|
||||
"project_id":30,
|
||||
"issue_id":469,
|
||||
"filename":"mariavontrap.jpeg"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id":470,
|
||||
"title":"issue 2",
|
||||
"author_id":1,
|
||||
"project_id":30,
|
||||
"created_at":"2019-08-07T04:15:57.607Z",
|
||||
"updated_at":"2019-08-07T04:15:57.607Z",
|
||||
"description":"",
|
||||
"state":"opened",
|
||||
"iid":2,
|
||||
"updated_by_id":null,
|
||||
"weight":null,
|
||||
"confidential":false,
|
||||
"due_date":null,
|
||||
"moved_to_id":null,
|
||||
"lock_version":0,
|
||||
"time_estimate":0,
|
||||
"relative_position":1073742823,
|
||||
"service_desk_reply_to":null,
|
||||
"last_edited_at":null,
|
||||
"last_edited_by_id":null,
|
||||
"discussion_locked":null,
|
||||
"closed_at":null,
|
||||
"closed_by_id":null,
|
||||
"state_id":1,
|
||||
"events":[
|
||||
{
|
||||
"id":1776,
|
||||
"project_id":30,
|
||||
"author_id":1,
|
||||
"target_id":470,
|
||||
"created_at":"2019-08-07T04:15:57.789Z",
|
||||
"updated_at":"2019-08-07T04:15:57.789Z",
|
||||
"target_type":"Issue",
|
||||
"action":1
|
||||
}
|
||||
],
|
||||
"timelogs":[
|
||||
|
||||
],
|
||||
"notes":[
|
||||
|
||||
],
|
||||
"label_links":[
|
||||
|
||||
],
|
||||
"resource_label_events":[
|
||||
|
||||
],
|
||||
"issue_assignees":[
|
||||
|
||||
],
|
||||
"designs":[
|
||||
{
|
||||
"id":42,
|
||||
"project_id":30,
|
||||
"issue_id":470,
|
||||
"filename":"1 (1).jpeg",
|
||||
"notes":[
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"id":43,
|
||||
"project_id":30,
|
||||
"issue_id":470,
|
||||
"filename":"2099743.jpg",
|
||||
"notes":[
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"id":44,
|
||||
"project_id":30,
|
||||
"issue_id":470,
|
||||
"filename":"a screenshot (1).jpg",
|
||||
"notes":[
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"id":41,
|
||||
"project_id":30,
|
||||
"issue_id":470,
|
||||
"filename":"chirrido3.jpg",
|
||||
"notes":[
|
||||
|
||||
]
|
||||
}
|
||||
],
|
||||
"design_versions":[
|
||||
{
|
||||
"id":27,
|
||||
"sha":"8587e78ab6bda3bc820a9f014c3be4a21ad4fcc8",
|
||||
"issue_id":470,
|
||||
"author_id":1,
|
||||
"actions":[
|
||||
{
|
||||
"design_id":41,
|
||||
"version_id":27,
|
||||
"event":0,
|
||||
"design":{
|
||||
"id":41,
|
||||
"project_id":30,
|
||||
"issue_id":470,
|
||||
"filename":"chirrido3.jpg"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id":28,
|
||||
"sha":"73f871b4c8c1d65c62c460635e023179fb53abc4",
|
||||
"issue_id":470,
|
||||
"author_id":2,
|
||||
"actions":[
|
||||
{
|
||||
"design_id":42,
|
||||
"version_id":28,
|
||||
"event":0,
|
||||
"design":{
|
||||
"id":42,
|
||||
"project_id":30,
|
||||
"issue_id":470,
|
||||
"filename":"1 (1).jpeg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"design_id":43,
|
||||
"version_id":28,
|
||||
"event":0,
|
||||
"design":{
|
||||
"id":43,
|
||||
"project_id":30,
|
||||
"issue_id":470,
|
||||
"filename":"2099743.jpg"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id":29,
|
||||
"sha":"c9b5f067f3e892122a4b12b0a25a8089192f3ac8",
|
||||
"issue_id":470,
|
||||
"author_id":2,
|
||||
"actions":[
|
||||
{
|
||||
"design_id":42,
|
||||
"version_id":29,
|
||||
"event":1,
|
||||
"design":{
|
||||
"id":42,
|
||||
"project_id":30,
|
||||
"issue_id":470,
|
||||
"filename":"1 (1).jpeg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"design_id":44,
|
||||
"version_id":29,
|
||||
"event":0,
|
||||
"design":{
|
||||
"id":44,
|
||||
"project_id":30,
|
||||
"issue_id":470,
|
||||
"filename":"a screenshot (1).jpg"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"snippets":[
|
||||
|
||||
],
|
||||
"releases":[
|
||||
|
||||
],
|
||||
"project_members":[
|
||||
{
|
||||
"id":95,
|
||||
"access_level":40,
|
||||
"source_id":30,
|
||||
"source_type":"Project",
|
||||
"user_id":1,
|
||||
"notification_level":3,
|
||||
"created_at":"2019-08-07T03:57:32.825Z",
|
||||
"updated_at":"2019-08-07T03:57:32.825Z",
|
||||
"created_by_id":1,
|
||||
"invite_email":null,
|
||||
"invite_token":null,
|
||||
"invite_accepted_at":null,
|
||||
"requested_at":null,
|
||||
"expires_at":null,
|
||||
"ldap":false,
|
||||
"override":false,
|
||||
"user":{
|
||||
"id":1,
|
||||
"email":"admin@example.com",
|
||||
"username":"root"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":96,
|
||||
"access_level":40,
|
||||
"source_id":30,
|
||||
"source_type":"Project",
|
||||
"user_id":2,
|
||||
"notification_level":3,
|
||||
"created_at":"2019-08-07T03:57:32.825Z",
|
||||
"updated_at":"2019-08-07T03:57:32.825Z",
|
||||
"created_by_id":null,
|
||||
"invite_email":null,
|
||||
"invite_token":null,
|
||||
"invite_accepted_at":null,
|
||||
"requested_at":null,
|
||||
"expires_at":null,
|
||||
"ldap":false,
|
||||
"override":false,
|
||||
"user":{
|
||||
"id":2,
|
||||
"email":"user_2@gitlabexample.com",
|
||||
"username":"user_2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"merge_requests":[
|
||||
|
||||
],
|
||||
"ci_pipelines":[
|
||||
|
||||
],
|
||||
"triggers":[
|
||||
|
||||
],
|
||||
"pipeline_schedules":[
|
||||
|
||||
],
|
||||
"services":[
|
||||
|
||||
],
|
||||
"protected_branches":[
|
||||
|
||||
],
|
||||
"protected_environments": [
|
||||
{
|
||||
"id": 14,
|
||||
"created_at": "2017-10-19T15:36:23.466Z",
|
||||
"updated_at": "2017-10-19T15:36:23.466Z",
|
||||
"name": "production",
|
||||
"deploy_access_levels": [
|
||||
{
|
||||
"id": 21,
|
||||
"created_at": "2017-10-19T15:36:23.466Z",
|
||||
"updated_at": "2017-10-19T15:36:23.466Z",
|
||||
"access_level": 40,
|
||||
"user_id": 1,
|
||||
"group_id": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"protected_tags":[
|
||||
|
||||
],
|
||||
"project_feature":{
|
||||
"id":30,
|
||||
"project_id":30,
|
||||
"merge_requests_access_level":20,
|
||||
"issues_access_level":20,
|
||||
"wiki_access_level":20,
|
||||
"snippets_access_level":20,
|
||||
"builds_access_level":20,
|
||||
"created_at":"2019-08-07T03:57:32.485Z",
|
||||
"updated_at":"2019-08-07T03:57:32.485Z",
|
||||
"repository_access_level":20,
|
||||
"pages_access_level":10
|
||||
},
|
||||
"custom_attributes":[
|
||||
|
||||
],
|
||||
"prometheus_metrics":[
|
||||
|
||||
],
|
||||
"project_badges":[
|
||||
|
||||
],
|
||||
"ci_cd_settings":{
|
||||
"group_runners_enabled":true
|
||||
},
|
||||
"boards":[
|
||||
|
||||
],
|
||||
"pipelines":[
|
||||
|
||||
]
|
||||
}
|
|
@ -6,7 +6,7 @@ describe Mutations::AlertManagement::UpdateAlertStatus do
|
|||
let_it_be(:current_user) { create(:user) }
|
||||
let_it_be(:alert) { create(:alert_management_alert, status: 'triggered') }
|
||||
let_it_be(:project) { alert.project }
|
||||
let(:new_status) { 'acknowledged' }
|
||||
let(:new_status) { Types::AlertManagement::StatusEnum.values['ACKNOWLEDGED'].value }
|
||||
let(:args) { { status: new_status, project_path: project.full_path, iid: alert.iid } }
|
||||
|
||||
specify { expect(described_class).to require_graphql_authorizations(:update_alert_management_alert) }
|
||||
|
|
19
spec/lib/api/entities/design_management/design_spec.rb
Normal file
19
spec/lib/api/entities/design_management/design_spec.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe API::Entities::DesignManagement::Design do
|
||||
let_it_be(:design) { create(:design) }
|
||||
let(:entity) { described_class.new(design, request: double) }
|
||||
|
||||
subject { entity.as_json }
|
||||
|
||||
it 'has the correct attributes' do
|
||||
expect(subject).to eq({
|
||||
id: design.id,
|
||||
project_id: design.project_id,
|
||||
filename: design.filename,
|
||||
image_url: ::Gitlab::UrlBuilder.build(design)
|
||||
})
|
||||
end
|
||||
end
|
|
@ -4,6 +4,7 @@ require 'spec_helper'
|
|||
|
||||
describe Banzai::Filter::IssueReferenceFilter do
|
||||
include FilterSpecHelper
|
||||
include DesignManagementTestHelpers
|
||||
|
||||
def helper
|
||||
IssuesHelper
|
||||
|
@ -358,6 +359,23 @@ describe Banzai::Filter::IssueReferenceFilter do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when processing a link to the designs tab' do
|
||||
let(:designs_tab_url) { url_for_designs(issue) }
|
||||
let(:input_text) { "See #{designs_tab_url}" }
|
||||
|
||||
subject(:link) { reference_filter(input_text).css('a').first }
|
||||
|
||||
before do
|
||||
enable_design_management
|
||||
end
|
||||
|
||||
it 'includes the word "designs" after the reference in the text content', :aggregate_failures do
|
||||
expect(link.attr('title')).to eq(issue.title)
|
||||
expect(link.attr('href')).to eq(designs_tab_url)
|
||||
expect(link.text).to eq("#{issue.to_reference} (designs)")
|
||||
end
|
||||
end
|
||||
|
||||
context 'group context' do
|
||||
let(:group) { create(:group) }
|
||||
let(:context) { { project: nil, group: group } }
|
||||
|
@ -467,4 +485,41 @@ describe Banzai::Filter::IssueReferenceFilter do
|
|||
end.not_to yield_control
|
||||
end
|
||||
end
|
||||
|
||||
describe '#object_link_text_extras' do
|
||||
before do
|
||||
enable_design_management(enabled)
|
||||
end
|
||||
|
||||
let(:current_user) { project.owner }
|
||||
let(:enabled) { true }
|
||||
let(:matches) { Issue.link_reference_pattern.match(input_text) }
|
||||
let(:extras) { subject.object_link_text_extras(issue, matches) }
|
||||
|
||||
subject { filter_instance }
|
||||
|
||||
context 'the link does not go to the designs tab' do
|
||||
let(:input_text) { Gitlab::Routing.url_helpers.project_issue_url(issue.project, issue) }
|
||||
|
||||
it 'does not include designs' do
|
||||
expect(extras).not_to include('designs')
|
||||
end
|
||||
end
|
||||
|
||||
context 'the link goes to the designs tab' do
|
||||
let(:input_text) { url_for_designs(issue) }
|
||||
|
||||
it 'includes designs' do
|
||||
expect(extras).to include('designs')
|
||||
end
|
||||
|
||||
context 'design management is disabled' do
|
||||
let(:enabled) { false }
|
||||
|
||||
it 'does not include designs in the extras' do
|
||||
expect(extras).not_to include('designs')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
91
spec/lib/banzai/reference_parser/design_parser_spec.rb
Normal file
91
spec/lib/banzai/reference_parser/design_parser_spec.rb
Normal file
|
@ -0,0 +1,91 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Banzai::ReferenceParser::DesignParser do
|
||||
include ReferenceParserHelpers
|
||||
include DesignManagementTestHelpers
|
||||
|
||||
let_it_be(:issue) { create(:issue) }
|
||||
let_it_be(:design) { create(:design, :with_versions, issue: issue) }
|
||||
let_it_be(:user) { create(:user, developer_projects: [issue.project]) }
|
||||
|
||||
subject(:instance) { described_class.new(Banzai::RenderContext.new(issue.project, user)) }
|
||||
|
||||
let(:link) { design_link(design) }
|
||||
|
||||
before do
|
||||
enable_design_management
|
||||
end
|
||||
|
||||
describe '#nodes_visible_to_user' do
|
||||
it_behaves_like 'referenced feature visibility', 'issues' do
|
||||
let(:project) { issue.project }
|
||||
end
|
||||
|
||||
describe 'specific states' do
|
||||
let_it_be(:public_project) { create(:project, :public) }
|
||||
|
||||
let_it_be(:other_project_link) do
|
||||
design_link(create(:design, :with_versions))
|
||||
end
|
||||
let_it_be(:public_link) do
|
||||
design_link(create(:design, :with_versions, issue: create(:issue, project: public_project)))
|
||||
end
|
||||
let_it_be(:public_but_confidential_link) do
|
||||
design_link(create(:design, :with_versions, issue: create(:issue, :confidential, project: public_project)))
|
||||
end
|
||||
|
||||
subject(:visible_nodes) do
|
||||
nodes = [link,
|
||||
other_project_link,
|
||||
public_link,
|
||||
public_but_confidential_link]
|
||||
|
||||
instance.nodes_visible_to_user(user, nodes)
|
||||
end
|
||||
|
||||
it 'redacts links we should not have access to' do
|
||||
expect(visible_nodes).to contain_exactly(link, public_link)
|
||||
end
|
||||
|
||||
context 'design management is not available' do
|
||||
before do
|
||||
enable_design_management(false)
|
||||
end
|
||||
|
||||
it 'redacts all nodes' do
|
||||
expect(visible_nodes).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#process' do
|
||||
it 'returns the correct designs' do
|
||||
frag = document([design, create(:design, :with_versions)])
|
||||
|
||||
expect(subject.process([frag])[:visible]).to contain_exactly(design)
|
||||
end
|
||||
end
|
||||
|
||||
def design_link(design)
|
||||
node = empty_html_link
|
||||
node['class'] = 'gfm'
|
||||
node['data-reference-type'] = 'design'
|
||||
node['data-project'] = design.project.id.to_s
|
||||
node['data-issue'] = design.issue.id.to_s
|
||||
node['data-design'] = design.id.to_s
|
||||
|
||||
node
|
||||
end
|
||||
|
||||
def document(designs)
|
||||
frag = Nokogiri::HTML.fragment('')
|
||||
designs.each do |design|
|
||||
frag.add_child(design_link(design))
|
||||
end
|
||||
|
||||
frag
|
||||
end
|
||||
end
|
42
spec/lib/gitlab/analytics/cycle_analytics/median_spec.rb
Normal file
42
spec/lib/gitlab/analytics/cycle_analytics/median_spec.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Analytics::CycleAnalytics::Median do
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let(:query) { Project.joins(merge_requests: :metrics) }
|
||||
|
||||
let(:stage) do
|
||||
build(
|
||||
:cycle_analytics_project_stage,
|
||||
start_event_identifier: Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestCreated.identifier,
|
||||
end_event_identifier: Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestMerged.identifier,
|
||||
project: project
|
||||
)
|
||||
end
|
||||
|
||||
subject { described_class.new(stage: stage, query: query).seconds }
|
||||
|
||||
around do |example|
|
||||
Timecop.freeze { example.run }
|
||||
end
|
||||
|
||||
it 'retruns nil when no results' do
|
||||
expect(subject).to eq(nil)
|
||||
end
|
||||
|
||||
it 'returns median duration seconds as float' do
|
||||
merge_request1 = create(:merge_request, source_branch: '1', target_project: project, source_project: project)
|
||||
merge_request2 = create(:merge_request, source_branch: '2', target_project: project, source_project: project)
|
||||
|
||||
Timecop.travel(5.minutes.from_now) do
|
||||
merge_request1.metrics.update!(merged_at: Time.zone.now)
|
||||
end
|
||||
|
||||
Timecop.travel(10.minutes.from_now) do
|
||||
merge_request2.metrics.update!(merged_at: Time.zone.now)
|
||||
end
|
||||
|
||||
expect(subject).to be_within(0.5).of(7.5.minutes.seconds)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::GrapeLogging::Loggers::CloudflareLogger do
|
||||
subject { described_class.new }
|
||||
|
||||
describe "#parameters" do
|
||||
let(:mock_request) { ActionDispatch::Request.new({}) }
|
||||
let(:start_time) { Time.new(2018, 01, 01) }
|
||||
|
||||
describe 'with no Cloudflare headers' do
|
||||
it 'returns an empty hash' do
|
||||
expect(subject.parameters(mock_request, nil)).to eq({})
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with Cloudflare headers' do
|
||||
before do
|
||||
mock_request.headers['Cf-Ray'] = SecureRandom.hex
|
||||
mock_request.headers['Cf-Request-Id'] = SecureRandom.hex
|
||||
end
|
||||
|
||||
it 'returns the correct duration in seconds' do
|
||||
data = subject.parameters(mock_request, nil)
|
||||
|
||||
expect(data.keys).to contain_exactly(:cf_ray, :cf_request_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
42
spec/lib/gitlab/import_export/design_repo_restorer_spec.rb
Normal file
42
spec/lib/gitlab/import_export/design_repo_restorer_spec.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::ImportExport::DesignRepoRestorer do
|
||||
include GitHelpers
|
||||
|
||||
describe 'bundle a design Git repo' do
|
||||
let(:user) { create(:user) }
|
||||
let!(:project_with_design_repo) { create(:project, :design_repo) }
|
||||
let!(:project) { create(:project) }
|
||||
let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
|
||||
let(:shared) { project.import_export_shared }
|
||||
let(:bundler) { Gitlab::ImportExport::DesignRepoSaver.new(project: project_with_design_repo, shared: shared) }
|
||||
let(:bundle_path) { File.join(shared.export_path, Gitlab::ImportExport.design_repo_bundle_filename) }
|
||||
let(:restorer) do
|
||||
described_class.new(path_to_bundle: bundle_path,
|
||||
shared: shared,
|
||||
project: project)
|
||||
end
|
||||
|
||||
before do
|
||||
allow_next_instance_of(Gitlab::ImportExport) do |instance|
|
||||
allow(instance).to receive(:storage_path).and_return(export_path)
|
||||
end
|
||||
|
||||
bundler.save
|
||||
end
|
||||
|
||||
after do
|
||||
FileUtils.rm_rf(export_path)
|
||||
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
|
||||
FileUtils.rm_rf(project_with_design_repo.design_repository.path_to_repo)
|
||||
FileUtils.rm_rf(project.design_repository.path_to_repo)
|
||||
end
|
||||
end
|
||||
|
||||
it 'restores the repo successfully' do
|
||||
expect(restorer.restore).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
37
spec/lib/gitlab/import_export/design_repo_saver_spec.rb
Normal file
37
spec/lib/gitlab/import_export/design_repo_saver_spec.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::ImportExport::DesignRepoSaver do
|
||||
describe 'bundle a design Git repo' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:design) { create(:design, :with_file, versions_count: 1) }
|
||||
let!(:project) { create(:project, :design_repo) }
|
||||
let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
|
||||
let(:shared) { project.import_export_shared }
|
||||
let(:design_bundler) { described_class.new(project: project, shared: shared) }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
allow_next_instance_of(Gitlab::ImportExport) do |instance|
|
||||
allow(instance).to receive(:storage_path).and_return(export_path)
|
||||
end
|
||||
end
|
||||
|
||||
after do
|
||||
FileUtils.rm_rf(export_path)
|
||||
end
|
||||
|
||||
it 'bundles the repo successfully' do
|
||||
expect(design_bundler.save).to be true
|
||||
end
|
||||
|
||||
context 'when the repo is empty' do
|
||||
let!(:project) { create(:project) }
|
||||
|
||||
it 'bundles the repo successfully' do
|
||||
expect(design_bundler.save).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -53,22 +53,15 @@ describe 'Test coverage of the Project Import' do
|
|||
].freeze
|
||||
|
||||
# A list of JSON fixture files we use to test Import.
|
||||
# Note that we use separate fixture to test ee-only features.
|
||||
# Most of the relations are present in `complex/project.json`
|
||||
# which is our main fixture.
|
||||
PROJECT_JSON_FIXTURES_EE =
|
||||
if Gitlab.ee?
|
||||
['ee/spec/fixtures/lib/gitlab/import_export/designs/project.json'].freeze
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
PROJECT_JSON_FIXTURES = [
|
||||
'spec/fixtures/lib/gitlab/import_export/complex/project.json',
|
||||
'spec/fixtures/lib/gitlab/import_export/group/project.json',
|
||||
'spec/fixtures/lib/gitlab/import_export/light/project.json',
|
||||
'spec/fixtures/lib/gitlab/import_export/milestone-iid/project.json'
|
||||
].freeze + PROJECT_JSON_FIXTURES_EE
|
||||
'spec/fixtures/lib/gitlab/import_export/milestone-iid/project.json',
|
||||
'spec/fixtures/lib/gitlab/import_export/designs/project.json'
|
||||
].freeze
|
||||
|
||||
it 'ensures that all imported/exported relations are present in test JSONs' do
|
||||
not_tested_relations = (relations_from_config - tested_relations) - MUTED_RELATIONS
|
||||
|
|
|
@ -51,7 +51,8 @@ describe Gitlab::ImportExport::Importer do
|
|||
Gitlab::ImportExport::UploadsRestorer,
|
||||
Gitlab::ImportExport::LfsRestorer,
|
||||
Gitlab::ImportExport::StatisticsRestorer,
|
||||
Gitlab::ImportExport::SnippetsRepoRestorer
|
||||
Gitlab::ImportExport::SnippetsRepoRestorer,
|
||||
Gitlab::ImportExport::DesignRepoRestorer
|
||||
].each do |restorer|
|
||||
it "calls the #{restorer}" do
|
||||
fake_restorer = double(restorer.to_s)
|
||||
|
|
|
@ -8,6 +8,7 @@ end
|
|||
|
||||
describe Gitlab::ImportExport::Project::TreeRestorer do
|
||||
include ImportExport::CommonUtil
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let(:shared) { project.import_export_shared }
|
||||
|
||||
|
@ -987,6 +988,69 @@ describe Gitlab::ImportExport::Project::TreeRestorer do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'JSON with design management data' do
|
||||
let_it_be(:user) { create(:admin, email: 'user_1@gitlabexample.com') }
|
||||
let_it_be(:second_user) { create(:user, email: 'user_2@gitlabexample.com') }
|
||||
let_it_be(:project) do
|
||||
create(:project, :builds_disabled, :issues_disabled,
|
||||
{ name: 'project', path: 'project' })
|
||||
end
|
||||
let(:shared) { project.import_export_shared }
|
||||
let(:project_tree_restorer) { described_class.new(user: user, shared: shared, project: project) }
|
||||
|
||||
subject(:restored_project_json) { project_tree_restorer.restore }
|
||||
|
||||
before do
|
||||
setup_import_export_config('designs')
|
||||
restored_project_json
|
||||
end
|
||||
|
||||
it_behaves_like 'restores project successfully', issues: 2
|
||||
|
||||
it 'restores project associations correctly' do
|
||||
expect(project.designs.size).to eq(7)
|
||||
end
|
||||
|
||||
describe 'restores issue associations correctly' do
|
||||
let(:issue) { project.issues.offset(index).first }
|
||||
|
||||
where(:index, :design_filenames, :version_shas, :events, :author_emails) do
|
||||
0 | %w[chirrido3.jpg jonathan_richman.jpg mariavontrap.jpeg] | %w[27702d08f5ee021ae938737f84e8fe7c38599e85 9358d1bac8ff300d3d2597adaa2572a20f7f8703 e1a4a501bcb42f291f84e5d04c8f927821542fb6] | %w[creation creation creation modification modification deletion] | %w[user_1@gitlabexample.com user_1@gitlabexample.com user_2@gitlabexample.com]
|
||||
1 | ['1 (1).jpeg', '2099743.jpg', 'a screenshot (1).jpg', 'chirrido3.jpg'] | %w[73f871b4c8c1d65c62c460635e023179fb53abc4 8587e78ab6bda3bc820a9f014c3be4a21ad4fcc8 c9b5f067f3e892122a4b12b0a25a8089192f3ac8] | %w[creation creation creation creation modification] | %w[user_1@gitlabexample.com user_2@gitlabexample.com user_2@gitlabexample.com]
|
||||
end
|
||||
|
||||
with_them do
|
||||
it do
|
||||
expect(issue.designs.pluck(:filename)).to contain_exactly(*design_filenames)
|
||||
expect(issue.design_versions.pluck(:sha)).to contain_exactly(*version_shas)
|
||||
expect(issue.design_versions.flat_map(&:actions).map(&:event)).to contain_exactly(*events)
|
||||
expect(issue.design_versions.map(&:author).map(&:email)).to contain_exactly(*author_emails)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'restores design version associations correctly' do
|
||||
let(:project_designs) { project.designs.reorder(:filename, :issue_id) }
|
||||
let(:design) { project_designs.offset(index).first }
|
||||
|
||||
where(:index, :version_shas) do
|
||||
0 | %w[73f871b4c8c1d65c62c460635e023179fb53abc4 c9b5f067f3e892122a4b12b0a25a8089192f3ac8]
|
||||
1 | %w[73f871b4c8c1d65c62c460635e023179fb53abc4]
|
||||
2 | %w[c9b5f067f3e892122a4b12b0a25a8089192f3ac8]
|
||||
3 | %w[27702d08f5ee021ae938737f84e8fe7c38599e85 9358d1bac8ff300d3d2597adaa2572a20f7f8703 e1a4a501bcb42f291f84e5d04c8f927821542fb6]
|
||||
4 | %w[8587e78ab6bda3bc820a9f014c3be4a21ad4fcc8]
|
||||
5 | %w[27702d08f5ee021ae938737f84e8fe7c38599e85 e1a4a501bcb42f291f84e5d04c8f927821542fb6]
|
||||
6 | %w[27702d08f5ee021ae938737f84e8fe7c38599e85]
|
||||
end
|
||||
|
||||
with_them do
|
||||
it do
|
||||
expect(design.versions.pluck(:sha)).to contain_exactly(*version_shas)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'enable ndjson import' do
|
||||
|
|
|
@ -168,6 +168,28 @@ describe Gitlab::ImportExport::Project::TreeSaver do
|
|||
it 'has issue resource label events' do
|
||||
expect(subject.first['resource_label_events']).not_to be_empty
|
||||
end
|
||||
|
||||
it 'saves the issue designs correctly' do
|
||||
expect(subject.first['designs'].size).to eq(1)
|
||||
end
|
||||
|
||||
it 'saves the issue design notes correctly' do
|
||||
expect(subject.first['designs'].first['notes']).not_to be_empty
|
||||
end
|
||||
|
||||
it 'saves the issue design versions correctly' do
|
||||
issue_json = subject.first
|
||||
actions = issue_json['design_versions'].flat_map { |v| v['actions'] }
|
||||
|
||||
expect(issue_json['design_versions'].size).to eq(2)
|
||||
issue_json['design_versions'].each do |version|
|
||||
expect(version['author_id']).to be_kind_of(Integer)
|
||||
end
|
||||
expect(actions.size).to eq(2)
|
||||
actions.each do |action|
|
||||
expect(action['design']).to be_present
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with ci_pipelines' do
|
||||
|
@ -442,6 +464,9 @@ describe Gitlab::ImportExport::Project::TreeSaver do
|
|||
board = create(:board, project: project, name: 'TestBoard')
|
||||
create(:list, board: board, position: 0, label: project_label)
|
||||
|
||||
design = create(:design, :with_file, versions_count: 2, issue: issue)
|
||||
create(:diff_note_on_design, noteable: design, project: project, author: user)
|
||||
|
||||
project
|
||||
end
|
||||
end
|
||||
|
|
52
spec/lib/gitlab/logging/cloudflare_helper_spec.rb
Normal file
52
spec/lib/gitlab/logging/cloudflare_helper_spec.rb
Normal file
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Logging::CloudflareHelper do
|
||||
let(:helper) do
|
||||
Class.new do
|
||||
include Gitlab::Logging::CloudflareHelper
|
||||
end.new
|
||||
end
|
||||
|
||||
describe '#store_cloudflare_headers!' do
|
||||
let(:payload) { {} }
|
||||
let(:env) { {} }
|
||||
let(:request) { ActionDispatch::Request.new(env) }
|
||||
|
||||
before do
|
||||
request.headers.merge!(headers)
|
||||
end
|
||||
|
||||
context 'with normal headers' do
|
||||
let(:headers) { { 'Cf-Ray' => SecureRandom.hex, 'Cf-Request-Id' => SecureRandom.hex } }
|
||||
|
||||
it 'adds Cf-Ray-Id and Cf-Request-Id' do
|
||||
helper.store_cloudflare_headers!(payload, request)
|
||||
|
||||
expect(payload[:cf_ray]).to eq(headers['Cf-Ray'])
|
||||
expect(payload[:cf_request_id]).to eq(headers['Cf-Request-Id'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with header values with long strings' do
|
||||
let(:headers) { { 'Cf-Ray' => SecureRandom.hex(33), 'Cf-Request-Id' => SecureRandom.hex(33) } }
|
||||
|
||||
it 'filters invalid header values' do
|
||||
helper.store_cloudflare_headers!(payload, request)
|
||||
|
||||
expect(payload.keys).not_to include(:cf_ray, :cf_request_id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with header values with non-alphanumeric characters' do
|
||||
let(:headers) { { 'Cf-Ray' => "Bad\u0000ray", 'Cf-Request-Id' => "Bad\u0000req" } }
|
||||
|
||||
it 'filters invalid header values' do
|
||||
helper.store_cloudflare_headers!(payload, request)
|
||||
|
||||
expect(payload.keys).not_to include(:cf_ray, :cf_request_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -19,7 +19,12 @@ describe Gitlab::Lograge::CustomOptions do
|
|||
1,
|
||||
2,
|
||||
'transaction_id',
|
||||
{ params: params, user_id: 'test' }
|
||||
{
|
||||
params: params,
|
||||
user_id: 'test',
|
||||
cf_ray: SecureRandom.hex,
|
||||
cf_request_id: SecureRandom.hex
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -46,5 +51,10 @@ describe Gitlab::Lograge::CustomOptions do
|
|||
it 'adds the user id' do
|
||||
expect(subject[:user_id]).to eq('test')
|
||||
end
|
||||
|
||||
it 'adds Cloudflare headers' do
|
||||
expect(subject[:cf_ray]).to eq(event.payload[:cf_ray])
|
||||
expect(subject[:cf_request_id]).to eq(event.payload[:cf_request_id])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,6 +25,7 @@ describe Gitlab::UrlBuilder do
|
|||
:project_snippet | ->(snippet) { "/#{snippet.project.full_path}/snippets/#{snippet.id}" }
|
||||
:project_wiki | ->(wiki) { "/#{wiki.container.full_path}/-/wikis/home" }
|
||||
:ci_build | ->(build) { "/#{build.project.full_path}/-/jobs/#{build.id}" }
|
||||
:design | ->(design) { "/#{design.project.full_path}/-/design_management/designs/#{design.id}/raw_image" }
|
||||
|
||||
:group | ->(group) { "/groups/#{group.full_path}" }
|
||||
:group_milestone | ->(milestone) { "/groups/#{milestone.group.full_path}/-/milestones/#{milestone.iid}" }
|
||||
|
@ -95,6 +96,16 @@ describe Gitlab::UrlBuilder do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when passing a DesignManagement::Design' do
|
||||
let(:design) { build_stubbed(:design) }
|
||||
|
||||
it 'uses the given ref and size in the URL' do
|
||||
url = subject.build(design, ref: 'feature', size: 'small')
|
||||
|
||||
expect(url).to eq "#{Settings.gitlab['url']}/#{design.project.full_path}/-/design_management/designs/#{design.id}/feature/resized_image/small"
|
||||
end
|
||||
end
|
||||
|
||||
context 'when passing an unsupported class' do
|
||||
let(:object) { Object.new }
|
||||
|
||||
|
|
14
spec/lib/gitlab/usage_data_counters/designs_counter_spec.rb
Normal file
14
spec/lib/gitlab/usage_data_counters/designs_counter_spec.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::UsageDataCounters::DesignsCounter do
|
||||
it_behaves_like 'a redis usage counter', 'Designs', :create
|
||||
it_behaves_like 'a redis usage counter', 'Designs', :update
|
||||
it_behaves_like 'a redis usage counter', 'Designs', :delete
|
||||
|
||||
it_behaves_like 'a redis usage counter with totals', :design_management_designs,
|
||||
create: 5,
|
||||
update: 3,
|
||||
delete: 2
|
||||
end
|
|
@ -712,6 +712,29 @@ describe Notify do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'for design notes' do
|
||||
let_it_be(:design) { create(:design, :with_file) }
|
||||
let_it_be(:recipient) { create(:user) }
|
||||
let_it_be(:note) do
|
||||
create(:diff_note_on_design,
|
||||
noteable: design,
|
||||
note: "Hello #{recipient.to_reference}")
|
||||
end
|
||||
|
||||
let(:header_name) { 'X-Gitlab-DesignManagement-Design-ID' }
|
||||
let(:refer_to_design) do
|
||||
have_attributes(subject: a_string_including(design.filename))
|
||||
end
|
||||
|
||||
subject { described_class.note_design_email(recipient.id, note.id) }
|
||||
|
||||
it { is_expected.to have_header(header_name, design.id.to_s) }
|
||||
|
||||
it { is_expected.to have_body_text(design.filename) }
|
||||
|
||||
it { is_expected.to refer_to_design }
|
||||
end
|
||||
|
||||
describe 'project was moved' do
|
||||
let(:recipient) { user }
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ describe API::Branches do
|
|||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
project.repository.add_branch(user, 'ends-with.txt', branch_sha)
|
||||
end
|
||||
|
||||
describe "GET /projects/:id/repository/branches" do
|
||||
|
@ -240,6 +241,12 @@ describe API::Branches do
|
|||
it_behaves_like 'repository branch'
|
||||
end
|
||||
|
||||
context 'when branch contains dot txt' do
|
||||
let(:branch_name) { project.repository.find_branch('ends-with.txt').name }
|
||||
|
||||
it_behaves_like 'repository branch'
|
||||
end
|
||||
|
||||
context 'when branch contains a slash' do
|
||||
let(:branch_name) { branch_with_slash.name }
|
||||
|
||||
|
|
|
@ -159,6 +159,46 @@ describe API::Todos do
|
|||
expect { get api('/todos', john_doe) }.not_to exceed_query_limit(control)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
|
||||
context 'when there is a Design Todo' do
|
||||
let!(:design_todo) { create_todo_for_mentioned_in_design }
|
||||
|
||||
def create_todo_for_mentioned_in_design
|
||||
issue = create(:issue, project: project_1)
|
||||
create(:todo, :mentioned,
|
||||
user: john_doe,
|
||||
project: project_1,
|
||||
target: create(:design, issue: issue),
|
||||
author: create(:user),
|
||||
note: create(:note, project: project_1, note: "I am note, hear me roar"))
|
||||
end
|
||||
|
||||
def api_request
|
||||
get api('/todos', john_doe)
|
||||
end
|
||||
|
||||
before do
|
||||
api_request
|
||||
end
|
||||
|
||||
specify do
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
|
||||
it 'avoids N+1 queries', :request_store do
|
||||
control = ActiveRecord::QueryRecorder.new { api_request }
|
||||
|
||||
create_todo_for_mentioned_in_design
|
||||
|
||||
expect { api_request }.not_to exceed_query_limit(control)
|
||||
end
|
||||
|
||||
it 'includes the Design Todo in the response' do
|
||||
expect(json_response).to include(
|
||||
a_hash_including('id' => design_todo.id)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /todos/:id/mark_as_done' do
|
||||
|
|
|
@ -8,7 +8,7 @@ describe AlertManagement::UpdateAlertStatusService do
|
|||
describe '#execute' do
|
||||
subject(:execute) { described_class.new(alert, new_status).execute }
|
||||
|
||||
let(:new_status) { 'acknowledged' }
|
||||
let(:new_status) { Types::AlertManagement::StatusEnum.values['ACKNOWLEDGED'].value }
|
||||
|
||||
it 'updates the status' do
|
||||
expect { execute }.to change { alert.acknowledged? }.to(true)
|
||||
|
|
|
@ -46,8 +46,8 @@ describe Projects::ImportExport::ExportService do
|
|||
# in the corresponding EE spec.
|
||||
skip if Gitlab.ee?
|
||||
|
||||
# once for the normal repo, once for the wiki
|
||||
expect(Gitlab::ImportExport::RepoSaver).to receive(:new).twice.and_call_original
|
||||
# once for the normal repo, once for the wiki repo, and once for the design repo
|
||||
expect(Gitlab::ImportExport::RepoSaver).to receive(:new).exactly(3).times.and_call_original
|
||||
|
||||
service.execute
|
||||
end
|
||||
|
@ -58,6 +58,12 @@ describe Projects::ImportExport::ExportService do
|
|||
service.execute
|
||||
end
|
||||
|
||||
it 'saves the design repo' do
|
||||
expect(Gitlab::ImportExport::DesignRepoSaver).to receive(:new).and_call_original
|
||||
|
||||
service.execute
|
||||
end
|
||||
|
||||
it 'saves the lfs objects' do
|
||||
expect(Gitlab::ImportExport::LfsSaver).to receive(:new).and_call_original
|
||||
|
||||
|
|
|
@ -19,7 +19,9 @@ module ActiveRecord
|
|||
|
||||
def show_backtrace(values)
|
||||
Rails.logger.debug("QueryRecorder SQL: #{values[:sql]}")
|
||||
Gitlab::BacktraceCleaner.clean_backtrace(caller).each { |line| Rails.logger.debug(" --> #{line}") }
|
||||
Gitlab::BacktraceCleaner.clean_backtrace(caller).each do |line|
|
||||
Rails.logger.debug("QueryRecorder backtrace: --> #{line}")
|
||||
end
|
||||
end
|
||||
|
||||
def get_sql_source(sql)
|
||||
|
|
|
@ -19,6 +19,9 @@ module UsageDataHelpers
|
|||
cycle_analytics_views
|
||||
productivity_analytics_views
|
||||
source_code_pushes
|
||||
design_management_designs_create
|
||||
design_management_designs_update
|
||||
design_management_designs_delete
|
||||
).freeze
|
||||
|
||||
COUNTS_KEYS = %i(
|
||||
|
|
|
@ -63,7 +63,7 @@ shared_examples 'Gitlab::Analytics::CycleAnalytics::DataCollector backend exampl
|
|||
|
||||
context 'provides the same results as the old implementation' do
|
||||
it 'for the median' do
|
||||
expect(data_collector.median.seconds).to eq(ISSUES_MEDIAN)
|
||||
expect(data_collector.median.seconds).to be_within(0.5).of(ISSUES_MEDIAN)
|
||||
end
|
||||
|
||||
it 'for the list of event records' do
|
||||
|
|
94
spec/workers/design_management/new_version_worker_spec.rb
Normal file
94
spec/workers/design_management/new_version_worker_spec.rb
Normal file
|
@ -0,0 +1,94 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe DesignManagement::NewVersionWorker do
|
||||
# TODO a number of these tests are being temporarily skipped unless run in EE,
|
||||
# as we are in the process of moving Design Management to FOSS in 13.0
|
||||
# in steps. In the current step the services have not yet been moved, and
|
||||
# certain services are called within these tests:
|
||||
# - `SystemNoteService`
|
||||
# - `DesignManagement::GenerateImageVersionsService`
|
||||
#
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283.
|
||||
describe '#perform' do
|
||||
let(:worker) { described_class.new }
|
||||
|
||||
context 'the id is wrong or out-of-date' do
|
||||
let(:version_id) { -1 }
|
||||
|
||||
it 'does not create system notes' do
|
||||
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
|
||||
|
||||
expect(SystemNoteService).not_to receive(:design_version_added)
|
||||
|
||||
worker.perform(version_id)
|
||||
end
|
||||
|
||||
it 'does not invoke GenerateImageVersionsService' do
|
||||
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
|
||||
|
||||
expect(DesignManagement::GenerateImageVersionsService).not_to receive(:new)
|
||||
|
||||
worker.perform(version_id)
|
||||
end
|
||||
|
||||
it 'logs the reason for this failure' do
|
||||
expect(Sidekiq.logger).to receive(:warn)
|
||||
.with(an_instance_of(ActiveRecord::RecordNotFound))
|
||||
|
||||
worker.perform(version_id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'the version id is valid' do
|
||||
let_it_be(:version) { create(:design_version, :with_lfs_file, designs_count: 2) }
|
||||
|
||||
it 'creates a system note' do
|
||||
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
|
||||
|
||||
expect { worker.perform(version.id) }.to change { Note.system.count }.by(1)
|
||||
end
|
||||
|
||||
it 'invokes GenerateImageVersionsService' do
|
||||
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
|
||||
|
||||
expect_next_instance_of(DesignManagement::GenerateImageVersionsService) do |service|
|
||||
expect(service).to receive(:execute)
|
||||
end
|
||||
|
||||
worker.perform(version.id)
|
||||
end
|
||||
|
||||
it 'does not log anything' do
|
||||
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
|
||||
|
||||
expect(Sidekiq.logger).not_to receive(:warn)
|
||||
|
||||
worker.perform(version.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'the version includes multiple types of action' do
|
||||
let_it_be(:version) do
|
||||
create(:design_version, :with_lfs_file,
|
||||
created_designs: create_list(:design, 1, :with_lfs_file),
|
||||
modified_designs: create_list(:design, 1))
|
||||
end
|
||||
|
||||
it 'creates two system notes' do
|
||||
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
|
||||
|
||||
expect { worker.perform(version.id) }.to change { Note.system.count }.by(2)
|
||||
end
|
||||
|
||||
it 'calls design_version_added' do
|
||||
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
|
||||
|
||||
expect(SystemNoteService).to receive(:design_version_added).with(version)
|
||||
|
||||
worker.perform(version.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue