diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-11 09:10:03 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-11 09:10:03 +0000 |
commit | e184bc1abfe4fe4fef8c25c0d2ccb4c0063e7d5e (patch) | |
tree | a3cb32545238fd46625485a482ef403de2d558b5 | |
parent | 281f1b6ba36531122463afd6869ca5dcdd0b0e35 (diff) | |
download | gitlab-ce-e184bc1abfe4fe4fef8c25c0d2ccb4c0063e7d5e.tar.gz |
Add latest changes from gitlab-org/gitlab@master
39 files changed, 668 insertions, 144 deletions
diff --git a/.gitlab/issue_templates/Feature proposal.md b/.gitlab/issue_templates/Feature proposal.md index 60e55d4bcfe..4e894b8ce80 100644 --- a/.gitlab/issue_templates/Feature proposal.md +++ b/.gitlab/issue_templates/Feature proposal.md @@ -87,18 +87,7 @@ In which enterprise tier should this feature go? See https://about.gitlab.com/ha ### Links / references -<!-- Label reminders - you should have one of each of the following labels if you can find the correct ones! - -Type - for example ~feature ~bug ~documentation ~meta /label ~"feature::addition" ~"feature::maintenance" ~tooling ~"tooling::pipelines" ~"tooling::workflow" per https://docs.gitlab.com/ee/development/contributing/issue_workflow.html#type-labels - -DevOps stage - for example ~"devops::secure" - -Group - for example ~"group::composition analysis" - -Category - for example ~"Category:Dependency Scanning" -<!-- Label reminders - you should have one of each of the following labels if you can figure out the correct ones! --> -/label ~"devops:: -/label ~"group:: -/label ~"Category: +<!-- Label reminders - you should have one of each of the following labels if you can figure out the correct ones --> +/label ~devops:: ~group: ~Category: /label ~feature diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION index 3500250a4b0..57807d6d0d0 100644 --- a/GITLAB_PAGES_VERSION +++ b/GITLAB_PAGES_VERSION @@ -1 +1 @@ -1.21.0 +1.22.0 diff --git a/app/assets/javascripts/vue_shared/components/gl_mentions.vue b/app/assets/javascripts/vue_shared/components/gl_mentions.vue index 1d8be1065e9..5d97d364e94 100644 --- a/app/assets/javascripts/vue_shared/components/gl_mentions.vue +++ b/app/assets/javascripts/vue_shared/components/gl_mentions.vue @@ -43,7 +43,12 @@ const autoCompleteMap = { const fullText = this.$slots.default?.[0]?.elm?.value; const selectionStart = this.$slots.default?.[0]?.elm?.selectionStart; - if (!this.assignees) { + // Need to check whether sidebar store assignees has been updated + // in the case where the assignees AJAX response comes after the user does @ autocomplete + const isAssigneesLengthSame = + this.assignees?.length === SidebarMediator.singleton?.store?.assignees?.length; + + if (!this.assignees || !isAssigneesLengthSame) { this.assignees = SidebarMediator.singleton?.store?.assignees?.map(assignee => assignee.username) || []; } diff --git a/app/models/resource_iteration_event.rb b/app/models/resource_iteration_event.rb new file mode 100644 index 00000000000..78d85ea8b95 --- /dev/null +++ b/app/models/resource_iteration_event.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class ResourceIterationEvent < ResourceTimeboxEvent + belongs_to :iteration +end diff --git a/app/models/resource_milestone_event.rb b/app/models/resource_milestone_event.rb index 36068cf508b..5fd71612de0 100644 --- a/app/models/resource_milestone_event.rb +++ b/app/models/resource_milestone_event.rb @@ -1,30 +1,17 @@ # frozen_string_literal: true -class ResourceMilestoneEvent < ResourceEvent +class ResourceMilestoneEvent < ResourceTimeboxEvent include IgnorableColumns - include IssueResourceEvent - include MergeRequestResourceEvent belongs_to :milestone - validate :exactly_one_issuable - scope :include_relations, -> { includes(:user, milestone: [:project, :group]) } - enum action: { - add: 1, - remove: 2 - } - # state is used for issue and merge request states. enum state: Issue.available_states.merge(MergeRequest.available_states) ignore_columns %i[reference reference_html cached_markdown_version], remove_with: '13.1', remove_after: '2020-06-22' - def self.issuable_attrs - %i(issue merge_request).freeze - end - def milestone_title milestone&.title end @@ -32,8 +19,4 @@ class ResourceMilestoneEvent < ResourceEvent def milestone_parent milestone&.parent end - - def issuable - issue || merge_request - end end diff --git a/app/models/resource_timebox_event.rb b/app/models/resource_timebox_event.rb new file mode 100644 index 00000000000..44f48915425 --- /dev/null +++ b/app/models/resource_timebox_event.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class ResourceTimeboxEvent < ResourceEvent + self.abstract_class = true + + include IssueResourceEvent + include MergeRequestResourceEvent + + validate :exactly_one_issuable + + enum action: { + add: 1, + remove: 2 + } + + def self.issuable_attrs + %i(issue merge_request).freeze + end + + def issuable + issue || merge_request + end +end diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb index fa86531018c..140420a32bd 100644 --- a/app/services/boards/issues/list_service.rb +++ b/app/services/boards/issues/list_service.rb @@ -45,6 +45,12 @@ module Boards # rubocop: enable CodeReuse/ActiveRecord def filter(issues) + # when grouping board issues by epics (used in board swimlanes) + # we need to get all issues in the board + # TODO: ignore hidden columns - + # https://gitlab.com/gitlab-org/gitlab/-/issues/233870 + return issues if params[:all_lists] + issues = without_board_labels(issues) unless list&.movable? || list&.closed? issues = with_list_label(issues) if list&.label? issues @@ -87,6 +93,8 @@ module Boards end def set_state + return if params[:all_lists] + params[:state] = list && list.closed? ? 'closed' : 'opened' end diff --git a/app/services/submit_usage_ping_service.rb b/app/services/submit_usage_ping_service.rb index 2846dd3a6fe..357a3fd7b7a 100644 --- a/app/services/submit_usage_ping_service.rb +++ b/app/services/submit_usage_ping_service.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true class SubmitUsagePingService - URL = 'https://version.gitlab.com/usage_data' + PRODUCTION_URL = 'https://version.gitlab.com/usage_data' + STAGING_URL = 'https://gitlab-services-version-gitlab-com-staging.gs-staging.gitlab.org/usage_data' METRICS = %w[leader_issues instance_issues percentage_issues leader_notes instance_notes percentage_notes leader_milestones instance_milestones percentage_milestones @@ -23,7 +24,7 @@ class SubmitUsagePingService raise SubmissionError.new('Usage data is blank') if payload.blank? response = Gitlab::HTTP.post( - URL, + url, body: payload, allow_local_requests: true, headers: { 'Content-type' => 'application/json' } @@ -45,4 +46,13 @@ class SubmitUsagePingService metrics.slice(*METRICS) ) end + + # See https://gitlab.com/gitlab-org/gitlab/-/issues/233615 for details + def url + if Rails.env.production? + PRODUCTION_URL + else + STAGING_URL + end + end end diff --git a/app/views/projects/merge_requests/_how_to_merge.html.haml b/app/views/projects/merge_requests/_how_to_merge.html.haml index a2da0e707d3..df81e608c3e 100644 --- a/app/views/projects/merge_requests/_how_to_merge.html.haml +++ b/app/views/projects/merge_requests/_how_to_merge.html.haml @@ -32,12 +32,12 @@ - if @merge_request.for_fork? :preserve git fetch origin - git checkout "origin/#{h @merge_request.target_branch}" + git checkout "#{h @merge_request.target_branch}" git merge --no-ff "#{h @merge_request.source_project_path}-#{h @merge_request.source_branch}" - else :preserve git fetch origin - git checkout "origin/#{h @merge_request.target_branch}" + git checkout "#{h @merge_request.target_branch}" git merge --no-ff "#{h @merge_request.source_branch}" %p %strong Step 4. diff --git a/changelogs/unreleased/bump-gitlab-pages-1-22-0.yml b/changelogs/unreleased/bump-gitlab-pages-1-22-0.yml new file mode 100644 index 00000000000..30e25aa7ce9 --- /dev/null +++ b/changelogs/unreleased/bump-gitlab-pages-1-22-0.yml @@ -0,0 +1,5 @@ +--- +title: Upgrade Pages to 1.22.0 +merge_request: 39172 +author: +type: added diff --git a/changelogs/unreleased/create-resource-iteration-events-table.yml b/changelogs/unreleased/create-resource-iteration-events-table.yml new file mode 100644 index 00000000000..54f0e1a83e7 --- /dev/null +++ b/changelogs/unreleased/create-resource-iteration-events-table.yml @@ -0,0 +1,5 @@ +--- +title: Add DB table and model to track changes of the iterations on issues +merge_request: 37617 +author: +type: added diff --git a/db/migrate/20200722131241_create_resource_iteration_events_table.rb b/db/migrate/20200722131241_create_resource_iteration_events_table.rb new file mode 100644 index 00000000000..619faeaaadf --- /dev/null +++ b/db/migrate/20200722131241_create_resource_iteration_events_table.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class CreateResourceIterationEventsTable < ActiveRecord::Migration[6.0] + DOWNTIME = false + + def change + create_table :resource_iteration_events do |t| + t.bigint :user_id, null: false, index: { name: 'index_resource_iteration_events_on_user_id' } + t.bigint :issue_id, null: true, index: { name: 'index_resource_iteration_events_on_issue_id' } + t.bigint :merge_request_id, null: true, index: { name: 'index_resource_iteration_events_on_merge_request_id' } + t.bigint :iteration_id, index: { name: 'index_resource_iteration_events_on_iteration_id' } + t.datetime_with_timezone :created_at, null: false + t.integer :action, limit: 2, null: false + end + end +end diff --git a/db/migrate/20200722132040_add_users_fk_to_resource_iteration_events_table.rb b/db/migrate/20200722132040_add_users_fk_to_resource_iteration_events_table.rb new file mode 100644 index 00000000000..e28405be53d --- /dev/null +++ b/db/migrate/20200722132040_add_users_fk_to_resource_iteration_events_table.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddUsersFkToResourceIterationEventsTable < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def up + with_lock_retries do + add_foreign_key :resource_iteration_events, :users, column: :user_id, on_delete: :nullify # rubocop:disable Migration/AddConcurrentForeignKey + end + end + + def down + with_lock_retries do + remove_foreign_key :resource_iteration_events, column: :user_id + end + end +end diff --git a/db/migrate/20200722132540_add_issues_fk_to_resource_iteration_events_table.rb b/db/migrate/20200722132540_add_issues_fk_to_resource_iteration_events_table.rb new file mode 100644 index 00000000000..adb10aaa707 --- /dev/null +++ b/db/migrate/20200722132540_add_issues_fk_to_resource_iteration_events_table.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddIssuesFkToResourceIterationEventsTable < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def up + with_lock_retries do + add_foreign_key :resource_iteration_events, :issues, column: :issue_id, on_delete: :cascade # rubocop:disable Migration/AddConcurrentForeignKey + end + end + + def down + with_lock_retries do + remove_foreign_key :resource_iteration_events, column: :issue_id + end + end +end diff --git a/db/migrate/20200722133040_add_merge_requests_fk_to_resource_iteration_events_table.rb b/db/migrate/20200722133040_add_merge_requests_fk_to_resource_iteration_events_table.rb new file mode 100644 index 00000000000..8b1859bb253 --- /dev/null +++ b/db/migrate/20200722133040_add_merge_requests_fk_to_resource_iteration_events_table.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddMergeRequestsFkToResourceIterationEventsTable < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def up + with_lock_retries do + add_foreign_key :resource_iteration_events, :merge_requests, column: :merge_request_id, on_delete: :cascade # rubocop:disable Migration/AddConcurrentForeignKey + end + end + + def down + with_lock_retries do + remove_foreign_key :resource_iteration_events, column: :merge_request_id + end + end +end diff --git a/db/migrate/20200722133540_add_iterations_fk_to_resource_iteration_events_table.rb b/db/migrate/20200722133540_add_iterations_fk_to_resource_iteration_events_table.rb new file mode 100644 index 00000000000..b42c29a0634 --- /dev/null +++ b/db/migrate/20200722133540_add_iterations_fk_to_resource_iteration_events_table.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddIterationsFkToResourceIterationEventsTable < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def up + with_lock_retries do + add_foreign_key :resource_iteration_events, :sprints, column: :iteration_id, on_delete: :cascade # rubocop:disable Migration/AddConcurrentForeignKey + end + end + + def down + with_lock_retries do + remove_foreign_key :resource_iteration_events, column: :iteration_id + end + end +end diff --git a/db/schema_migrations/20200722131241 b/db/schema_migrations/20200722131241 new file mode 100644 index 00000000000..327486a7367 --- /dev/null +++ b/db/schema_migrations/20200722131241 @@ -0,0 +1 @@ +5fc97e904844b43a331246c68b1d0a879f683d5b36fab6cd024f417922259864
\ No newline at end of file diff --git a/db/schema_migrations/20200722132040 b/db/schema_migrations/20200722132040 new file mode 100644 index 00000000000..7993eaafe02 --- /dev/null +++ b/db/schema_migrations/20200722132040 @@ -0,0 +1 @@ +7b33a7f96ea02e3d5510cb6a6d75a9c753c1bd24c93d9995f91596d6b1a44592
\ No newline at end of file diff --git a/db/schema_migrations/20200722132540 b/db/schema_migrations/20200722132540 new file mode 100644 index 00000000000..683c61dd661 --- /dev/null +++ b/db/schema_migrations/20200722132540 @@ -0,0 +1 @@ +543ebc8ab7fad96aeb5a8618bc954babb0eed1d252fe490aa6fab9b8e80ffbf1
\ No newline at end of file diff --git a/db/schema_migrations/20200722133040 b/db/schema_migrations/20200722133040 new file mode 100644 index 00000000000..602ba562760 --- /dev/null +++ b/db/schema_migrations/20200722133040 @@ -0,0 +1 @@ +4a6a322cf3f6e1622d3244f6faa99a1befcb8c626a8fea36b09d1910b770b88c
\ No newline at end of file diff --git a/db/schema_migrations/20200722133540 b/db/schema_migrations/20200722133540 new file mode 100644 index 00000000000..1a0385bcaea --- /dev/null +++ b/db/schema_migrations/20200722133540 @@ -0,0 +1 @@ +0307eb0f5082a5da84f5e94084b677d13e03f9cd5011efe3dc7e25645a00082d
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 7fd390b6ea4..86fa7a40734 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -15063,6 +15063,25 @@ CREATE SEQUENCE public.requirements_management_test_reports_id_seq ALTER SEQUENCE public.requirements_management_test_reports_id_seq OWNED BY public.requirements_management_test_reports.id; +CREATE TABLE public.resource_iteration_events ( + id bigint NOT NULL, + user_id bigint NOT NULL, + issue_id bigint, + merge_request_id bigint, + iteration_id bigint, + created_at timestamp with time zone NOT NULL, + action smallint NOT NULL +); + +CREATE SEQUENCE public.resource_iteration_events_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE public.resource_iteration_events_id_seq OWNED BY public.resource_iteration_events.id; + CREATE TABLE public.resource_label_events ( id bigint NOT NULL, action integer NOT NULL, @@ -17214,6 +17233,8 @@ ALTER TABLE ONLY public.requirements ALTER COLUMN id SET DEFAULT nextval('public ALTER TABLE ONLY public.requirements_management_test_reports ALTER COLUMN id SET DEFAULT nextval('public.requirements_management_test_reports_id_seq'::regclass); +ALTER TABLE ONLY public.resource_iteration_events ALTER COLUMN id SET DEFAULT nextval('public.resource_iteration_events_id_seq'::regclass); + ALTER TABLE ONLY public.resource_label_events ALTER COLUMN id SET DEFAULT nextval('public.resource_label_events_id_seq'::regclass); ALTER TABLE ONLY public.resource_milestone_events ALTER COLUMN id SET DEFAULT nextval('public.resource_milestone_events_id_seq'::regclass); @@ -18440,6 +18461,9 @@ ALTER TABLE ONLY public.requirements_management_test_reports ALTER TABLE ONLY public.requirements ADD CONSTRAINT requirements_pkey PRIMARY KEY (id); +ALTER TABLE ONLY public.resource_iteration_events + ADD CONSTRAINT resource_iteration_events_pkey PRIMARY KEY (id); + ALTER TABLE ONLY public.resource_label_events ADD CONSTRAINT resource_label_events_pkey PRIMARY KEY (id); @@ -20526,6 +20550,14 @@ CREATE INDEX index_requirements_on_title_trigram ON public.requirements USING gi CREATE INDEX index_requirements_on_updated_at ON public.requirements USING btree (updated_at); +CREATE INDEX index_resource_iteration_events_on_issue_id ON public.resource_iteration_events USING btree (issue_id); + +CREATE INDEX index_resource_iteration_events_on_iteration_id ON public.resource_iteration_events USING btree (iteration_id); + +CREATE INDEX index_resource_iteration_events_on_merge_request_id ON public.resource_iteration_events USING btree (merge_request_id); + +CREATE INDEX index_resource_iteration_events_on_user_id ON public.resource_iteration_events USING btree (user_id); + CREATE INDEX index_resource_label_events_issue_id_label_id_action ON public.resource_label_events USING btree (issue_id, label_id, action); CREATE INDEX index_resource_label_events_on_epic_id ON public.resource_label_events USING btree (epic_id); @@ -22275,6 +22307,9 @@ ALTER TABLE ONLY public.security_scans ALTER TABLE ONLY public.merge_request_diff_files ADD CONSTRAINT fk_rails_501aa0a391 FOREIGN KEY (merge_request_diff_id) REFERENCES public.merge_request_diffs(id) ON DELETE CASCADE; +ALTER TABLE ONLY public.resource_iteration_events + ADD CONSTRAINT fk_rails_501fa15d69 FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE SET NULL; + ALTER TABLE ONLY public.status_page_settings ADD CONSTRAINT fk_rails_506e5ba391 FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE; @@ -22416,6 +22451,9 @@ ALTER TABLE ONLY public.web_hook_logs ALTER TABLE ONLY public.jira_imports ADD CONSTRAINT fk_rails_675d38c03b FOREIGN KEY (label_id) REFERENCES public.labels(id) ON DELETE SET NULL; +ALTER TABLE ONLY public.resource_iteration_events + ADD CONSTRAINT fk_rails_6830c13ac1 FOREIGN KEY (merge_request_id) REFERENCES public.merge_requests(id) ON DELETE CASCADE; + ALTER TABLE ONLY public.geo_hashed_storage_migrated_events ADD CONSTRAINT fk_rails_687ed7d7c5 FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE; @@ -22713,6 +22751,9 @@ ALTER TABLE ONLY public.x509_commit_signatures ALTER TABLE ONLY public.ci_build_trace_sections ADD CONSTRAINT fk_rails_ab7c104e26 FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE; +ALTER TABLE ONLY public.resource_iteration_events + ADD CONSTRAINT fk_rails_abf5d4affa FOREIGN KEY (issue_id) REFERENCES public.issues(id) ON DELETE CASCADE; + ALTER TABLE ONLY public.clusters ADD CONSTRAINT fk_rails_ac3a663d79 FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE SET NULL; @@ -22869,6 +22910,9 @@ ALTER TABLE ONLY public.issue_tracker_data ALTER TABLE ONLY public.resource_milestone_events ADD CONSTRAINT fk_rails_cedf8cce4d FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE SET NULL; +ALTER TABLE ONLY public.resource_iteration_events + ADD CONSTRAINT fk_rails_cee126f66c FOREIGN KEY (iteration_id) REFERENCES public.sprints(id) ON DELETE CASCADE; + ALTER TABLE ONLY public.epic_metrics ADD CONSTRAINT fk_rails_d071904753 FOREIGN KEY (epic_id) REFERENCES public.epics(id) ON DELETE CASCADE; diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index 85633512323..61adc3283b9 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -998,6 +998,36 @@ type Board { assignee: User """ + Epics associated with board issues. + """ + epics( + """ + Returns the elements in the list that come after the specified cursor. + """ + after: String + + """ + Returns the elements in the list that come before the specified cursor. + """ + before: String + + """ + Returns the first _n_ elements from the list. + """ + first: Int + + """ + Filters applied when selecting issues on the board + """ + issueFilters: BoardEpicIssueInput + + """ + Returns the last _n_ elements from the list. + """ + last: Int + ): EpicConnection + + """ Whether or not backlog list is hidden. """ hideBacklogList: Boolean @@ -1093,6 +1123,48 @@ type BoardEdge { node: Board } +input BoardEpicIssueInput { + """ + Username of a user assigned to issues + """ + assigneeUsername: [String] + + """ + Username of the issues author + """ + authorUsername: String + + """ + Epic ID applied to issues + """ + epicId: String + + """ + Label applied to issues + """ + labelName: [String] + + """ + Milestone applied to issues + """ + milestoneTitle: String + + """ + Reaction emoji applied to issues + """ + myReactionEmoji: String + + """ + Release applied to issues + """ + releaseTag: String + + """ + Weight applied to issues + """ + weight: String +} + """ Represents a list for an issue board """ diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index 6912f78ad0a..f61025aedc7 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -2676,6 +2676,69 @@ "deprecationReason": null }, { + "name": "epics", + "description": "Epics associated with board issues.", + "args": [ + { + "name": "issueFilters", + "description": "Filters applied when selecting issues on the board", + "type": { + "kind": "INPUT_OBJECT", + "name": "BoardEpicIssueInput", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "after", + "description": "Returns the elements in the list that come after the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "before", + "description": "Returns the elements in the list that come before the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "first", + "description": "Returns the first _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "last", + "description": "Returns the last _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "EpicConnection", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "hideBacklogList", "description": "Whether or not backlog list is hidden.", "args": [ @@ -2947,6 +3010,105 @@ "possibleTypes": null }, { + "kind": "INPUT_OBJECT", + "name": "BoardEpicIssueInput", + "description": null, + "fields": null, + "inputFields": [ + { + "name": "labelName", + "description": "Label applied to issues", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "milestoneTitle", + "description": "Milestone applied to issues", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "assigneeUsername", + "description": "Username of a user assigned to issues", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "authorUsername", + "description": "Username of the issues author", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "releaseTag", + "description": "Release applied to issues", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "epicId", + "description": "Epic ID applied to issues", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "myReactionEmoji", + "description": "Reaction emoji applied to issues", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "weight", + "description": "Weight applied to issues", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { "kind": "OBJECT", "name": "BoardList", "description": "Represents a list for an issue board", diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb index d662d846828..e0fcb9b0599 100644 --- a/qa/qa/git/repository.rb +++ b/qa/qa/git/repository.rb @@ -102,6 +102,10 @@ module QA git_lfs_track_result.to_s + git_add_result.to_s end + def add_tag(tag_name) + run("git tag #{tag_name}").to_s + end + def delete_tag(tag_name) run(%Q{git push origin --delete #{tag_name}}, max_attempts: 3).to_s end @@ -122,6 +126,10 @@ module QA run("git push --all").to_s end + def push_tags_and_branches(branches) + run("git push --tags origin #{branches.join(' ')}").to_s + end + def merge(branch) run("git merge #{branch}") end diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb index f4fe43aa3ab..eba8ada50ab 100644 --- a/qa/qa/resource/project.rb +++ b/qa/qa/resource/project.rb @@ -103,6 +103,20 @@ module QA response.any? { |file| file[:path] == file_path } end + def has_branches?(branches) + branches.all? do |branch| + response = get(Runtime::API::Request.new(api_client, "#{api_repository_branches_path}/#{branch}").url) + response.code == HTTP_STATUS_OK + end + end + + def has_tags?(tags) + tags.all? do |tag| + response = get(Runtime::API::Request.new(api_client, "#{api_repository_tags_path}/#{tag}").url) + response.code == HTTP_STATUS_OK + end + end + def api_get_path "/projects/#{CGI.escape(path_with_namespace)}" end @@ -131,6 +145,10 @@ module QA "#{api_get_path}/repository/branches" end + def api_repository_tags_path + "#{api_get_path}/repository/tags" + end + def api_repository_tree_path "#{api_get_path}/repository/tree" end @@ -212,6 +230,10 @@ module QA parse_body(get(Runtime::API::Request.new(api_client, api_repository_branches_path).url)) end + def repository_tags + parse_body(get(Runtime::API::Request.new(api_client, api_repository_tags_path).url)) + end + def repository_tree parse_body(get(Runtime::API::Request.new(api_client, api_repository_tree_path).url)) end diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb index a002779d7d9..524cc3fc8a1 100644 --- a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb @@ -16,7 +16,7 @@ module QA Flow::Login.sign_in end - it 'creates a basic merge request' do + it 'creates a basic merge request', :smoke do Resource::MergeRequest.fabricate_via_browser_ui! do |merge_request| merge_request.project = project merge_request.title = merge_request_title diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb index c01558d3702..ceacc73e3c3 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Create' do - describe 'Git push over HTTP', :ldap_no_tls do + describe 'Git push over HTTP', :ldap_no_tls, :smoke do it 'user using a personal access token pushes code to the repository' do Flow::Login.sign_in diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb index b918b2ff268..2baf1e1d8fd 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb @@ -3,7 +3,7 @@ module QA RSpec.describe 'Create' do describe 'Git push over HTTP', :ldap_no_tls do - it 'user pushes code to the repository' do + it 'user pushes code to the repository', :smoke do Flow::Login.sign_in Resource::Repository::ProjectPush.fabricate! do |push| diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb new file mode 100644 index 00000000000..d0f0cabbbca --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Create' do + describe 'SSH key support' do + # Note: If you run these tests against GDK make sure you've enabled sshd + # See: https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/run_qa_against_gdk.md + + let(:project) do + Resource::Project.fabricate! do |project| + project.name = 'ssh-tests' + end + end + + before(:context) do + @key = Resource::SSHKey.fabricate_via_api! do |resource| + resource.title = "key for ssh tests #{Time.now.to_f}" + end + end + + after(:context) do + @key.remove_via_api! + end + + before do + Flow::Login.sign_in + end + + it 'pushes code to the repository via SSH', :smoke do + Resource::Repository::ProjectPush.fabricate! do |push| + push.project = project + push.ssh_key = @key + push.file_name = 'README.md' + push.file_content = '# Test Use SSH Key' + push.commit_message = 'Add README.md' + end.project.visit! + + Page::Project::Show.perform do |project| + expect(project).to have_file('README.md') + expect(project).to have_readme_content('Test Use SSH Key') + end + end + + it 'pushes multiple branches and tags together', :smoke do + branches = [] + tags = [] + Git::Repository.perform do |repository| + repository.uri = project.repository_ssh_location.uri + repository.use_ssh_key(@key) + repository.clone + repository.configure_identity('GitLab QA', 'root@gitlab.com') + 1.upto(3) do |i| + branches << "branch#{i}" + tags << "tag#{i}" + repository.checkout("branch#{i}", new_branch: true) + repository.commit_file("file#{i}", SecureRandom.random_bytes(10000), "Add file#{i}") + repository.add_tag("tag#{i}") + end + repository.push_tags_and_branches(branches) + end + + expect(project).to have_branches(branches) + expect(project).to have_tags(tags) + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb deleted file mode 100644 index e91717b0f5f..00000000000 --- a/qa/qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -module QA - RSpec.describe 'Create' do - describe 'SSH key support' do - # Note: If you run this test against GDK make sure you've enabled sshd - # See: https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/run_qa_against_gdk.md - - let(:key_title) { "key for ssh tests #{Time.now.to_f}" } - - it 'user adds an ssh key and pushes code to the repository' do - Flow::Login.sign_in - - key = Resource::SSHKey.fabricate_via_api! do |resource| - resource.title = key_title - end - - Resource::Repository::ProjectPush.fabricate! do |push| - push.ssh_key = key - push.file_name = 'README.md' - push.file_content = '# Test Use SSH Key' - push.commit_message = 'Add README.md' - end.project.visit! - - expect(page).to have_content('README.md') - expect(page).to have_content('Test Use SSH Key') - - Page::Main::Menu.perform(&:click_settings_link) - Page::Profile::Menu.perform(&:click_ssh_keys) - - Page::Profile::SSHKeys.perform do |ssh_keys| - ssh_keys.remove_key(key_title) - end - end - end - end -end diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index a2a95124b31..bbba17eddb7 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -165,6 +165,9 @@ RSpec.describe 'Database schema' do context 'for enums' do ApplicationRecord.descendants.each do |model| + # skip model if it is an abstract class as it would not have an associated DB table + next if model.abstract_class? + describe model do let(:ignored_enums) { ignored_limit_enums(model.name) } let(:enums) { model.defined_enums.keys - ignored_enums } diff --git a/spec/factories/resource_iteration_event.rb b/spec/factories/resource_iteration_event.rb new file mode 100644 index 00000000000..85e7320f7a7 --- /dev/null +++ b/spec/factories/resource_iteration_event.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :resource_iteration_event do + issue { merge_request.nil? ? create(:issue) : nil } + merge_request { nil } + iteration + action { :add } + user { issue&.author || merge_request&.author || create(:user) } + end +end diff --git a/spec/models/resource_iteration_event_spec.rb b/spec/models/resource_iteration_event_spec.rb new file mode 100644 index 00000000000..fe1310d7bf1 --- /dev/null +++ b/spec/models/resource_iteration_event_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ResourceIterationEvent, type: :model do + it_behaves_like 'a resource event' + it_behaves_like 'a resource event for issues' + it_behaves_like 'a resource event for merge requests' + + it_behaves_like 'having unique enum values' + it_behaves_like 'timebox resource event validations' + it_behaves_like 'timebox resource event actions' + + describe 'associations' do + it { is_expected.to belong_to(:iteration) } + end +end diff --git a/spec/models/resource_milestone_event_spec.rb b/spec/models/resource_milestone_event_spec.rb index 76ffb358d80..0a5292b2d16 100644 --- a/spec/models/resource_milestone_event_spec.rb +++ b/spec/models/resource_milestone_event_spec.rb @@ -8,77 +8,14 @@ RSpec.describe ResourceMilestoneEvent, type: :model do it_behaves_like 'a resource event for merge requests' it_behaves_like 'having unique enum values' + it_behaves_like 'timebox resource event validations' + it_behaves_like 'timebox resource event states' + it_behaves_like 'timebox resource event actions' describe 'associations' do it { is_expected.to belong_to(:milestone) } end - describe 'validations' do - context 'when issue and merge_request are both nil' do - subject { build(described_class.name.underscore.to_sym, issue: nil, merge_request: nil) } - - it { is_expected.not_to be_valid } - end - - context 'when issue and merge_request are both set' do - subject { build(described_class.name.underscore.to_sym, issue: build(:issue), merge_request: build(:merge_request)) } - - it { is_expected.not_to be_valid } - end - - context 'when issue is set' do - subject { create(described_class.name.underscore.to_sym, issue: create(:issue), merge_request: nil) } - - it { is_expected.to be_valid } - end - - context 'when merge_request is set' do - subject { create(described_class.name.underscore.to_sym, issue: nil, merge_request: create(:merge_request)) } - - it { is_expected.to be_valid } - end - end - - describe 'states' do - [Issue, MergeRequest].each do |klass| - klass.available_states.each do |state| - it "supports state #{state.first} for #{klass.name.underscore}" do - model = create(klass.name.underscore, state: state[0]) - key = model.class.name.underscore - event = build(described_class.name.underscore.to_sym, key => model, state: model.state) - - expect(event.state).to eq(state[0]) - end - end - end - end - - shared_examples 'a milestone action queryable resource event' do |expected_results_for_actions| - [Issue, MergeRequest].each do |klass| - expected_results_for_actions.each do |action, expected_result| - it "is #{expected_result} for action #{action} on #{klass.name.underscore}" do - model = create(klass.name.underscore) - key = model.class.name.underscore - event = build(described_class.name.underscore.to_sym, key => model, action: action) - - expect(event.send(query_method)).to eq(expected_result) - end - end - end - end - - describe '#added?' do - it_behaves_like 'a milestone action queryable resource event', { add: true, remove: false } do - let(:query_method) { :add? } - end - end - - describe '#removed?' do - it_behaves_like 'a milestone action queryable resource event', { add: false, remove: true } do - let(:query_method) { :remove? } - end - end - describe '#milestone_title' do let(:milestone) { create(:milestone, title: 'v2.3') } let(:event) { create(:resource_milestone_event, milestone: milestone) } diff --git a/spec/services/submit_usage_ping_service_spec.rb b/spec/services/submit_usage_ping_service_spec.rb index e2ebf20c331..6031a71deef 100644 --- a/spec/services/submit_usage_ping_service_spec.rb +++ b/spec/services/submit_usage_ping_service_spec.rb @@ -164,7 +164,7 @@ RSpec.describe SubmitUsagePingService do end def stub_response(body:, status: 201) - stub_full_request('https://version.gitlab.com/usage_data', method: :post) + stub_full_request(SubmitUsagePingService::STAGING_URL, method: :post) .to_return( headers: { 'Content-Type' => 'application/json' }, body: body.to_json, diff --git a/spec/support/shared_examples/resource_events.rb b/spec/support/shared_examples/models/resource_event_shared_examples.rb index c0158f9b24b..c0158f9b24b 100644 --- a/spec/support/shared_examples/resource_events.rb +++ b/spec/support/shared_examples/models/resource_event_shared_examples.rb diff --git a/spec/support/shared_examples/models/resource_timebox_event_shared_examples.rb b/spec/support/shared_examples/models/resource_timebox_event_shared_examples.rb new file mode 100644 index 00000000000..07552b62cdd --- /dev/null +++ b/spec/support/shared_examples/models/resource_timebox_event_shared_examples.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.shared_examples 'timebox resource event validations' do + describe 'validations' do + context 'when issue and merge_request are both nil' do + subject { build(described_class.name.underscore.to_sym, issue: nil, merge_request: nil) } + + it { is_expected.not_to be_valid } + end + + context 'when issue and merge_request are both set' do + subject { build(described_class.name.underscore.to_sym, issue: build(:issue), merge_request: build(:merge_request)) } + + it { is_expected.not_to be_valid } + end + + context 'when issue is set' do + subject { create(described_class.name.underscore.to_sym, issue: create(:issue), merge_request: nil) } + + it { is_expected.to be_valid } + end + + context 'when merge_request is set' do + subject { create(described_class.name.underscore.to_sym, issue: nil, merge_request: create(:merge_request)) } + + it { is_expected.to be_valid } + end + end +end + +RSpec.shared_examples 'timebox resource event states' do + describe 'states' do + [Issue, MergeRequest].each do |klass| + klass.available_states.each do |state| + it "supports state #{state.first} for #{klass.name.underscore}" do + model = create(klass.name.underscore, state: state[0]) + key = model.class.name.underscore + event = build(described_class.name.underscore.to_sym, key => model, state: model.state) + + expect(event.state).to eq(state[0]) + end + end + end + end +end + +RSpec.shared_examples 'queryable timebox action resource event' do |expected_results_for_actions| + [Issue, MergeRequest].each do |klass| + expected_results_for_actions.each do |action, expected_result| + it "is #{expected_result} for action #{action} on #{klass.name.underscore}" do + model = build(klass.name.underscore) + key = model.class.name.underscore + event = build(described_class.name.underscore.to_sym, key => model, action: action) + + expect(event.send(query_method)).to eq(expected_result) + end + end + end +end + +RSpec.shared_examples 'timebox resource event actions' do + describe '#added?' do + it_behaves_like 'queryable timebox action resource event', { add: true, remove: false } do + let(:query_method) { :add? } + end + end + + describe '#removed?' do + it_behaves_like 'queryable timebox action resource event', { add: false, remove: true } do + let(:query_method) { :remove? } + end + end +end diff --git a/spec/support/shared_examples/services/boards/issues_list_service_shared_examples.rb b/spec/support/shared_examples/services/boards/issues_list_service_shared_examples.rb index 2fcf439a6c6..06e2b715e6d 100644 --- a/spec/support/shared_examples/services/boards/issues_list_service_shared_examples.rb +++ b/spec/support/shared_examples/services/boards/issues_list_service_shared_examples.rb @@ -80,4 +80,17 @@ RSpec.shared_examples 'issues list service' do expect { service.execute }.to raise_error(ActiveRecord::RecordNotFound) end end + + context 'when :all_lists is used' do + it 'returns issues from all lists' do + params = { board_id: board.id, all_lists: true } + + issues = described_class.new(parent, user, params).execute + + expected = [opened_issue2, reopened_issue1, opened_issue1, list1_issue1, + list1_issue2, list1_issue3, list2_issue1, closed_issue1, + closed_issue2, closed_issue3, closed_issue4, closed_issue5] + expect(issues).to match_array(expected) + end + end end |