summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/boards/components/board.js3
-rw-r--r--app/assets/javascripts/boards/components/project_select.vue2
-rw-r--r--app/assets/javascripts/jobs/components/sidebar.vue11
-rw-r--r--app/assets/javascripts/jobs/components/stages_dropdown.vue1
-rw-r--r--app/assets/javascripts/jobs/store/mutations.js2
-rw-r--r--app/assets/javascripts/jobs/store/state.js4
-rw-r--r--app/assets/javascripts/lib/utils/text_markdown.js17
-rw-r--r--app/assets/stylesheets/framework/typography.scss2
-rw-r--r--app/controllers/concerns/boards_responses.rb5
-rw-r--r--app/finders/issuable_finder.rb35
-rw-r--r--app/finders/issues_finder.rb8
-rw-r--r--app/models/commit_status.rb2
-rw-r--r--app/services/resource_events/merge_into_notes_service.rb2
-rw-r--r--app/views/shared/boards/components/_board.html.haml15
-rw-r--r--app/views/shared/notifications/_button.html.haml4
-rw-r--r--changelogs/unreleased/51306-fix-inaccessible-dropdown-for-codeless-projects.yml5
-rw-r--r--changelogs/unreleased/52115-Link-button-in-markdown-editor-should-recognize-URLs.yml5
-rw-r--r--changelogs/unreleased/52384-api-filter-assignee-none-any.yml5
-rw-r--r--changelogs/unreleased/52545-guest-create-issue-in-group-board.yml5
-rw-r--r--changelogs/unreleased/53133-jobs-list.yml5
-rw-r--r--changelogs/unreleased/avoid-lock-when-introduce-new-failure-reason.yml5
-rw-r--r--changelogs/unreleased/bvl-preload-user-status-for-events.yml5
-rw-r--r--doc/api/issues.md6
-rw-r--r--doc/api/merge_requests.md6
-rw-r--r--lib/api/helpers/custom_validators.rb13
-rw-r--r--lib/api/issues.rb3
-rw-r--r--lib/api/merge_requests.rb3
-rw-r--r--lib/api/runner.rb3
-rw-r--r--spec/controllers/boards/issues_controller_spec.rb19
-rw-r--r--spec/features/boards/new_issue_spec.rb10
-rw-r--r--spec/features/issues/resource_label_events_spec.rb7
-rw-r--r--spec/finders/issues_finder_spec.rb30
-rw-r--r--spec/javascripts/jobs/components/sidebar_spec.js5
-rw-r--r--spec/javascripts/jobs/store/mutations_spec.js4
-rw-r--r--spec/javascripts/lib/utils/text_markdown_spec.js27
-rw-r--r--spec/lib/api/helpers/custom_validators_spec.rb64
-rw-r--r--spec/requests/api/issues_spec.rb18
-rw-r--r--spec/requests/api/merge_requests_spec.rb17
-rw-r--r--spec/requests/api/runner_spec.rb9
-rw-r--r--spec/services/resource_events/merge_into_notes_service_spec.rb9
40 files changed, 339 insertions, 62 deletions
diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js
index 75477ebb3b3..623cda5679a 100644
--- a/app/assets/javascripts/boards/components/board.js
+++ b/app/assets/javascripts/boards/components/board.js
@@ -53,6 +53,9 @@ export default Vue.extend({
const { issuesSize } = this.list;
return `${n__('%d issue', '%d issues', issuesSize)}`;
},
+ isNewIssueShown() {
+ return this.list.type === 'backlog' || (!this.disabled && this.list.type !== 'closed');
+ }
},
watch: {
filter: {
diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue
index 4e8fe16160a..427a0868b0c 100644
--- a/app/assets/javascripts/boards/components/project_select.vue
+++ b/app/assets/javascripts/boards/components/project_select.vue
@@ -46,7 +46,7 @@ export default {
selectable: true,
data: (term, callback) => {
this.loading = true;
- return Api.groupProjects(this.groupId, term, {}, projects => {
+ return Api.groupProjects(this.groupId, term, {with_issues_enabled: true}, projects => {
this.loading = false;
callback(projects);
});
diff --git a/app/assets/javascripts/jobs/components/sidebar.vue b/app/assets/javascripts/jobs/components/sidebar.vue
index 906769ee6a2..28a02230d89 100644
--- a/app/assets/javascripts/jobs/components/sidebar.vue
+++ b/app/assets/javascripts/jobs/components/sidebar.vue
@@ -31,7 +31,7 @@ export default {
},
},
computed: {
- ...mapState(['job', 'stages', 'jobs', 'selectedStage']),
+ ...mapState(['job', 'stages', 'jobs', 'selectedStage', 'isLoadingStages']),
coverage() {
return `${this.job.coverage}%`;
},
@@ -59,10 +59,10 @@ export default {
return '';
}
- let t = this.job.metadata.timeout_human_readable;
- if (this.job.metadata.timeout_source !== '') {
- t += ` (from ${this.job.metadata.timeout_source})`;
- }
+ let t = this.job.metadata.timeout_human_readable;
+ if (this.job.metadata.timeout_source !== '') {
+ t += ` (from ${this.job.metadata.timeout_source})`;
+ }
return t;
},
@@ -270,6 +270,7 @@ export default {
/>
<stages-dropdown
+ v-if="!isLoadingStages"
:stages="stages"
:pipeline="job.pipeline"
:selected-stage="selectedStage"
diff --git a/app/assets/javascripts/jobs/components/stages_dropdown.vue b/app/assets/javascripts/jobs/components/stages_dropdown.vue
index e5e1d56e287..dc26b246d71 100644
--- a/app/assets/javascripts/jobs/components/stages_dropdown.vue
+++ b/app/assets/javascripts/jobs/components/stages_dropdown.vue
@@ -22,7 +22,6 @@ export default {
required: true,
},
},
-
computed: {
hasRef() {
return !_.isEmpty(this.pipeline.ref);
diff --git a/app/assets/javascripts/jobs/store/mutations.js b/app/assets/javascripts/jobs/store/mutations.js
index 4195d787f12..cd440d21c1f 100644
--- a/app/assets/javascripts/jobs/store/mutations.js
+++ b/app/assets/javascripts/jobs/store/mutations.js
@@ -71,7 +71,7 @@ export default {
* after the first request,
* and we do not want to hijack that
*/
- if (state.selectedStage === 'More' && job.stage) {
+ if (state.selectedStage === '' && job.stage) {
state.selectedStage = job.stage;
}
},
diff --git a/app/assets/javascripts/jobs/store/state.js b/app/assets/javascripts/jobs/store/state.js
index 0eb269ca38f..04825187c99 100644
--- a/app/assets/javascripts/jobs/store/state.js
+++ b/app/assets/javascripts/jobs/store/state.js
@@ -1,5 +1,3 @@
-import { __ } from '~/locale';
-
export default () => ({
jobEndpoint: null,
traceEndpoint: null,
@@ -29,7 +27,7 @@ export default () => ({
// sidebar dropdown & list of jobs
isLoadingStages: false,
isLoadingJobs: false,
- selectedStage: __('More'),
+ selectedStage: '',
stages: [],
jobs: [],
});
diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js
index e26a6b986be..c52cfb806a2 100644
--- a/app/assets/javascripts/lib/utils/text_markdown.js
+++ b/app/assets/javascripts/lib/utils/text_markdown.js
@@ -2,6 +2,8 @@
import $ from 'jquery';
import { insertText } from '~/lib/utils/common_utils';
+const LINK_TAG_PATTERN = '[{text}](url)';
+
function selectedText(text, textarea) {
return text.substring(textarea.selectionStart, textarea.selectionEnd);
}
@@ -76,6 +78,21 @@ export function insertMarkdownText({ textArea, text, tag, blockTag, selected, wr
removedFirstNewLine = false;
currentLineEmpty = false;
+ // check for link pattern and selected text is an URL
+ // if so fill in the url part instead of the text part of the pattern.
+ if (tag === LINK_TAG_PATTERN) {
+ if (URL) {
+ try {
+ const ignoredUrl = new URL(selected);
+ // valid url
+ tag = '[text]({text})';
+ select = 'text';
+ } catch (e) {
+ // ignore - no valid url
+ }
+ }
+ }
+
// Remove the first newline
if (selected.indexOf('\n') === 0) {
removedFirstNewLine = true;
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 6d891e21556..e261bd7c0ca 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -34,7 +34,7 @@
margin-bottom: 0;
}
- *:first-child:not(.katex-display) {
+ *:first-child {
margin-top: 0;
}
diff --git a/app/controllers/concerns/boards_responses.rb b/app/controllers/concerns/boards_responses.rb
index b7e4f9b81f1..3cdf4ddf8bb 100644
--- a/app/controllers/concerns/boards_responses.rb
+++ b/app/controllers/concerns/boards_responses.rb
@@ -50,7 +50,10 @@ module BoardsResponses
end
def authorize_create_issue
- authorize_action_for!(project, :admin_issue)
+ list = List.find(issue_params[:list_id])
+ action = list.backlog? ? :create_issue : :admin_issue
+
+ authorize_action_for!(project, action)
end
def authorize_admin_list
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 8abfe0c4c17..eb3d2498830 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -14,7 +14,7 @@
# project_id: integer
# milestone_title: string
# author_id: integer
-# assignee_id: integer
+# assignee_id: integer or 'None' or 'Any'
# search: string
# label_name: string
# sort: string
@@ -34,6 +34,11 @@ class IssuableFinder
requires_cross_project_access unless: -> { project? }
+ # This is used as a common filter for None / Any
+ FILTER_NONE = 'none'.freeze
+ FILTER_ANY = 'any'.freeze
+
+ # This is accepted as a deprecated filter and is also used in unassigning users
NONE = '0'.freeze
attr_accessor :current_user, :params
@@ -236,16 +241,20 @@ class IssuableFinder
# rubocop: enable CodeReuse/ActiveRecord
def assignee_id?
- params[:assignee_id].present? && params[:assignee_id].to_s != NONE
+ params[:assignee_id].present?
end
def assignee_username?
- params[:assignee_username].present? && params[:assignee_username].to_s != NONE
+ params[:assignee_username].present?
end
- def no_assignee?
+ def filter_by_no_assignee?
# Assignee_id takes precedence over assignee_username
- params[:assignee_id].to_s == NONE || params[:assignee_username].to_s == NONE
+ [NONE, FILTER_NONE].include?(params[:assignee_id].to_s.downcase) || params[:assignee_username].to_s == NONE
+ end
+
+ def filter_by_any_assignee?
+ params[:assignee_id].to_s.downcase == FILTER_ANY
end
# rubocop: disable CodeReuse/ActiveRecord
@@ -399,15 +408,17 @@ class IssuableFinder
# rubocop: disable CodeReuse/ActiveRecord
def by_assignee(items)
- if assignee
- items = items.where(assignee_id: assignee.id)
- elsif no_assignee?
- items = items.where(assignee_id: nil)
+ if filter_by_no_assignee?
+ items.where(assignee_id: nil)
+ elsif filter_by_any_assignee?
+ items.where('assignee_id IS NOT NULL')
+ elsif assignee
+ items.where(assignee_id: assignee.id)
elsif assignee_id? || assignee_username? # assignee not found
- items = items.none
+ items.none
+ else
+ items
end
-
- items
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb
index abdc47b9866..cee57a83df4 100644
--- a/app/finders/issues_finder.rb
+++ b/app/finders/issues_finder.rb
@@ -137,10 +137,12 @@ class IssuesFinder < IssuableFinder
# rubocop: disable CodeReuse/ActiveRecord
def by_assignee(items)
- if assignee
- items.assigned_to(assignee)
- elsif no_assignee?
+ if filter_by_no_assignee?
items.unassigned
+ elsif filter_by_any_assignee?
+ items.assigned
+ elsif assignee
+ items.assigned_to(assignee)
elsif assignee_id? || assignee_username? # assignee not found
items.none
else
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 06507345fe8..344f091c872 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -109,7 +109,7 @@ class CommitStatus < ActiveRecord::Base
before_transition any => :failed do |commit_status, transition|
failure_reason = transition.args.first
- commit_status.failure_reason = failure_reason
+ commit_status.failure_reason = CommitStatus.failure_reasons[failure_reason]
end
after_transition do |commit_status, transition|
diff --git a/app/services/resource_events/merge_into_notes_service.rb b/app/services/resource_events/merge_into_notes_service.rb
index 596c0105ea0..7504773a002 100644
--- a/app/services/resource_events/merge_into_notes_service.rb
+++ b/app/services/resource_events/merge_into_notes_service.rb
@@ -34,7 +34,7 @@ module ResourceEvents
def label_events_by_discussion_id
return [] unless resource.respond_to?(:resource_label_events)
- events = resource.resource_label_events.includes(:label, :user)
+ events = resource.resource_label_events.includes(:label, user: :status)
events = since_fetch_at(events)
events.group_by { |event| event.discussion_id }
diff --git a/app/views/shared/boards/components/_board.html.haml b/app/views/shared/boards/components/_board.html.haml
index e26f5260e5b..c6c5cadc3f5 100644
--- a/app/views/shared/boards/components/_board.html.haml
+++ b/app/views/shared/boards/components/_board.html.haml
@@ -39,14 +39,13 @@
{{ list.issuesSize }}
= render_if_exists "shared/boards/components/list_weight"
- - if can?(current_user, :admin_list, current_board_parent)
- %button.issue-count-badge-add-button.btn.btn-sm.btn-default.ml-1.has-tooltip.js-no-trigger-collapse{ type: "button",
- "@click" => "showNewIssueForm",
- "v-if" => 'list.type !== "closed"',
- "aria-label" => _("New issue"),
- "title" => _("New issue"),
- data: { placement: "top", container: "body" } }
- = icon("plus", class: "js-no-trigger-collapse")
+ %button.issue-count-badge-add-button.btn.btn-sm.btn-default.ml-1.has-tooltip.js-no-trigger-collapse{ type: "button",
+ "@click" => "showNewIssueForm",
+ "v-if" => "isNewIssueShown",
+ "aria-label" => _("New issue"),
+ "title" => _("New issue"),
+ data: { placement: "top", container: "body" } }
+ = icon("plus", class: "js-no-trigger-collapse")
%board-list{ "v-if" => 'list.type !== "blank" && list.type !== "promotion"',
":list" => "list",
diff --git a/app/views/shared/notifications/_button.html.haml b/app/views/shared/notifications/_button.html.haml
index 09ddf732ada..f6c7ca70ebd 100644
--- a/app/views/shared/notifications/_button.html.haml
+++ b/app/views/shared/notifications/_button.html.haml
@@ -9,11 +9,11 @@
%button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } }
= icon("bell", class: "js-notification-loading")
= notification_title(notification_setting.level)
- %button.btn.dropdown-toggle{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting) } }
+ %button.btn.dropdown-toggle{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
= icon('caret-down')
.sr-only Toggle dropdown
- else
- %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), display: 'static' } }
+ %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
= icon("bell", class: "js-notification-loading")
= notification_title(notification_setting.level)
= icon("caret-down")
diff --git a/changelogs/unreleased/51306-fix-inaccessible-dropdown-for-codeless-projects.yml b/changelogs/unreleased/51306-fix-inaccessible-dropdown-for-codeless-projects.yml
new file mode 100644
index 00000000000..13e3bb66430
--- /dev/null
+++ b/changelogs/unreleased/51306-fix-inaccessible-dropdown-for-codeless-projects.yml
@@ -0,0 +1,5 @@
+---
+title: Fix inaccessible dropdown for code-less projects
+merge_request: 22137
+author:
+type: other
diff --git a/changelogs/unreleased/52115-Link-button-in-markdown-editor-should-recognize-URLs.yml b/changelogs/unreleased/52115-Link-button-in-markdown-editor-should-recognize-URLs.yml
new file mode 100644
index 00000000000..8521335c2ea
--- /dev/null
+++ b/changelogs/unreleased/52115-Link-button-in-markdown-editor-should-recognize-URLs.yml
@@ -0,0 +1,5 @@
+---
+title: Link button in markdown editor recognize URLs
+merge_request: 1983
+author: Johann Hubert Sonntagbauer
+type: changed
diff --git a/changelogs/unreleased/52384-api-filter-assignee-none-any.yml b/changelogs/unreleased/52384-api-filter-assignee-none-any.yml
new file mode 100644
index 00000000000..9acec04d946
--- /dev/null
+++ b/changelogs/unreleased/52384-api-filter-assignee-none-any.yml
@@ -0,0 +1,5 @@
+---
+title: Add None/Any option for assignee_id in Issues and Merge Requests API
+merge_request: 22598
+author: Heinrich Lee Yu
+type: added
diff --git a/changelogs/unreleased/52545-guest-create-issue-in-group-board.yml b/changelogs/unreleased/52545-guest-create-issue-in-group-board.yml
new file mode 100644
index 00000000000..5701e44eb32
--- /dev/null
+++ b/changelogs/unreleased/52545-guest-create-issue-in-group-board.yml
@@ -0,0 +1,5 @@
+---
+title: Always show new issue button in boards' Open list
+merge_request: 22557
+author: Heinrich Lee Yu
+type: fixed
diff --git a/changelogs/unreleased/53133-jobs-list.yml b/changelogs/unreleased/53133-jobs-list.yml
new file mode 100644
index 00000000000..2e13edc0e76
--- /dev/null
+++ b/changelogs/unreleased/53133-jobs-list.yml
@@ -0,0 +1,5 @@
+---
+title: Fix stage dropdown not rendering in different languages
+merge_request: 22604
+author:
+type: other
diff --git a/changelogs/unreleased/avoid-lock-when-introduce-new-failure-reason.yml b/changelogs/unreleased/avoid-lock-when-introduce-new-failure-reason.yml
new file mode 100644
index 00000000000..30b9ae032d4
--- /dev/null
+++ b/changelogs/unreleased/avoid-lock-when-introduce-new-failure-reason.yml
@@ -0,0 +1,5 @@
+---
+title: Support backward compatibility when introduce new failure reason
+merge_request: 22566
+author:
+type: changed
diff --git a/changelogs/unreleased/bvl-preload-user-status-for-events.yml b/changelogs/unreleased/bvl-preload-user-status-for-events.yml
new file mode 100644
index 00000000000..e13b19b19c1
--- /dev/null
+++ b/changelogs/unreleased/bvl-preload-user-status-for-events.yml
@@ -0,0 +1,5 @@
+---
+title: Show user status for label events in system notes
+merge_request: 22609
+author:
+type: fixed
diff --git a/doc/api/issues.md b/doc/api/issues.md
index cc1d6834a20..57e861bc62e 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -40,7 +40,7 @@ GET /issues?my_reaction_emoji=star
| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone. `Any+Milestone` lists all issues that have an assigned milestone |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned_to_me`. _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
+| `assignee_id` | integer | no | Return issues assigned to the given user `id`. `None` returns unassigned issues. `Any` returns issues with an assignee. _([Introduced][ce-13004] in GitLab 9.5)_ |
| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
| `iids[]` | Array[integer] | no | Return only the issues having the given `iid` |
| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
@@ -154,7 +154,7 @@ GET /groups/:id/issues?my_reaction_emoji=star
| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
+| `assignee_id` | integer | no | Return issues assigned to the given user `id`. `None` returns unassigned issues. `Any` returns issues with an assignee. _([Introduced][ce-13004] in GitLab 9.5)_ |
| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
@@ -268,7 +268,7 @@ GET /projects/:id/issues?my_reaction_emoji=star
| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
+| `assignee_id` | integer | no | Return issues assigned to the given user `id`. `None` returns unassigned issues. `Any` returns issues with an assignee. _([Introduced][ce-13004] in GitLab 9.5)_ |
| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 862ee398a84..0291b7e00c2 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -42,7 +42,7 @@ Parameters:
| `updated_before` | datetime | no | Return merge requests updated on or before the given time |
| `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead. |
| `author_id` | integer | no | Returns merge requests created by the given user `id`. Combine with `scope=all` or `scope=assigned_to_me` |
-| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id` |
+| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
| `source_branch` | string | no | Return merge requests with the given source branch |
| `target_branch` | string | no | Return merge requests with the given target branch |
@@ -166,7 +166,7 @@ Parameters:
| `updated_before` | datetime | no | Return merge requests updated on or before the given time |
| `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13060] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Returns merge requests created by the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ |
-| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ |
+| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. _([Introduced][ce-13060] in GitLab 9.5)_ |
| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
| `source_branch` | string | no | Return merge requests with the given source branch |
| `target_branch` | string | no | Return merge requests with the given target branch |
@@ -279,7 +279,7 @@ Parameters:
| `updated_before` | datetime | no | Return merge requests updated on or before the given time |
| `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> |
| `author_id` | integer | no | Returns merge requests created by the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ |
-| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ |
+| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. _([Introduced][ce-13060] in GitLab 9.5)_ |
| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ |
| `source_branch` | string | no | Return merge requests with the given source branch |
| `target_branch` | string | no | Return merge requests with the given target branch |
diff --git a/lib/api/helpers/custom_validators.rb b/lib/api/helpers/custom_validators.rb
index 23b1cd1ad45..1058f4e8a5e 100644
--- a/lib/api/helpers/custom_validators.rb
+++ b/lib/api/helpers/custom_validators.rb
@@ -10,8 +10,21 @@ module API
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:absence)
end
end
+
+ class IntegerNoneAny < Grape::Validations::Base
+ def validate_param!(attr_name, params)
+ value = params[attr_name]
+
+ return if value.is_a?(Integer) ||
+ [IssuableFinder::FILTER_NONE, IssuableFinder::FILTER_ANY].include?(value.to_s.downcase)
+
+ raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
+ message: "should be an integer, 'None' or 'Any'"
+ end
+ end
end
end
end
Grape::Validations.register_validator(:absence, ::API::Helpers::CustomValidators::Absence)
+Grape::Validations.register_validator(:integer_none_any, ::API::Helpers::CustomValidators::IntegerNoneAny)
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 25d78053c88..405fc30a2ed 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -40,7 +40,8 @@ module API
optional :updated_after, type: DateTime, desc: 'Return issues updated after the specified time'
optional :updated_before, type: DateTime, desc: 'Return issues updated before the specified time'
optional :author_id, type: Integer, desc: 'Return issues which are authored by the user with the given ID'
- optional :assignee_id, type: Integer, desc: 'Return issues which are assigned to the user with the given ID'
+ optional :assignee_id, types: [Integer, String], integer_none_any: true,
+ desc: 'Return issues which are assigned to the user with the given ID'
optional :scope, type: String, values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
desc: 'Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`'
optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji'
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 440d94ae186..a617efaaa4c 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -89,7 +89,8 @@ module API
optional :updated_before, type: DateTime, desc: 'Return merge requests updated before the specified time'
optional :view, type: String, values: %w[simple], desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request'
optional :author_id, type: Integer, desc: 'Return merge requests which are authored by the user with the given ID'
- optional :assignee_id, type: Integer, desc: 'Return merge requests which are assigned to the user with the given ID'
+ optional :assignee_id, types: [Integer, String], integer_none_any: true,
+ desc: 'Return merge requests which are assigned to the user with the given ID'
optional :scope, type: String, values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
desc: 'Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`'
optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji'
diff --git a/lib/api/runner.rb b/lib/api/runner.rb
index d8768a54986..2f15f3a7d76 100644
--- a/lib/api/runner.rb
+++ b/lib/api/runner.rb
@@ -142,8 +142,7 @@ module API
requires :id, type: Integer, desc: %q(Job's ID)
optional :trace, type: String, desc: %q(Job's full trace)
optional :state, type: String, desc: %q(Job's status: success, failed)
- optional :failure_reason, type: String, values: CommitStatus.failure_reasons.keys,
- desc: %q(Job's failure_reason)
+ optional :failure_reason, type: String, desc: %q(Job's failure_reason)
end
put '/:id' do
job = authenticate_job!
diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb
index c365988a100..98946e4287b 100644
--- a/spec/controllers/boards/issues_controller_spec.rb
+++ b/spec/controllers/boards/issues_controller_spec.rb
@@ -208,11 +208,22 @@ describe Boards::IssuesController do
end
end
- context 'with unauthorized user' do
- it 'returns a forbidden 403 response' do
- create_issue user: guest, board: board, list: list1, title: 'New issue'
+ context 'with guest user' do
+ context 'in open list' do
+ it 'returns a successful 200 response' do
+ open_list = board.lists.create(list_type: :backlog)
+ create_issue user: guest, board: board, list: open_list, title: 'New issue'
- expect(response).to have_gitlab_http_status(403)
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
+ context 'in label list' do
+ it 'returns a forbidden 403 response' do
+ create_issue user: guest, board: board, list: list1, title: 'New issue'
+
+ expect(response).to have_gitlab_http_status(403)
+ end
end
end
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index 0bf1ecbc433..164442a47f5 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -94,8 +94,14 @@ describe 'Issue Boards new issue', :js do
wait_for_requests
end
- it 'does not display new issue button' do
- expect(page).to have_selector('.issue-count-badge-add-button', count: 0)
+ it 'displays new issue button in open list' do
+ expect(first('.board')).to have_selector('.issue-count-badge-add-button', count: 1)
+ end
+
+ it 'does not display new issue button in label list' do
+ page.within('.board:nth-child(2)') do
+ expect(page).not_to have_selector('.issue-count-badge-add-button')
+ end
end
end
end
diff --git a/spec/features/issues/resource_label_events_spec.rb b/spec/features/issues/resource_label_events_spec.rb
index 40c452c991a..b0764db7751 100644
--- a/spec/features/issues/resource_label_events_spec.rb
+++ b/spec/features/issues/resource_label_events_spec.rb
@@ -7,6 +7,7 @@ describe 'List issue resource label events', :js do
let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project, author: user) }
let!(:label) { create(:label, project: project, title: 'foo') }
+ let!(:user_status) { create(:user_status, user: user) }
context 'when user displays the issue' do
let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue, note: 'some note') }
@@ -23,6 +24,12 @@ describe 'List issue resource label events', :js do
expect(find("#note_#{event.discussion_id}")).to have_content 'added foo label'
end
end
+
+ it 'shows the user status on the system note for the label' do
+ page.within("#note_#{event.discussion_id}") do
+ expect(page).to show_user_status user_status
+ end
+ end
end
context 'when user adds label to the issue' do
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 0689c843104..2f164ffa8b0 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -57,11 +57,37 @@ describe IssuesFinder do
end
context 'filtering by no assignee' do
- let(:params) { { assignee_id: 0 } }
+ let(:params) { { assignee_id: 'None' } }
- it 'returns issues not assign to any assignee' do
+ it 'returns issues not assigned to any assignee' do
expect(issues).to contain_exactly(issue4)
end
+
+ it 'returns issues not assigned to any assignee' do
+ params[:assignee_id] = 0
+
+ expect(issues).to contain_exactly(issue4)
+ end
+
+ it 'returns issues not assigned to any assignee' do
+ params[:assignee_id] = 'none'
+
+ expect(issues).to contain_exactly(issue4)
+ end
+ end
+
+ context 'filtering by any assignee' do
+ let(:params) { { assignee_id: 'Any' } }
+
+ it 'returns issues assigned to any assignee' do
+ expect(issues).to contain_exactly(issue1, issue2, issue3)
+ end
+
+ it 'returns issues assigned to any assignee' do
+ params[:assignee_id] = 'any'
+
+ expect(issues).to contain_exactly(issue1, issue2, issue3)
+ end
end
context 'filtering by group_id' do
diff --git a/spec/javascripts/jobs/components/sidebar_spec.js b/spec/javascripts/jobs/components/sidebar_spec.js
index 460a2e1b5da..424092d2d88 100644
--- a/spec/javascripts/jobs/components/sidebar_spec.js
+++ b/spec/javascripts/jobs/components/sidebar_spec.js
@@ -140,10 +140,11 @@ describe('Sidebar details block', () => {
});
describe('while fetching stages', () => {
- it('renders dropdown with More label', () => {
+ it('it does not render dropdown', () => {
+ store.dispatch('requestStages');
vm = mountComponentWithStore(SidebarComponent, { store });
- expect(vm.$el.querySelector('.js-selected-stage').textContent.trim()).toEqual('More');
+ expect(vm.$el.querySelector('.js-selected-stage')).toBeNull();
});
});
diff --git a/spec/javascripts/jobs/store/mutations_spec.js b/spec/javascripts/jobs/store/mutations_spec.js
index 4230a7c42cf..d7908efcf13 100644
--- a/spec/javascripts/jobs/store/mutations_spec.js
+++ b/spec/javascripts/jobs/store/mutations_spec.js
@@ -124,8 +124,8 @@ describe('Jobs Store Mutations', () => {
expect(stateCopy.job).toEqual({ id: 1312321 });
});
- it('sets selectedStage when the selectedStage is More', () => {
- expect(stateCopy.selectedStage).toEqual('More');
+ it('sets selectedStage when the selectedStage is empty', () => {
+ expect(stateCopy.selectedStage).toEqual('');
mutations[types.RECEIVE_JOB_SUCCESS](stateCopy, { id: 1312321, stage: 'deploy' });
expect(stateCopy.selectedStage).toEqual('deploy');
diff --git a/spec/javascripts/lib/utils/text_markdown_spec.js b/spec/javascripts/lib/utils/text_markdown_spec.js
index bb7a29fe30a..b9e805628f8 100644
--- a/spec/javascripts/lib/utils/text_markdown_spec.js
+++ b/spec/javascripts/lib/utils/text_markdown_spec.js
@@ -166,6 +166,33 @@ describe('init markdown', () => {
expect(textArea.selectionStart).toEqual(expectedText.lastIndexOf(select));
expect(textArea.selectionEnd).toEqual(expectedText.lastIndexOf(select) + select.length);
});
+
+ it('should support selected urls', () => {
+ const expectedUrl = 'http://www.gitlab.com';
+ const expectedSelectionText = 'text';
+ const expectedText = `text [${expectedSelectionText}](${expectedUrl}) text`;
+ const initialValue = `text ${expectedUrl} text`;
+
+ textArea.value = initialValue;
+ const selectedIndex = initialValue.indexOf(expectedUrl);
+ textArea.setSelectionRange(selectedIndex, selectedIndex + expectedUrl.length);
+
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag,
+ blockTag: null,
+ selected: expectedUrl,
+ wrap: false,
+ select,
+ });
+
+ expect(textArea.value).toEqual(expectedText);
+ expect(textArea.selectionStart).toEqual(expectedText.indexOf(expectedSelectionText, 1));
+ expect(textArea.selectionEnd).toEqual(
+ expectedText.indexOf(expectedSelectionText, 1) + expectedSelectionText.length,
+ );
+ });
});
});
});
diff --git a/spec/lib/api/helpers/custom_validators_spec.rb b/spec/lib/api/helpers/custom_validators_spec.rb
new file mode 100644
index 00000000000..41e6fb47b11
--- /dev/null
+++ b/spec/lib/api/helpers/custom_validators_spec.rb
@@ -0,0 +1,64 @@
+require 'spec_helper'
+
+describe API::Helpers::CustomValidators do
+ let(:scope) do
+ Struct.new(:opts) do
+ def full_name(attr_name)
+ attr_name
+ end
+ end
+ end
+
+ describe API::Helpers::CustomValidators::Absence do
+ subject do
+ described_class.new(['test'], {}, false, scope.new)
+ end
+
+ context 'empty param' do
+ it 'does not raise a validation error' do
+ expect_no_validation_error({})
+ end
+ end
+
+ context 'invalid parameters' do
+ it 'should raise a validation error' do
+ expect_validation_error({ 'test' => 'some_value' })
+ end
+ end
+ end
+
+ describe API::Helpers::CustomValidators::IntegerNoneAny do
+ subject do
+ described_class.new(['test'], {}, false, scope.new)
+ end
+
+ context 'valid parameters' do
+ it 'does not raise a validation error' do
+ expect_no_validation_error({ 'test' => 2 })
+ expect_no_validation_error({ 'test' => 100 })
+ expect_no_validation_error({ 'test' => 'None' })
+ expect_no_validation_error({ 'test' => 'Any' })
+ expect_no_validation_error({ 'test' => 'none' })
+ expect_no_validation_error({ 'test' => 'any' })
+ end
+ end
+
+ context 'invalid parameters' do
+ it 'should raise a validation error' do
+ expect_validation_error({ 'test' => 'some_other_string' })
+ end
+ end
+ end
+
+ def expect_no_validation_error(params)
+ expect { validate_test_param!(params) }.not_to raise_error
+ end
+
+ def expect_validation_error(params)
+ expect { validate_test_param!(params) }.to raise_error(Grape::Exceptions::Validation)
+ end
+
+ def validate_test_param!(params)
+ subject.validate_param!('test', params)
+ end
+end
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 9f6cf12f9a7..9cda39a569b 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -178,6 +178,24 @@ describe API::Issues do
expect(first_issue['id']).to eq(issue2.id)
end
+ it 'returns issues with no assignee' do
+ issue2 = create(:issue, author: user2, project: project)
+
+ get api('/issues', user), assignee_id: 'None', scope: 'all'
+
+ expect_paginated_array_response(size: 1)
+ expect(first_issue['id']).to eq(issue2.id)
+ end
+
+ it 'returns issues with any assignee' do
+ # This issue without assignee should not be returned
+ create(:issue, author: user2, project: project)
+
+ get api('/issues', user), assignee_id: 'Any', scope: 'all'
+
+ expect_paginated_array_response(size: 3)
+ end
+
it 'returns issues reacted by the authenticated user by the given emoji' do
issue2 = create(:issue, project: project, author: user, assignees: [user])
award_emoji = create(:award_emoji, awardable: issue2, user: user2, name: 'star')
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 07d19e3ad29..e4e0ca285e0 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -143,6 +143,23 @@ describe API::MergeRequests do
expect_response_ordered_exactly(merge_request3)
end
+ it 'returns an array of merge requests with no assignee' do
+ merge_request3 = create(:merge_request, :simple, author: user, source_project: project2, target_project: project2, source_branch: 'other-branch')
+
+ get api('/merge_requests', user), assignee_id: 'None', scope: :all
+
+ expect_response_ordered_exactly(merge_request3)
+ end
+
+ it 'returns an array of merge requests with any assignee' do
+ # This MR with no assignee should not be returned
+ create(:merge_request, :simple, author: user, source_project: project2, target_project: project2, source_branch: 'other-branch')
+
+ get api('/merge_requests', user), assignee_id: 'Any', scope: :all
+
+ expect_response_contain_exactly(merge_request, merge_request2, merge_request_closed, merge_request_merged, merge_request_locked)
+ end
+
it 'returns an array of merge requests assigned to me' do
merge_request3 = create(:merge_request, :simple, author: user, assignee: user2, source_project: project2, target_project: project2, source_branch: 'other-branch')
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index 43ceb332cfb..c0d5a3ad74b 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -797,6 +797,15 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
it { expect(job).to be_runner_system_failure }
end
+
+ context 'when failure_reason is unrecognized value' do
+ before do
+ update_job(state: 'failed', failure_reason: 'what_is_this')
+ job.reload
+ end
+
+ it { expect(job).to be_unknown_failure }
+ end
end
context 'when trace is given' do
diff --git a/spec/services/resource_events/merge_into_notes_service_spec.rb b/spec/services/resource_events/merge_into_notes_service_spec.rb
index 0d333d541c9..c76f6e6f77e 100644
--- a/spec/services/resource_events/merge_into_notes_service_spec.rb
+++ b/spec/services/resource_events/merge_into_notes_service_spec.rb
@@ -66,5 +66,14 @@ describe ResourceEvents::MergeIntoNotesService do
expect(notes.count).to eq 1
expect(notes.first.discussion_id).to eq event.discussion_id
end
+
+ it "preloads the note author's status" do
+ event = create_event(created_at: time)
+ create(:user_status, user: event.user)
+
+ notes = described_class.new(resource, user).execute
+
+ expect(notes.first.author.association(:status)).to be_loaded
+ end
end
end