Commit f44e8aac authored by Valery Sizov's avatar Valery Sizov

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ee into ce-to-ee-tue

parents d68babd4 57ef7bca
......@@ -528,6 +528,8 @@
}
.comments-disabled-notif {
line-height: 28px;
.btn {
margin-left: 5px;
}
......
......@@ -61,7 +61,6 @@ class Projects::CompareController < Projects::ApplicationController
@environment = EnvironmentsFinder.new(@project, current_user, environment_params).execute.last
@diff_notes_disabled = true
@grouped_diff_discussions = {}
end
end
......
......@@ -18,7 +18,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_action :define_show_vars, only: [:show, :diffs, :commits, :conflicts, :conflict_for_path, :builds, :pipelines]
before_action :define_widget_vars, only: [:merge, :cancel_merge_when_pipeline_succeeds, :merge_check]
before_action :define_commit_vars, only: [:diffs]
before_action :define_diff_comment_vars, only: [:diffs]
before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds, :conflicts, :conflict_for_path, :pipelines]
before_action :close_merge_request_without_source_project, only: [:show, :diffs, :commits, :builds, :pipelines]
before_action :apply_diff_view_cookie!, only: [:new_diffs]
......@@ -104,34 +103,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
respond_to do |format|
format.html { define_discussion_vars }
format.json do
@merge_request_diff =
if params[:diff_id]
@merge_request.merge_request_diffs.viewable.find(params[:diff_id])
else
@merge_request.merge_request_diff
end
@merge_request_diffs = @merge_request.merge_request_diffs.viewable.select_without_diff
@comparable_diffs = @merge_request_diffs.select { |diff| diff.id < @merge_request_diff.id }
if params[:start_sha].present?
@start_sha = params[:start_sha]
@start_version = @comparable_diffs.find { |diff| diff.head_commit_sha == @start_sha }
unless @start_version
@start_sha = @merge_request_diff.head_commit_sha
@start_version = @merge_request_diff
end
end
define_diff_vars
define_diff_comment_vars
@environment = @merge_request.environments_for(current_user).last
if @start_sha
compared_diff_version
else
original_diff_version
end
render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") }
end
end
......@@ -143,16 +119,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def diff_for_path
if params[:id]
merge_request
define_diff_vars
define_diff_comment_vars
else
build_merge_request
@diffs = @merge_request.diffs(diff_options)
@diff_notes_disabled = true
@grouped_diff_discussions = {}
end
define_commit_vars
render_diff_for_path(@merge_request.diffs(diff_options))
render_diff_for_path(@diffs)
end
def commits
......@@ -647,15 +624,46 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@base_commit = @merge_request.diff_base_commit || @merge_request.likely_diff_base_commit
end
def define_diff_vars
@merge_request_diff =
if params[:diff_id]
@merge_request.merge_request_diffs.viewable.find(params[:diff_id])
else
@merge_request.merge_request_diff
end
@merge_request_diffs = @merge_request.merge_request_diffs.viewable.select_without_diff
@comparable_diffs = @merge_request_diffs.select { |diff| diff.id < @merge_request_diff.id }
if params[:start_sha].present?
@start_sha = params[:start_sha]
@start_version = @comparable_diffs.find { |diff| diff.head_commit_sha == @start_sha }
unless @start_version
@start_sha = @merge_request_diff.head_commit_sha
@start_version = @merge_request_diff
end
end
@diffs =
if @start_sha
@merge_request_diff.compare_with(@start_sha).diffs(diff_options)
else
@merge_request_diff.diffs(diff_options)
end
end
def define_diff_comment_vars
@new_diff_note_attrs = {
noteable_type: 'MergeRequest',
noteable_id: @merge_request.id
}
@diff_notes_disabled = !@merge_request_diff.latest? || @start_sha
@use_legacy_diff_notes = !@merge_request.has_complete_diff_refs?
@grouped_diff_discussions = @merge_request.grouped_diff_discussions
@grouped_diff_discussions = @merge_request.grouped_diff_discussions(@merge_request_diff.diff_refs)
@notes = prepare_notes_for_rendering(@grouped_diff_discussions.values.flatten.flat_map(&:notes))
end
......@@ -773,16 +781,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params.merge(diff_options: diff_options)).execute
end
def compared_diff_version
@diff_notes_disabled = true
@diffs = @merge_request_diff.compare_with(@start_sha).diffs(diff_options)
end
def original_diff_version
@diff_notes_disabled = !@merge_request_diff.latest?
@diffs = @merge_request_diff.diffs(diff_options)
end
def close_merge_request_without_source_project
if !@merge_request.source_project && @merge_request.open?
@merge_request.close
......
......@@ -62,6 +62,8 @@ module DiffHelper
end
def parallel_diff_discussions(left, right, diff_file)
return unless @grouped_diff_discussions
discussions_left = discussions_right = nil
if left && (left.unchanged? || left.removed?)
......
......@@ -61,12 +61,23 @@ module NotesHelper
end
def discussion_diff_path(discussion)
return unless discussion.diff_discussion?
if discussion.for_merge_request? && discussion.diff_discussion?
if discussion.active?
# Without a diff ID, the link always points to the latest diff version
diff_id = nil
elsif merge_request_diff = discussion.latest_merge_request_diff
diff_id = merge_request_diff.id
else
# If the discussion is not active, and we cannot find the latest
# merge request diff for this discussion, we return no path at all.
return
end
if discussion.for_merge_request? && discussion.active?
diffs_namespace_project_merge_request_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: discussion.line_code)
diffs_namespace_project_merge_request_path(discussion.project.namespace, discussion.project, discussion.noteable, diff_id: diff_id, anchor: discussion.line_code)
elsif discussion.for_commit?
namespace_project_commit_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: discussion.line_code)
anchor = discussion.line_code if discussion.diff_discussion?
namespace_project_commit_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: anchor)
end
end
end
......@@ -5,8 +5,6 @@ module DiscussionOnDiff
included do
NUMBER_OF_TRUNCATED_DIFF_LINES = 16
memoized_values << :active
delegate :line_code,
:original_line_code,
:diff_file,
......@@ -29,12 +27,6 @@ module DiscussionOnDiff
true
end
def active?
return @active if @active.present?
@active = first_note.active?
end
# Returns an array of at most 16 highlighted lines above a diff note
def truncated_diff_lines(highlight: true)
lines = highlight ? highlighted_diff_lines : diff_lines
......
......@@ -25,4 +25,18 @@ module NoteOnDiff
def diff_attributes
raise NotImplementedError
end
def active?(diff_refs = nil)
raise NotImplementedError
end
private
def noteable_diff_refs
if noteable.respond_to?(:diff_sha_refs)
noteable.diff_sha_refs
else
noteable.diff_refs
end
end
end
......@@ -36,10 +36,10 @@ module Noteable
.discussions(self)
end
def grouped_diff_discussions
def grouped_diff_discussions(*args)
# Doesn't use `discussion_notes`, because this may include commit diff notes
# besides MR diff notes, that we do no want to display on the MR Changes tab.
notes.inc_relations_for_view.grouped_diff_discussions
notes.inc_relations_for_view.grouped_diff_discussions(*args)
end
def resolvable_discussions
......
......@@ -10,6 +10,7 @@ class DiffDiscussion < Discussion
delegate :position,
:original_position,
:latest_merge_request_diff,
to: :first_note
......
......@@ -70,20 +70,18 @@ class DiffNote < Note
self.position.diff_refs == diff_refs
end
def latest_merge_request_diff
return unless for_merge_request?
self.noteable.merge_request_diff_for(self.position.diff_refs)
end
private
def supported?
for_commit? || self.noteable.has_complete_diff_refs?
end
def noteable_diff_refs
if noteable.respond_to?(:diff_sha_refs)
noteable.diff_sha_refs
else
noteable.diff_refs
end
end
def set_original_position
self.original_position = self.position.dup unless self.original_position&.complete?
end
......
......@@ -22,7 +22,9 @@ class GeoNode < ActiveRecord::Base
after_initialize :build_dependents
after_save :refresh_bulk_notify_worker_status
after_save :expire_cache!
after_destroy :refresh_bulk_notify_worker_status
after_destroy :expire_cache!
before_validation :update_dependents_attributes
before_validation :ensure_access_keys!
......@@ -160,4 +162,8 @@ class GeoNode < ActiveRecord::Base
self.system_hook.push_events = true
self.system_hook.tag_push_events = true
end
def expire_cache!
Gitlab::Geo.expire_cache!
end
end
......@@ -7,6 +7,8 @@
class LegacyDiffDiscussion < Discussion
include DiscussionOnDiff
memoized_values << :active
def legacy_diff_discussion?
true
end
......@@ -15,6 +17,12 @@ class LegacyDiffDiscussion < Discussion
LegacyDiffNote
end
def active?(*args)
return @active if @active.present?
@active = first_note.active?(*args)
end
def collapsed?
!active?
end
......
......@@ -61,11 +61,12 @@ class LegacyDiffNote < Note
#
# If the note's current diff cannot be matched in the MergeRequest's current
# diff, it's considered inactive.
def active?
def active?(diff_refs = nil)
return @active if defined?(@active)
return true if for_commit?
return true unless diff_line
return false unless noteable
return false if diff_refs && diff_refs != noteable_diff_refs
noteable_diff = find_noteable_diff
......
......@@ -390,6 +390,14 @@ class MergeRequest < ActiveRecord::Base
merge_request_diff(true)
end
def merge_request_diff_for(diff_refs)
@merge_request_diffs_by_diff_refs ||= Hash.new do |h, diff_refs|
h[diff_refs] = merge_request_diffs.viewable.select_without_diff.find_by_diff_refs(diff_refs)
end
@merge_request_diffs_by_diff_refs[diff_refs]
end
def reload_diff_if_branch_changed
if source_branch_changed? || target_branch_changed?
reload_diff
......
......@@ -31,6 +31,10 @@ class MergeRequestDiff < ActiveRecord::Base
# It allows you to override variables like head_commit_sha before getting diff.
after_create :save_git_content, unless: :importing?
def self.find_by_diff_refs(diff_refs)
find_by(start_commit_sha: diff_refs.start_sha, head_commit_sha: diff_refs.head_sha, base_commit_sha: diff_refs.base_sha)
end
def self.select_without_diff
select(column_names - ['st_diffs'])
end
......@@ -130,6 +134,12 @@ class MergeRequestDiff < ActiveRecord::Base
st_commits.map { |commit| commit[:id] }
end
def diff_refs=(new_diff_refs)
self.base_commit_sha = new_diff_refs&.base_sha
self.start_commit_sha = new_diff_refs&.start_sha
self.head_commit_sha = new_diff_refs&.head_sha
end
def diff_refs
return unless start_commit_sha || base_commit_sha
......
......@@ -115,11 +115,11 @@ class Note < ActiveRecord::Base
Discussion.build(notes)
end
def grouped_diff_discussions
def grouped_diff_discussions(diff_refs = nil)
diff_notes.
fresh.
discussions.
select(&:active?).
select { |n| n.active?(diff_refs) }.
group_by(&:line_code)
end
......@@ -146,6 +146,10 @@ class Note < ActiveRecord::Base
true
end
def latest_merge_request_diff
nil
end
def max_attachment_size
current_application_settings.max_attachment_size.megabytes.to_i
end
......
......@@ -34,6 +34,7 @@ module Projects
unless remove_legacy_registry_tags
raise_error('Failed to remove some tags in project container registry. Please try again or contact administrator.')
<<<<<<< HEAD
end
unless remove_repository(repo_path)
......@@ -42,6 +43,8 @@ module Projects
unless remove_repository(wiki_path)
raise_error('Failed to remove wiki repository. Please try again or contact administrator.')
=======
>>>>>>> 57ef7bca47f3cdeedd684779f1b1f825bb778cec
end
end
......@@ -76,11 +79,11 @@ module Projects
def trash_repositories!
unless remove_repository(repo_path)
raise_error('Failed to remove project repository. Please try again or contact administrator')
raise_error('Failed to remove project repository. Please try again or contact administrator.')
end
unless remove_repository(wiki_path)
raise_error('Failed to remove wiki repository. Please try again or contact administrator')
raise_error('Failed to remove wiki repository. Please try again or contact administrator.')
end
end
......
......@@ -20,21 +20,22 @@
= discussion.author.to_reference
started a discussion
- url = discussion_diff_path(discussion)
- if discussion.for_commit? && @noteable != discussion.noteable
on
- commit = discussion.noteable
- if commit
commit
- anchor = discussion.line_code if discussion.diff_discussion?
= link_to commit.short_id, namespace_project_commit_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: anchor), class: 'monospace'
= link_to commit.short_id, url, class: 'monospace'
- else
a deleted commit
- elsif discussion.diff_discussion?
on
- if discussion.active?
= link_to 'the diff', discussion_diff_path(discussion)
- else
an outdated diff
= conditional_link_to url.present?, url do
- if discussion.active?
the diff
- else
an outdated diff
= time_ago_with_tooltip(discussion.created_at, placement: "bottom", html_class: "note-created-ago")
= render "discussions/headline", discussion: discussion
......
......@@ -26,6 +26,7 @@
#blob-content-holder.blob-content-holder
%article.file-holder
= render "projects/blob/header", blob: blob
<<<<<<< HEAD
- if current_user
.js-file-fork-suggestion-section.file-fork-suggestion.hidden
%span.file-fork-suggestion-note
......@@ -33,6 +34,8 @@
= link_to 'Fork', fork_path, method: :post, class: 'btn btn-grouped btn-inverted btn-new'
%button.js-cancel-fork-suggestion.btn.btn-grouped{ type: 'button' }
Cancel
=======
>>>>>>> 57ef7bca47f3cdeedd684779f1b1f825bb778cec
- if blob.empty?
.file-content.code
......
......@@ -40,6 +40,13 @@
- if current_user
= replace_blob_link
= delete_blob_link
- if current_user
.js-file-fork-suggestion-section.file-fork-suggestion.hidden
%span.file-fork-suggestion-note
You don't have permission to edit this file. Try forking this project to edit the file.
= link_to 'Fork', fork_path, method: :post, class: 'btn btn-grouped btn-inverted btn-new'
%button.js-cancel-fork-suggestion.btn.btn-grouped{ type: 'button' }
Cancel
- if license_allows_file_locks?
:javascript
......
......@@ -5,8 +5,7 @@
- left = line[:left]
- right = line[:right]
- last_line = right.new_pos if right
- unless @diff_notes_disabled
- discussions_left, discussions_right = parallel_diff_discussions(left, right, diff_file)
- discussions_left, discussions_right = parallel_diff_discussions(left, right, diff_file)
%tr.line_holder.parallel
- if left
- case left.type
......
......@@ -4,11 +4,10 @@
%a.show-suppressed-diff.js-show-suppressed-diff Changes suppressed. Click to show.
%table.text-file.code.js-syntax-highlight{ data: diff_view_data, class: too_big ? 'hide' : '' }
- discussions = @grouped_diff_discussions unless @diff_notes_disabled
= render partial: "projects/diffs/line",
collection: diff_file.highlighted_diff_lines,
as: :line,
locals: { diff_file: diff_file, discussions: discussions }
locals: { diff_file: diff_file, discussions: @grouped_diff_discussions }
- if !diff_file.new_file && !diff_file.deleted_file && diff_file.highlighted_diff_lines.any?
- last_line = diff_file.highlighted_diff_lines.last
......
......@@ -72,13 +72,16 @@
= link_to namespace_project_compare_path(@project.namespace, @project, from: @start_version.base_commit_sha, to: @merge_request_diff.base_commit_sha) do
new commits
from
%code= @merge_request.target_branch
= succeed '.' do
%code= @merge_request.target_branch
- unless @merge_request_diff.latest? && !@start_sha
- if @diff_notes_disabled
.comments-disabled-notif.content-block
= icon('info-circle')
- if @start_sha
Comments are disabled because you're comparing two versions of this merge request.
- else
Comments are disabled because you're viewing an old version of this merge request.
= link_to 'Show latest version', diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-sm'
Discussions on this version of the merge request are displayed but comment creation is disabled.
.pull-right
= link_to 'Show latest version', diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-sm'
......@@ -73,6 +73,10 @@
{{name}}
%span.dropdown-light-content
@{{username}}
<<<<<<< HEAD
=======
>>>>>>> 57ef7bca47f3cdeedd684779f1b1f825bb778cec
- unless board && board.milestone_id
#js-dropdown-milestone.filtered-search-input-dropdown-menu.dropdown-menu{ data: { icon: 'clock-o', hint: 'milestone', tag: '%milestone' } }
%ul{ data: { dropdown: true } }
......@@ -90,6 +94,10 @@
%li.filter-dropdown-item
%button.btn.btn-link.js-data-value
{{title}}
<<<<<<< HEAD
=======
>>>>>>> 57ef7bca47f3cdeedd684779f1b1f825bb778cec
#js-dropdown-label.filtered-search-input-dropdown-menu.dropdown-menu{ data: { icon: 'tag', hint: 'label', tag: '~label', type: 'array' } }
%ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'none' } }
......
---
title: Cache Gitlab::Geo queries
merge_request: 1507
author:
---
title: Link to outdated diff in older MR version from outdated diff discussion
merge_request:
author:
......@@ -166,7 +166,10 @@ constraints(ProjectUrlConstrainer.new) do
resources :push_access_levels, only: [:destroy]
end
end
<<<<<<< HEAD
=======
>>>>>>> 57ef7bca47f3cdeedd684779f1b1f825bb778cec
resources :protected_tags, only: [:index, :show, :create, :update, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
resources :variables, only: [:index, :show, :update, :create, :destroy]
......
......@@ -11,7 +11,12 @@
#
# It's strongly recommended that you check this file into your version control system.
<<<<<<< HEAD
ActiveRecord::Schema.define(version: 20170408033905) do
=======
ActiveRecord::Schema.define(version: 20170407140450) do
>>>>>>> 57ef7bca47f3cdeedd684779f1b1f825bb778cec
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
enable_extension "pg_trgm"
......
......@@ -44,11 +44,18 @@ All technical content published by GitLab lives in the documentation, including:
- [Upload your GitLab License](user/admin_area/license.md) Upload the license you purchased for GitLab Enterprise Edition to unlock its features.
- [Audit Events](administration/audit_events.md) Check how user access changed in projects and groups.
- [Access restrictions](user/admin_area/settings/visibility_and_access_controls.md#enabled-git-access-protocols) Define which Git access protocols can be used to talk to GitLab
<<<<<<< HEAD
- [Changing the appearance of the login page](customization/branded_login_page.md) Make the login page branded for your GitLab instance.
- [Email](tools/email.md) Email GitLab users from GitLab
- [Push Rules](push_rules/push_rules.md) Advanced push rules for your project.
- [Help message](customization/help_message.md) Set information about administrators of your GitLab instance.
=======
>>>>>>> 57ef7bca47f3cdeedd684779f1b1f825bb778cec
- [Authentication/Authorization](administration/auth/README.md) Configure external authentication with LDAP, SAML, CAS and additional Omniauth providers.
- [Changing the appearance of the login page](customization/branded_login_page.md) Make the login page branded for your GitLab instance.
- [Email](tools/email.md) Email GitLab users from GitLab
- [Push Rules](push_rules/push_rules.md) Advanced push rules for your project.
- [Help message](customization/help_message.md) Set information about administrators of your GitLab instance.
- [Container Registry](administration/container_registry.md) Configure Docker Registry with GitLab.
- [Custom Git hooks](administration/custom_hooks.md) Custom Git hooks (on the filesystem) for when webhooks aren't enough.
- [Debugging Tips](administration/troubleshooting/debug.md) Tips to debug problems when things go wrong
......
......@@ -112,7 +112,11 @@ GET /groups/:id/issues?search=issue+title+or+description
| Attribute | Type | Required | Description |
|-------------|----------------|----------|-----------------------------------------------------------------------------------------------------------------------------|
<<<<<<< HEAD
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
=======
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
>>>>>>> 57ef7bca47f3cdeedd684779f1b1f825bb778cec
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
| `iids` | Array[integer] | no | Return only the issues having the given `iid` |
......
......@@ -200,6 +200,12 @@ milestone with the due date that is next.
![Update boards milestone](img/issue_board_multiple_milestone.png)
## Focus mode
Click the button at the top right to toggle focus mode on and off. In focus mode, the navigation UI is hidden, allowing you to focus on issues in the board.
![Board focus mode](img/issue_board_focus_mode.gif)
## Permissions
[Developers and up](../permissions.md) can use all the functionality of the
......
......@@ -623,7 +623,11 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should not see merge button' do
page.within '.mr-state-widget' do
<<<<<<< HEAD
expect(page).not_to have_button('Accept merge mequest')
=======
expect(page).not_to have_button('Accept merge request')
>>>>>>> 57ef7bca47f3cdeedd684779f1b1f825bb778cec
end
end
......
......@@ -5,21 +5,26 @@ module API
before { authenticate! }
helpers do
params :optional_params do
params :optional_params_ce do
optional :description, type: String, desc: 'The description of the group'
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the group'
optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
optional :membership_lock, type: Boolean, desc: 'Prevent adding new members to project membership within this group'
optional :share_with_group_lock, type: Boolean, desc: 'Prevent sharing a project with another group within this group'
end
params :optional_params_ee do
optional :membership_lock, type: Boolean, desc: 'Prevent adding new members to project membership within this group'
optional :ldap_cn, type: String, desc: 'LDAP Common Name'
optional :ldap_access, type: Integer, desc: 'A valid access level'
all_or_none_of :ldap_cn, :ldap_access
end
params :optional_params do
use :optional_params_ce
use :optional_params_ee
end
params :statistics_params do
optional :statistics, type: Boolean, default: false, desc: 'Include project statistics'
end
......@@ -75,7 +80,6 @@ module API
requires :path, type: String, desc: 'The path of the group'
optional :parent_id, type: Integer, desc: 'The parent group id for creating nested group'
use :optional_params
use :optional_params_ee
end
post do
authorize! :create_group
......
......@@ -30,16 +30,23 @@ module API
use :pagination
end
params :issue_params do
params :issue_params_ce do
optional :description, type: String, desc: 'The description of an issue'
optional :assignee_id, type: Integer, desc: 'The ID of a user to assign issue'
optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign issue'
optional :labels, type: String, desc: 'Comma-separated list of label names'
optional :due_date, type: String, desc: 'Date string in the format YEAR-MONTH-DAY'
optional :confidential, type: Boolean, desc: 'Boolean parameter if the issue should be confidential'
# Gitlab-EE specific
end
params :issue_params_ee do
optional :weight, type: Integer, values: 0..9, desc: 'The weight of the issue'
end
params :issue_params do
use :issue_params_ce
use :issue_params_ee
end
end
resource :issues do
......
......@@ -33,15 +33,23 @@ module API
end
end
params :optional_params do
params :optional_params_ce do
optional :description, type: String, desc: 'The description of the merge request'
optional :assignee_id, type: Integer, desc: 'The ID of a user to assign the merge request'
optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign the merge request'
optional :labels, type: String, desc: 'Comma-separated list of label names'
optional :approvals_before_merge, type: Integer, desc: 'Number of approvals required before this can be merged'
optional :remove_source_branch, type: Boolean, desc: 'Remove source branch when merging'
end
params :optional_params_ee do
optional :approvals_before_merge, type: Integer, desc: 'Number of approvals required before this can be merged'
optional :squash, type: Boolean, desc: 'Squash commits when merging'
end
params :optional_params do
use :optional_params_ce
use :optional_params_ee
end
end
desc 'List merge requests' do
......@@ -147,14 +155,29 @@ module API
success Entities::MergeRequest
end
params do
# CE
at_least_one_of_ce = [
:assignee_id,
:description,
:labels,
:milestone_id,
:remove_source_branch,
:state_event,
:target_branch,
:title
]
optional :title, type: String, allow_blank: false, desc: 'The title of the merge request'
optional :target_branch, type: String, allow_blank: false, desc: 'The target branch'
optional :state_event, type: String, values: %w[close reopen],
desc: 'Status of the merge request'
# EE
at_least_one_of_ee = [
:squash
]
use :optional_params
at_least_one_of :title, :target_branch, :description, :assignee_id,
:milestone_id, :labels, :state_event,
:remove_source_branch, :squash
at_least_one_of(*(at_least_one_of_ce + at_least_one_of_ee))
end
put ':id/merge_requests/:merge_request_iid' do
merge_request = find_merge_request_with_access(params.delete(:merge_request_iid), :update_merge_request)
......@@ -175,12 +198,15 @@ module API
success Entities::MergeRequest
end
params do
# CE
optional :merge_commit_message, type: String, desc: 'Custom merge commit message'
optional :should_remove_source_branch, type: Boolean,
desc: 'When true, the source branch will be deleted if possible'
optional :merge_when_pipeline_succeeds, type: Boolean,
desc: 'When true, this merge request will be merged when the pipeline succeeds'
optional :sha, type: String, desc: 'When present, must have the HEAD SHA of the source branch'
# EE
optional :squash, type: Boolean, desc: 'When true, the commits will be squashed into a single commit on merge'
end
put ':id/merge_requests/:merge_request_iid/merge' do
......
......@@ -6,7 +6,7 @@ module API
before { authenticate_non_get! }
helpers do
params :optional_params do
params :optional_params_ce do
optional :description, type: String, desc: 'The description of the project'
optional :issues_enabled, type: Boolean, desc: 'Flag indication if the issue tracker is enabled'
optional :merge_requests_enabled, type: Boolean, desc: 'Flag indication if merge requests are enabled'
......@@ -21,11 +21,17 @@ module API
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
optional :only_allow_merge_if_pipeline_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed'
optional :only_allow_merge_if_all_discussions_are_resolved, type: Boolean, desc: 'Only allow to merge if all discussions are resolved'
end
# EE-specific
params :optional_params_ee do
optional :repository_storage, type: String, desc: 'Which storage shard the repository is on. Available only to admins'
optional :approvals_before_merge, type: Integer, desc: 'How many approvers should approve merge request by default'
end
params :optional_params do
use :optional_params_ce
use :optional_params_ee
end
end
resource :projects do
......@@ -202,19 +208,39 @@ module API
success Entities::Project
end
params do
# CE
at_least_one_of_ce =
[
:builds_enabled,
:container_registry_enabled,
:default_branch,
:description,
:issues_enabled,
:lfs_enabled,
:merge_requests_enabled,
:name,
:only_allow_merge_if_all_discussions_are_resolved,
:only_allow_merge_if_pipeline_succeeds,
:path,
:public_builds,
:request_access_enabled,
:shared_runners_enabled,
:snippets_enabled,
:visibility,
:wiki_enabled,
]
optional :name, type: String, desc: 'The name of the project'
optional :default_branch, type: String, desc: 'The default branch of the project'
optional :path, type: String, desc: 'The path of the repository'
# EE
at_least_one_of_ee = [
:approvals_before_merge,
:repository_storage
]
use :optional_params
at_least_one_of :name, :description, :issues_enabled, :merge_requests_enabled,
:wiki_enabled, :builds_enabled, :snippets_enabled,
:shared_runners_enabled, :container_registry_enabled,
:lfs_enabled, :visibility, :public_builds,
:request_access_enabled, :only_allow_merge_if_pipeline_succeeds,
:only_allow_merge_if_all_discussions_are_resolved, :path,
:default_branch,
## EE-specific
:repository_storage, :approvals_before_merge
at_least_one_of(*(at_least_one_of_ce + at_least_one_of_ee))
end
put ':id' do
authorize_admin_project
......
......@@ -20,6 +20,55 @@ module API
success Entities::ApplicationSetting
end
params do
# CE
at_least_one_of_ce = [
:admin_notification_email,
:after_sign_out_path,
:after_sign_up_text,
:akismet_enabled,
:container_registry_token_expire_delay,
:default_artifacts_expire_in,
:default_branch_protection,
:default_group_visibility,
:default_project_visibility,
:default_projects_limit,
:default_snippet_visibility,
:disabled_oauth_sign_in_sources,
:domain_blacklist_enabled,
:domain_whitelist,
:email_author_in_body,
:enabled_git_access_protocol,
:gravatar_enabled,
:help_page_text,
:home_page_url,
:housekeeping_enabled,
:html_emails_enabled,
:import_sources,
:koding_enabled,
:max_artifacts_size,
:max_attachment_size,
:max_pages_size,
:metrics_enabled,
:plantuml_enabled,
:polling_interval_multiplier,
:recaptcha_enabled,
:repository_checks_enabled,
:repository_storage,
:require_two_factor_authentication,
:restricted_visibility_levels,
:send_user_confirmation_email,
:sentry_enabled,
:session_expire_delay,
:shared_runners_enabled,
:sidekiq_throttling_enabled,
:sign_in_text,
:signin_enabled,
:signup_enabled,
:terminal_max_session_time,
:user_default_external,
:user_oauth_applications,
:version_check_enabled
]
optional :default_branch_protection, type: Integer, values: [0, 1, 2], desc: 'Determine if developers can push to master'
optional :default_project_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default project visibility'
optional :default_snippet_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default snippet visibility'
......@@ -111,22 +160,6 @@ module API
end
optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.'
optional :polling_interval_multiplier, type: BigDecimal, desc: 'Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling.'
# GitLab-EE specific settings
optional :help_text, type: String, desc: 'GitLab server administrator information'
optional :elasticsearch_indexing, type: Boolean, desc: 'Enable Elasticsearch indexing'
given elasticsearch_indexing: ->(val) { val } do
optional :elasticsearch_search, type: Boolean, desc: 'Enable Elasticsearch search'
requires :elasticsearch_url, type: String, desc: 'The url to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., "http://localhost:9200, http://localhost:9201")'
end
optional :elasticsearch_aws, type: Boolean, desc: 'Enable support for AWS hosted elasticsearch'
given elasticsearch_aws: ->(val) { val } do
requires :elasticsearch_aws_region, type: String, desc: 'The AWS region the elasticsearch domain is configured'
optional :elasticsearch_aws_access_key, type: String, desc: 'AWS IAM access key'
optional :elasticsearch_aws_secret_access_key, type: String, desc: 'AWS IAM secret access key'
end
optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.'
optional :repository_storages, type: Array[String], desc: 'A list of names of enabled storage paths, taken from `gitlab.yml`. New projects will be created in one of these stores, chosen at random.'
optional :repository_size_limit, type: Integer, desc: 'Size limit per repository (MB)'
at_least_one_of :default_branch_protection, :default_project_visibility, :default_snippet_visibility,
:default_group_visibility, :restricted_visibility_levels, :import_sources,
:enabled_git_access_protocol, :gravatar_enabled, :default_projects_limit,
......@@ -142,10 +175,33 @@ module API
:akismet_enabled, :admin_notification_email, :sentry_enabled,
:repository_checks_enabled, :koding_enabled, :plantuml_enabled,
:version_check_enabled, :email_author_in_body, :html_emails_enabled,
:housekeeping_enabled, :terminal_max_session_time, :polling_interval_multiplier,
# GitLab-EE specific settings
:help_text, :elasticsearch_indexing, :usage_ping_enabled,
:repository_storages, :repository_size_limit
:housekeeping_enabled, :terminal_max_session_time, :polling_interval_multiplier
# EE
at_least_one_of_ee = [
:elasticsearch_indexing,
:help_text,
:repository_size_limit,
:repository_storages,
:usage_ping_enabled
]
optional :help_text, type: String, desc: 'GitLab server administrator information'
optional :elasticsearch_indexing, type: Boolean, desc: 'Enable Elasticsearch indexing'
given elasticsearch_indexing: ->(val) { val } do
optional :elasticsearch_search, type: Boolean, desc: 'Enable Elasticsearch search'
requires :elasticsearch_url, type: String, desc: 'The url to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., "http://localhost:9200, http://localhost:9201")'
end
optional :elasticsearch_aws, type: Boolean, desc: 'Enable support for AWS hosted elasticsearch'
given elasticsearch_aws: ->(val) { val } do
requires :elasticsearch_aws_region, type: String, desc: 'The AWS region the elasticsearch domain is configured'
optional :elasticsearch_aws_access_key, type: String, desc: 'AWS IAM access key'
optional :elasticsearch_aws_secret_access_key, type: String, desc: 'AWS IAM secret access key'
end
optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.'
optional :repository_storages, type: Array[String], desc: 'A list of names of enabled storage paths, taken from `gitlab.yml`. New projects will be created in one of these stores, chosen at random.'
optional :repository_size_limit, type: Integer, desc: 'Size limit per repository (MB)'
at_least_one_of(*(at_least_one_of_ce + at_least_one_of_ee))
end
put "application/settings" do
attrs = declared_params(include_missing: false)
......
......@@ -37,12 +37,16 @@ module API
success Entities::UserBasic
end
params do
# CE
optional :username, type: String, desc: 'Get a single user with a specific username'
optional :search, type: String, desc: 'Search for a username'
optional :active, type: Boolean, default: false, desc: 'Filters only active users'
optional :external, type: Boolean, default: false, desc: 'Filters only external users'
optional :blocked, type: Boolean, default: false, desc: 'Filters only blocked users'
# EE
optional :skip_ldap, type: Boolean, default: false, desc: 'Skip LDAP users'
use :pagination
end
get do
......
......@@ -18,6 +18,12 @@ module Gitlab
head_sha == other.head_sha
end
alias_method :eql?, :==
def hash
[base_sha, start_sha, head_sha].hash
end
# There is only one case in which we will have `start_sha` and `head_sha`,
# but not `base_sha`, which is when a diff is generated between an
# orphaned branch and another branch, which means there _is_ no base, but
......
......@@ -2,6 +2,16 @@ module Gitlab
module Geo
OauthApplicationUndefinedError = Class.new(StandardError)
CACHE_KEYS = %i[
geo_primary_node
geo_secondary_nodes
geo_node_enabled
geo_node_primary
geo_node_secondary
geo_primary_ssh_path_prefix
geo_oauth_application
].freeze
def self.current_node
self.cache_value(:geo_node_current) do
GeoNode.find_by(host: Gitlab.config.gitlab.host,
......@@ -80,7 +90,19 @@ module Gitlab
def self.cache_value(key, &block)
return yield unless RequestStore.active?
RequestStore.fetch(key) { yield }
# We need a short expire time as we can't manually expire on a secondary node
RequestStore.fetch(key) { Rails.cache.fetch(key, expires_in: 15.seconds) { yield } }
end
def self.expire_cache!
return true unless RequestStore.active?
CACHE_KEYS.each do |key|
Rails.cache.delete(key)
RequestStore.delete(key)
end
true
end
def self.generate_access_keys
......
......@@ -40,6 +40,7 @@ FactoryGirl.define do
transient do
line_number 14
diff_refs { noteable.try(:diff_refs) }
end
position do
......@@ -48,7 +49,7 @@ FactoryGirl.define do
new_path: "files/ruby/popen.rb",
old_line: nil,
new_line: line_number,
diff_refs: noteable.try(:diff_refs)
diff_refs: diff_refs
)
end
......
......@@ -246,7 +246,7 @@ feature 'Merge request approvals', js: true, feature: true do
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
it 'I am unable to set Merge When Pipeline Succeeds' do
it 'I am unable to set Merge when pipeline succeeds' do
# before approval status is loaded
expect(page).to have_button('Merge when pipeline succeeds', disabled: true)
......
require 'spec_helper'
feature 'Merge Request Discussions', feature: true do
before do
login_as :admin
end
context "Diff discussions" do
let(:merge_request) { create(:merge_request, importing: true) }
let(:project) { merge_request.source_project }
let!(:old_merge_request_diff) { merge_request.merge_request_diffs.create(diff_refs: outdated_diff_refs) }
let!(:new_merge_request_diff) { merge_request.merge_request_diffs.create }
let!(:outdated_discussion) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: outdated_position).to_discussion }
let!(:active_discussion) { create(:diff_note_on_merge_request, noteable: merge_request, project: project).to_discussion }
let(:outdated_position) do
Gitlab::Diff::Position.new(
old_path: "files/ruby/popen.rb",
new_path: "files/ruby/popen.rb",
old_line: nil,
new_line: 9,
diff_refs: outdated_diff_refs
)
end
let(:outdated_diff_refs) { project.commit("874797c3a73b60d2187ed6e2fcabd289ff75171e").diff_refs }
before(:each) do
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
context 'active discussions' do
it 'shows a link to the diff' do
within(".discussion[data-discussion-id='#{active_discussion.id}']") do
path = diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, anchor: active_discussion.line_code)
expect(page).to have_link('the diff', href: path)
end
end
end
context 'outdated discussions' do
it 'shows a link to the outdated diff' do
within(".discussion[data-discussion-id='#{outdated_discussion.id}']") do
path = diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, diff_id: old_merge_request_diff.id, anchor: outdated_discussion.line_code)
expect(page).to have_link('an outdated diff', href: path)
end
end
end
end
end
......@@ -36,8 +36,23 @@ feature 'Merge Request versions', js: true, feature: true do
expect(page).to have_content '5 changed files'
end
it 'show the message about disabled comments' do
expect(page).to have_content 'Comments are disabled'
it 'show the message about disabled comment creation' do
expect(page).to have_content 'comment creation is disabled'
end
it 'shows comments that were last relevant at that version' do
position = Gitlab::Diff::Position.new(
old_path: ".gitmodules",
new_path: ".gitmodules",
old_line: nil,
new_line: 4,
diff_refs: merge_request_diff1.diff_refs
)
outdated_diff_note = create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: position)
outdated_diff_note.position = outdated_diff_note.original_position
outdated_diff_note.save!
expect(page).to have_css(".diffs .notes[data-discussion-id='#{outdated_diff_note.discussion_id}']")
end
end
......
......@@ -130,7 +130,11 @@ describe('DropDown', function () {
beforeEach(function () {
this.list = { dispatchEvent: () => {} };
this.dropdown = { hide: () => {}, list: this.list, addSelectedClass: () => {} };
<<<<<<< HEAD
this.event = { preventDefault: () => {}, target: {} };
=======
this.event = { preventDefault: () => {}, target: 'target' };
>>>>>>> 57ef7bca47f3cdeedd684779f1b1f825bb778cec
this.customEvent = {};
this.closestElement = {};
......
......@@ -155,6 +155,23 @@ describe DiffNote, models: true do
end
end
describe '#latest_merge_request_diff' do
context 'when active' do
it 'returns the current merge request diff' do
expect(subject.latest_merge_request_diff).to eq(merge_request.merge_request_diff)
end
end
context 'when outdated' do
let!(:old_merge_request_diff) { merge_request.merge_request_diff }
let!(:new_merge_request_diff) { merge_request.merge_request_diffs.create(diff_refs: commit.diff_refs) }
it 'returns the latest merge request diff that this diff note applied to' do
expect(subject.latest_merge_request_diff).to eq(old_merge_request_diff)
end
end
end
describe "creation" do
describe "updating of position" do
context "when noteable is a commit" do
......
......@@ -95,6 +95,22 @@ describe GeoNode, type: :model do
end
end
context 'cache expiration' do
let(:new_node) { FactoryGirl.build(:geo_node) }
it 'expires cache when saved' do
expect(new_node).to receive(:expire_cache!)
new_node.save!
end
it 'expires cache when removed' do
expect(node).to receive(:expire_cache!) # 1 for creation 1 for deletion
node.destroy
end
end
describe '#uri' do
context 'when all fields are filled' do
it 'returns an URI object' do
......
......@@ -1126,6 +1126,13 @@ describe API::Projects, :api do
before { project_member3 }
before { project_member2 }
it 'returns 400 when nothing sent' do
project_param = {}
put api("/projects/#{project.id}", user), project_param
expect(response).to have_http_status(400)
expect(json_response['error']).to match('at least one parameter must be provided')
end
context 'when unauthenticated' do
it 'returns authentication error' do
project_param = { name: 'bar' }
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment