diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-25 18:10:17 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-25 18:10:17 +0300 |
commit | 11a29f1f026ecd0c466625782af00a14889a2f91 (patch) | |
tree | c4b893017684aa7ebabbefb695674f7f448038fc | |
parent | c1892df2eb57a33cc3a751378e40cb49823fee82 (diff) |
Add latest changes from gitlab-org/gitlab@master
42 files changed, 926 insertions, 72 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index 7a5516338e8..2cf8db04bb9 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -165,12 +165,15 @@ /.gitlab/ci/ @gl-quality/eng-prod /.gitlab/ci/docs.gitlab-ci.yml @gl-quality/eng-prod @gl-docsteam /.gitlab/ci/releases.gitlab-ci.yml @gl-quality/eng-prod @gitlab-org/delivery +/.gitlab/ci/dast.gitlab-ci.yml @dappelt @ngeorge1 @gl-quality/eng-prod +/.gitlab/ci/reports.gitlab-ci.yml @gitlab-com/gl-security/appsec @gl-quality/eng-prod /.gitlab/CODEOWNERS @gl-quality/eng-prod Dangerfile @gl-quality/eng-prod /danger/ @gl-quality/eng-prod /lib/gitlab/danger/ @gl-quality/eng-prod /scripts/ @gl-quality/eng-prod /scripts/frontend/ @gl-quality/eng-prod @gitlab-org/maintainers/frontend +/scripts/review_apps/seed-dast-test-data.sh @dappelt @ngeorge1 @gl-quality/eng-prod .editorconfig @gl-quality/eng-prod [End-to-end] diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index 0b54626f690..6acd52382cf 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -496,11 +496,12 @@ rspec foss-impact: - .rails:rules:ee-mr-only script: - install_gitlab_gem + - install_tff_gem - run_timed_command "scripts/gitaly-test-build" - run_timed_command "scripts/gitaly-test-spawn" - source scripts/rspec_helpers.sh - tooling/bin/find_foss_tests tmp/matching_foss_tests.txt - - rspec_matched_tests tmp/matching_foss_tests.txt "--tag ~quarantine" + - rspec_matched_foss_tests tmp/matching_foss_tests.txt "--tag ~quarantine" artifacts: expire_in: 7d paths: diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 49f96eeac74..b2836d2600e 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -149,6 +149,7 @@ - "*_VERSION" - "Gemfile{,.lock}" - "Rakefile" + - "tests.yml" - "config.ru" - "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*" - "doc/api/graphql/reference/*" # Files in this folder are auto-generated @@ -170,6 +171,7 @@ - "*_VERSION" - "Gemfile{,.lock}" - "Rakefile" + - "tests.yml" - "config.ru" - "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*" - "doc/api/graphql/reference/*" # Files in this folder are auto-generated @@ -193,6 +195,7 @@ - "*_VERSION" - "Gemfile{,.lock}" - "Rakefile" + - "tests.yml" - "config.ru" - "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*" - "doc/api/graphql/reference/*" # Files in this folder are auto-generated @@ -213,6 +216,7 @@ - "*_VERSION" - "Gemfile{,.lock}" - "Rakefile" + - "tests.yml" - "config.ru" - "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*" - "doc/api/graphql/reference/*" # Files in this folder are auto-generated @@ -762,6 +766,12 @@ changes: *code-backstage-patterns when: on_success +.setup:rules:verify-tests-yml: + rules: + - <<: *if-default-refs + changes: *code-backstage-patterns + when: on_success + ####################### # Test metadata rules # ####################### diff --git a/.gitlab/ci/setup.gitlab-ci.yml b/.gitlab/ci/setup.gitlab-ci.yml index 26c7a2194cc..b2b64700b18 100644 --- a/.gitlab/ci/setup.gitlab-ci.yml +++ b/.gitlab/ci/setup.gitlab-ci.yml @@ -48,3 +48,14 @@ no_ee_check: stage: test script: - scripts/no-ee-check + +verify-tests-yml: + extends: + - .setup:rules:verify-tests-yml + image: ruby:2.6-alpine + stage: test + needs: [] + script: + - source scripts/utils.sh + - install_tff_gem + - scripts/verify-tff-mapping diff --git a/CHANGELOG.md b/CHANGELOG.md index 99a0b061a29..d7bb4ac918d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,14 +2,6 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. -## 13.3.1 (2020-08-25) - -### Fixed (2 changes) - -- Fix bug when promoting an Issue with attachments to an Epic. !39654 -- Avoid creating diff position when line-code is nil. !40089 - - ## 13.3.0 (2020-08-22) ### Security (2 changes) diff --git a/app/assets/javascripts/design_management/components/design_notes/design_note.vue b/app/assets/javascripts/design_management/components/design_notes/design_note.vue index 172e61920ef..ab64e5c43db 100644 --- a/app/assets/javascripts/design_management/components/design_notes/design_note.vue +++ b/app/assets/javascripts/design_management/components/design_notes/design_note.vue @@ -102,12 +102,12 @@ export default { </a> <span class="note-headline-light note-headline-meta"> <span class="system-note-message"> <slot></slot> </span> - <template v-if="note.createdAt"> - <span class="system-note-separator"></span> - <a class="note-timestamp system-note-separator" :href="`#note_${noteAnchorId}`"> - <time-ago-tooltip :time="note.createdAt" tooltip-placement="bottom" /> - </a> - </template> + <a + class="note-timestamp system-note-separator gl-display-block gl-mb-2" + :href="`#note_${noteAnchorId}`" + > + <time-ago-tooltip :time="note.createdAt" tooltip-placement="bottom" /> + </a> </span> </div> <div class="gl-display-flex"> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue index 9df0c045fe4..a5ec095b8ec 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue @@ -1,4 +1,5 @@ <script> +import { GlButton } from '@gitlab/ui'; import { n__ } from '~/locale'; import { stripHtml } from '~/lib/utils/text_utility'; import statusIcon from '../mr_widget_status_icon.vue'; @@ -8,6 +9,7 @@ export default { name: 'MRWidgetFailedToMerge', components: { + GlButton, statusIcon, }, @@ -84,14 +86,14 @@ export default { <span v-else> {{ s__('mrWidget|Merge failed.') }} </span> <span :class="{ 'has-custom-error': mr.mergeError }"> {{ timerText }} </span> </span> - <button - class="btn btn-default btn-sm js-refresh-button" + <gl-button + size="small" + data-testid="merge-request-failed-refresh-button" data-qa-selector="merge_request_error_content" - type="button" @click="refresh" > {{ s__('mrWidget|Refresh now') }} - </button> + </gl-button> </div> </template> </div> diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss index 3eff1807403..55996a074c6 100644 --- a/app/assets/stylesheets/framework/wells.scss +++ b/app/assets/stylesheets/framework/wells.scss @@ -110,10 +110,6 @@ .dark-well { background-color: $gray-normal; - - .btn { - width: 100%; - } } .card.card-body-centered { diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index c04f4da70cf..7709b18fb39 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -70,9 +70,19 @@ module Types description: 'Text to echo back', resolver: Resolvers::EchoResolver + field :issue, Types::IssueType, + null: true, + description: 'Find an issue' do + argument :id, ::Types::GlobalIDType[::Issue], required: true, description: 'The global ID of the Issue' + end + def design_management DesignManagementObject.new(nil) end + + def issue(id:) + GitlabSchema.object_from_id(id, expected_type: ::Issue) + end end end diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 8c9e98765aa..b6ac5db4d2d 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -13,14 +13,14 @@ .admin-dashboard.gl-mt-3 .row .col-sm-4 - .info-well.dark-well + .info-well.dark-well.flex-fill .well-segment.well-centered = link_to admin_projects_path do %h3.text-center Projects: = approximate_count_with_delimiters(@counts, Project) %hr - = link_to('New project', new_project_path, class: "btn btn-success") + = link_to('New project', new_project_path, class: "btn btn-success gl-w-full") .col-sm-4 .info-well.dark-well .well-segment.well-centered @@ -30,8 +30,8 @@ = approximate_count_with_delimiters(@counts, User) %hr .btn-group.d-flex{ role: 'group' } - = link_to 'New user', new_admin_user_path, class: "btn btn-success" - = link_to s_('AdminArea|Users statistics'), admin_dashboard_stats_path, class: 'btn btn-primary' + = link_to 'New user', new_admin_user_path, class: "btn btn-success gl-w-full" + = link_to s_('AdminArea|Users statistics'), admin_dashboard_stats_path, class: 'btn btn-primary gl-w-full' .col-sm-4 .info-well.dark-well .well-segment.well-centered @@ -40,7 +40,7 @@ Groups: = approximate_count_with_delimiters(@counts, Group) %hr - = link_to 'New group', new_admin_group_path, class: "btn btn-success" + = link_to 'New group', new_admin_group_path, class: "btn btn-success gl-w-full" .row .col-md-4 #js-admin-statistics-container diff --git a/app/views/import/fogbugz/new.html.haml b/app/views/import/fogbugz/new.html.haml index 626080c284b..4daa769215f 100644 --- a/app/views/import/fogbugz/new.html.haml +++ b/app/views/import/fogbugz/new.html.haml @@ -1,7 +1,8 @@ - page_title _("FogBugz Import") - header_title _("Projects"), root_path -%h3.page-title - %i.fa.fa-bug +%h3.page-title.d-flex + .gl-display-flex.gl-align-items-center.gl-justify-content-center + = sprite_icon('bug', css_class: 'gl-mr-2') = _('Import projects from FogBugz') %hr diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml index cdc53520e93..fb93a3eca0d 100644 --- a/app/views/import/fogbugz/new_user_map.html.haml +++ b/app/views/import/fogbugz/new_user_map.html.haml @@ -1,7 +1,8 @@ - page_title _('User map'), _('FogBugz import') - header_title _("Projects"), root_path -%h3.page-title - %i.fa.fa-bug +%h3.page-title.d-flex + .gl-display-flex.gl-align-items-center.gl-justify-content-center + = sprite_icon('bug', css_class: 'gl-mr-2') = _('Import projects from FogBugz') %hr diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml index f201c0e83fe..e04a412e3bc 100644 --- a/app/views/import/fogbugz/status.html.haml +++ b/app/views/import/fogbugz/status.html.haml @@ -1,7 +1,8 @@ - page_title _("FogBugz import") - header_title _("Projects"), root_path -%h3.page-title - %i.fa.fa-bug +%h3.page-title.d-flex + .gl-display-flex.gl-align-items-center.gl-justify-content-center + = sprite_icon('bug', css_class: 'gl-mr-2') = _('Import projects from FogBugz') %p.light diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml index dd7971f6db0..fe3354aefbb 100644 --- a/app/views/projects/_import_project_pane.html.haml +++ b/app/views/projects/_import_project_pane.html.haml @@ -46,7 +46,8 @@ - if fogbugz_import_enabled? %div = link_to new_import_fogbugz_path, class: 'btn import_fogbugz', **tracking_attrs(track_label, 'click_button', 'fogbugz') do - = icon('bug', text: 'FogBugz') + = sprite_icon('bug') + FogBugz - if gitea_import_enabled? %div diff --git a/changelogs/unreleased/209770-design-comments-text-wrapping-behavior.yml b/changelogs/unreleased/209770-design-comments-text-wrapping-behavior.yml new file mode 100644 index 00000000000..10c5689eccf --- /dev/null +++ b/changelogs/unreleased/209770-design-comments-text-wrapping-behavior.yml @@ -0,0 +1,5 @@ +--- +title: 'Resolve Design comments: Text wrapping behavior' +merge_request: 40359 +author: +type: fixed diff --git a/changelogs/unreleased/225939-replace-fa-bugs-icons-with-gitlab-svg-bug-icon.yml b/changelogs/unreleased/225939-replace-fa-bugs-icons-with-gitlab-svg-bug-icon.yml new file mode 100644 index 00000000000..3c47daf4e25 --- /dev/null +++ b/changelogs/unreleased/225939-replace-fa-bugs-icons-with-gitlab-svg-bug-icon.yml @@ -0,0 +1,5 @@ +--- +title: Replace fa-bugs icons with GitLab SVG bug icon +merge_request: 40273 +author: +type: changed diff --git a/changelogs/unreleased/229296-migrate-bootstrap-button-to-gitlab-ui-glbutton-in-app-assets-javas.yml b/changelogs/unreleased/229296-migrate-bootstrap-button-to-gitlab-ui-glbutton-in-app-assets-javas.yml new file mode 100644 index 00000000000..11de8081fee --- /dev/null +++ b/changelogs/unreleased/229296-migrate-bootstrap-button-to-gitlab-ui-glbutton-in-app-assets-javas.yml @@ -0,0 +1,5 @@ +--- +title: Migrate Bootstrap button to GitLab UI GlButton in mr_widget_failed_to_merge +merge_request: 40170 +author: +type: other diff --git a/changelogs/unreleased/37248-add-api-to-monitor-issue-creation.yml b/changelogs/unreleased/37248-add-api-to-monitor-issue-creation.yml new file mode 100644 index 00000000000..10499223fb3 --- /dev/null +++ b/changelogs/unreleased/37248-add-api-to-monitor-issue-creation.yml @@ -0,0 +1,5 @@ +--- +title: Add ability to get an Issue using GraphQL and REST API +merge_request: 35176 +author: +type: added diff --git a/changelogs/unreleased/id-fix-nil-line-codes-for-diff-positions.yml b/changelogs/unreleased/id-fix-nil-line-codes-for-diff-positions.yml new file mode 100644 index 00000000000..1d7b495e8f0 --- /dev/null +++ b/changelogs/unreleased/id-fix-nil-line-codes-for-diff-positions.yml @@ -0,0 +1,5 @@ +--- +title: Avoid creating diff position when line-code is nil +merge_request: 40089 +author: +type: fixed diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index 41f85567624..eb781fc082b 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -7223,6 +7223,11 @@ type IssueEdge { } """ +Identifier of Issue +""" +scalar IssueID + +""" Autogenerated input type of IssueMoveList """ input IssueMoveListInput { @@ -12508,6 +12513,16 @@ type Query { instanceSecurityDashboard: InstanceSecurityDashboard """ + Find an issue + """ + issue( + """ + The global ID of the Issue + """ + id: IssueID! + ): Issue + + """ Find an iteration """ iteration( diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index 77eeb7012b5..fa4b5620b86 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -19964,6 +19964,16 @@ "possibleTypes": null }, { + "kind": "SCALAR", + "name": "IssueID", + "description": "Identifier of Issue", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { "kind": "INPUT_OBJECT", "name": "IssueMoveListInput", "description": "Autogenerated input type of IssueMoveList", @@ -36857,6 +36867,33 @@ "deprecationReason": null }, { + "name": "issue", + "description": "Find an issue", + "args": [ + { + "name": "id", + "description": "The global ID of the Issue", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "IssueID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "Issue", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "iteration", "description": "Find an iteration", "args": [ diff --git a/doc/api/issues.md b/doc/api/issues.md index c9f7dd71849..93f788d62a3 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -564,6 +564,320 @@ the issue still exists. ## Single issue +Only for administrators. Get a single issue. + +The preferred way to do this is by using [personal access tokens](../user/profile/personal_access_tokens.md). + +```plaintext +GET /issues/:id +``` + +| Attribute | Type | Required | Description | +|-------------|---------|----------|--------------------------------------| +| `id` | integer | yes | The ID of the issue | + +```shell +curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/issues/41" +``` + +Example response: + +```json +{ + "id" : 1, + "milestone" : { + "due_date" : null, + "project_id" : 4, + "state" : "closed", + "description" : "Rerum est voluptatem provident consequuntur molestias similique ipsum dolor.", + "iid" : 3, + "id" : 11, + "title" : "v3.0", + "created_at" : "2016-01-04T15:31:39.788Z", + "updated_at" : "2016-01-04T15:31:39.788Z", + "closed_at" : "2016-01-05T15:31:46.176Z" + }, + "author" : { + "state" : "active", + "web_url" : "https://gitlab.example.com/root", + "avatar_url" : null, + "username" : "root", + "id" : 1, + "name" : "Administrator" + }, + "description" : "Omnis vero earum sunt corporis dolor et placeat.", + "state" : "closed", + "iid" : 1, + "assignees" : [{ + "avatar_url" : null, + "web_url" : "https://gitlab.example.com/lennie", + "state" : "active", + "username" : "lennie", + "id" : 9, + "name" : "Dr. Luella Kovacek" + }], + "assignee" : { + "avatar_url" : null, + "web_url" : "https://gitlab.example.com/lennie", + "state" : "active", + "username" : "lennie", + "id" : 9, + "name" : "Dr. Luella Kovacek" + }, + "labels" : [], + "upvotes": 4, + "downvotes": 0, + "merge_requests_count": 0, + "title" : "Ut commodi ullam eos dolores perferendis nihil sunt.", + "updated_at" : "2016-01-04T15:31:46.176Z", + "created_at" : "2016-01-04T15:31:46.176Z", + "closed_at" : null, + "closed_by" : null, + "subscribed": false, + "user_notes_count": 1, + "due_date": null, + "web_url": "http://example.com/my-group/my-project/issues/1", + "references": { + "short": "#1", + "relative": "#1", + "full": "my-group/my-project#1" + }, + "time_stats": { + "time_estimate": 0, + "total_time_spent": 0, + "human_time_estimate": null, + "human_total_time_spent": null + }, + "confidential": false, + "discussion_locked": false, + "_links": { + "self": "http://example.com/api/v4/projects/1/issues/2", + "notes": "http://example.com/api/v4/projects/1/issues/2/notes", + "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", + "project": "http://example.com/api/v4/projects/1" + }, + "task_completion_status":{ + "count":0, + "completed_count":0 + }, + "weight": null, + "has_tasks": false, + "_links": { + "self": "http://gitlab.dummy:3000/api/v4/projects/1/issues/1", + "notes": "http://gitlab.dummy:3000/api/v4/projects/1/issues/1/notes", + "award_emoji": "http://gitlab.dummy:3000/api/v4/projects/1/issues/1/award_emoji", + "project": "http://gitlab.dummy:3000/api/v4/projects/1" + }, + "references": { + "short": "#1", + "relative": "#1", + "full": "gitlab-org/gitlab-test#1" + }, + "subscribed": true, + "moved_to_id": null, + "epic_iid": null, + "epic": null +} +``` + +Users on GitLab [Starter, Bronze, or higher](https://about.gitlab.com/pricing/) will also see +the `weight` parameter: + +```json +{ + "project_id" : 4, + "description" : "Omnis vero earum sunt corporis dolor et placeat.", + "weight": null, + ... +} +``` + +Users on GitLab [Ultimate](https://about.gitlab.com/pricing/) will additionally see +the `epic` property: + +```javascript +{ + "project_id" : 4, + "description" : "Omnis vero earum sunt corporis dolor et placeat.", + "epic": { + "epic_iid" : 5, //deprecated, use `iid` of the `epic` attribute + "epic": { + "id" : 42, + "iid" : 5, + "title": "My epic epic", + "url" : "/groups/h5bp/-/epics/5", + "group_id": 8 + }, + // ... +} +``` + +**Note**: `assignee` column is deprecated, now we show it as a single-sized array `assignees` to conform to the GitLab EE API. + +**Note**: The `closed_by` attribute was [introduced in GitLab 10.6](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042). This value will only be present for issues which were closed after GitLab 10.6 and when the user account that closed the issue still exists. + +**Note**: The `epic_iid` attribute is deprecated and [will be removed in version 5](https://gitlab.com/gitlab-org/gitlab/-/issues/35157). +Please use `iid` of the `epic` attribute instead. + +## Single Issue + +Only for administrators. Get a single issue. + +The preferred way to do this is by using [personal access tokens](../user/profile/personal_access_tokens.md). + +```plaintext +GET /issues/:id +``` + +| Attribute | Type | Required | Description | +|-------------|---------|----------|--------------------------------------| +| `id` | integer | yes | The ID of the issue | + +```shell +curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/issues/41" +``` + +Example response: + +```json +{ + "id" : 1, + "milestone" : { + "due_date" : null, + "project_id" : 4, + "state" : "closed", + "description" : "Rerum est voluptatem provident consequuntur molestias similique ipsum dolor.", + "iid" : 3, + "id" : 11, + "title" : "v3.0", + "created_at" : "2016-01-04T15:31:39.788Z", + "updated_at" : "2016-01-04T15:31:39.788Z", + "closed_at" : "2016-01-05T15:31:46.176Z" + }, + "author" : { + "state" : "active", + "web_url" : "https://gitlab.example.com/root", + "avatar_url" : null, + "username" : "root", + "id" : 1, + "name" : "Administrator" + }, + "description" : "Omnis vero earum sunt corporis dolor et placeat.", + "state" : "closed", + "iid" : 1, + "assignees" : [{ + "avatar_url" : null, + "web_url" : "https://gitlab.example.com/lennie", + "state" : "active", + "username" : "lennie", + "id" : 9, + "name" : "Dr. Luella Kovacek" + }], + "assignee" : { + "avatar_url" : null, + "web_url" : "https://gitlab.example.com/lennie", + "state" : "active", + "username" : "lennie", + "id" : 9, + "name" : "Dr. Luella Kovacek" + }, + "labels" : [], + "upvotes": 4, + "downvotes": 0, + "merge_requests_count": 0, + "title" : "Ut commodi ullam eos dolores perferendis nihil sunt.", + "updated_at" : "2016-01-04T15:31:46.176Z", + "created_at" : "2016-01-04T15:31:46.176Z", + "closed_at" : null, + "closed_by" : null, + "subscribed": false, + "user_notes_count": 1, + "due_date": null, + "web_url": "http://example.com/my-group/my-project/issues/1", + "references": { + "short": "#1", + "relative": "#1", + "full": "my-group/my-project#1" + }, + "time_stats": { + "time_estimate": 0, + "total_time_spent": 0, + "human_time_estimate": null, + "human_total_time_spent": null + }, + "confidential": false, + "discussion_locked": false, + "_links": { + "self": "http://example.com/api/v4/projects/1/issues/2", + "notes": "http://example.com/api/v4/projects/1/issues/2/notes", + "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", + "project": "http://example.com/api/v4/projects/1" + }, + "task_completion_status":{ + "count":0, + "completed_count":0 + }, + "weight": null, + "has_tasks": false, + "_links": { + "self": "http://gitlab.dummy:3000/api/v4/projects/1/issues/1", + "notes": "http://gitlab.dummy:3000/api/v4/projects/1/issues/1/notes", + "award_emoji": "http://gitlab.dummy:3000/api/v4/projects/1/issues/1/award_emoji", + "project": "http://gitlab.dummy:3000/api/v4/projects/1" + }, + "references": { + "short": "#1", + "relative": "#1", + "full": "gitlab-org/gitlab-test#1" + }, + "subscribed": true, + "moved_to_id": null, + "epic_iid": null, + "epic": null +} +``` + +Users on GitLab [Starter, Bronze, or higher](https://about.gitlab.com/pricing/) will also see +the `weight` parameter: + +```json +{ + "project_id" : 4, + "description" : "Omnis vero earum sunt corporis dolor et placeat.", + "weight": null, + ... +} +``` + +Users on GitLab [Ultimate](https://about.gitlab.com/pricing/) will additionally see +the `epic` property: + +```javascript +{ + "project_id" : 4, + "description" : "Omnis vero earum sunt corporis dolor et placeat.", + "epic": { + "epic_iid" : 5, //deprecated, use `iid` of the `epic` attribute + "epic": { + "id" : 42, + "iid" : 5, + "title": "My epic epic", + "url" : "/groups/h5bp/-/epics/5", + "group_id": 8 + }, + // ... +} +``` + +**Note**: `assignee` column is deprecated, now we show it as a single-sized array `assignees` to conform to the GitLab EE API. + +**Note**: The `closed_by` attribute was [introduced in GitLab 10.6](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042). This value will only be present for issues which were closed after GitLab 10.6 and when the user account that closed the issue still exists. + +**Note**: The `epic_iid` attribute is deprecated and [will be removed in version 5](https://gitlab.com/gitlab-org/gitlab/-/issues/35157). +Please use `iid` of the `epic` attribute instead. + +## Single Project Issue + Get a single project issue. If the project is private or the issue is confidential, you need to provide credentials to authorize. diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md index 531b308111a..274eb3baee4 100644 --- a/doc/user/project/import/github.md +++ b/doc/user/project/import/github.md @@ -135,8 +135,8 @@ your GitHub repositories are listed. ## Mirroring and pipeline status sharing -Depending your GitLab tier, [project mirroring](../repository/repository_mirroring.md) can be set up to keep -your imported project in sync with its GitHub copy. +Depending on your GitLab tier, [repository mirroring](../repository/repository_mirroring.md) can be set up to keep +your imported repository in sync with its GitHub copy. Additionally, you can configure GitLab to send pipeline status updates back GitHub with the [GitHub Project Integration](../integrations/github.md). **(PREMIUM)** diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 1694a967f26..0e5b0fae6e2 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -114,6 +114,19 @@ module API present issues, options end + + desc "Get specified issue (admin only)" do + success Entities::Issue + end + params do + requires :id, type: String, desc: 'The ID of the Issue' + end + get ":id" do + authenticated_as_admin! + issue = Issue.find(params['id']) + + present issue, with: Entities::Issue, current_user: current_user, project: issue.project + end end params do diff --git a/lib/gitlab/danger/helper.rb b/lib/gitlab/danger/helper.rb index 077c71f1233..9ba5df89a35 100644 --- a/lib/gitlab/danger/helper.rb +++ b/lib/gitlab/danger/helper.rb @@ -171,6 +171,7 @@ module Gitlab %r{\A(ee/)?scripts/} => :engineering_productivity, %r{\Atooling/} => :engineering_productivity, %r{(CODEOWNERS)} => :engineering_productivity, + %r{(tests.yml)} => :engineering_productivity, %r{\A(ee/)?spec/features/} => :test, %r{\A(ee/)?spec/support/shared_examples/features/} => :test, diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 43f3e471da8..f5bb70a8ff8 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3328,6 +3328,9 @@ msgstr "" msgid "As U2F devices are only supported by a few browsers, we require that you set up a two-factor authentication app before a U2F device. That way you'll always be able to log in - even when you're using an unsupported browser." msgstr "" +msgid "As we continue to build more features for SAST, we'd love your feedback on the SAST configuration feature in %{linkStart}this issue%{linkEnd}." +msgstr "" + msgid "AsanaService|%{user} pushed to branch %{branch} of %{project_name} ( %{commit_url} ):" msgstr "" @@ -14283,6 +14286,9 @@ msgstr "" msgid "License ID:" msgstr "" +msgid "License overview" +msgstr "" + msgid "License-Check" msgstr "" @@ -14409,6 +14415,9 @@ msgstr "" msgid "Licensed to" msgstr "" +msgid "Licensed to:" +msgstr "" + msgid "Licenses" msgstr "" @@ -26979,6 +26988,9 @@ msgstr "" msgid "Users were successfully added." msgstr "" +msgid "Users with a Guest role or those who don't belong to a Project or Group will not use a seat from your license." +msgstr "" + msgid "Users with a Guest role or those who don't belong to any projects or groups don't count towards seats in use." msgstr "" diff --git a/package.json b/package.json index cb288aece9d..f5bcb056c7d 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,8 @@ "@babel/plugin-syntax-import-meta": "^7.10.1", "@babel/preset-env": "^7.10.1", "@gitlab/at.js": "1.5.5", - "@gitlab/svgs": "1.160.0", - "@gitlab/ui": "20.6.0", + "@gitlab/svgs": "1.161.0", + "@gitlab/ui": "20.8.0", "@gitlab/visual-review-tools": "1.6.1", "@rails/actioncable": "^6.0.3-1", "@sentry/browser": "^5.10.2", diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb index 3e25ecfd45d..f393a12844b 100644 --- a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb +++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb @@ -23,7 +23,7 @@ module QA cluster&.remove! end - it 'runs auto devops' do + it 'runs auto devops', status_issue: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/702' do Flow::Login.sign_in # Set an application secret CI variable (prefixed with K8S_SECRET_) @@ -116,7 +116,7 @@ module QA end end - it 'runs an AutoDevOps pipeline' do + it 'runs an AutoDevOps pipeline', status_issue: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/444' do Page::Project::Menu.perform(&:click_ci_cd_pipelines) Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline) diff --git a/qa/qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb index 5073b715341..a9eebb80d5a 100644 --- a/qa/qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb +++ b/qa/qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb @@ -20,7 +20,7 @@ module QA cluster.remove! end - it 'can create and associate a project cluster', :smoke do + it 'can create and associate a project cluster', :smoke, status_issue: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/707' do Resource::KubernetesCluster::ProjectCluster.fabricate_via_browser_ui! do |k8s_cluster| k8s_cluster.project = project k8s_cluster.cluster = cluster diff --git a/qa/qa/specs/features/browser_ui/8_monitor/all_monitor_core_features_spec.rb b/qa/qa/specs/features/browser_ui/8_monitor/all_monitor_core_features_spec.rb index 7c883559574..af9c9a9e48f 100644 --- a/qa/qa/specs/features/browser_ui/8_monitor/all_monitor_core_features_spec.rb +++ b/qa/qa/specs/features/browser_ui/8_monitor/all_monitor_core_features_spec.rb @@ -10,13 +10,13 @@ module QA @project.visit! end - it 'configures custom metrics' do + it 'configures custom metrics', status_issue: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/872' do verify_add_custom_metric verify_edit_custom_metric verify_delete_custom_metric end - it 'duplicates to create dashboard to custom' do + it 'duplicates to create dashboard to custom', status_issue: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/871' do Page::Project::Menu.perform(&:go_to_operations_metrics) Page::Project::Operations::Metrics::Show.perform do |on_dashboard| @@ -27,7 +27,7 @@ module QA end end - it 'verifies data on filtered deployed environment' do + it 'verifies data on filtered deployed environment', status_issue: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/874' do Page::Project::Menu.perform(&:go_to_operations_metrics) Page::Project::Operations::Metrics::Show.perform do |on_dashboard| @@ -37,7 +37,7 @@ module QA end end - it 'filters using the quick range' do + it 'filters using the quick range', status_issue: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/873' do Page::Project::Menu.perform(&:go_to_operations_metrics) Page::Project::Operations::Metrics::Show.perform do |on_dashboard| @@ -52,7 +52,7 @@ module QA end end - it 'observes cluster health graph' do + it 'observes cluster health graph', status_issue: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/920' do Page::Project::Menu.perform(&:go_to_operations_kubernetes) Page::Project::Operations::Kubernetes::Index.perform do |cluster_list| diff --git a/scripts/rspec_helpers.sh b/scripts/rspec_helpers.sh index 1ecf9a566d7..3b0c7d973de 100644 --- a/scripts/rspec_helpers.sh +++ b/scripts/rspec_helpers.sh @@ -111,7 +111,7 @@ function rspec_paralellized_job() { date } -function rspec_matched_tests() { +function rspec_matched_foss_tests() { local test_file_count_threshold=20 local matching_tests_file=${1} local rspec_opts=${2} @@ -131,6 +131,6 @@ function rspec_matched_tests() { if [[ -n $test_files ]]; then rspec_simple_job "${rspec_opts} ${test_files}" else - echo "No test files to run" + echo "No FOSS test files to run" fi } diff --git a/scripts/utils.sh b/scripts/utils.sh index f81e5c8982a..98d9dca74c0 100644 --- a/scripts/utils.sh +++ b/scripts/utils.sh @@ -35,6 +35,10 @@ function install_gitlab_gem() { gem install gitlab --no-document --version 4.13.0 } +function install_tff_gem() { + gem install test_file_finder --version 0.1.0 +} + function run_timed_command() { local cmd="${1}" local start=$(date +%s) diff --git a/scripts/verify-tff-mapping b/scripts/verify-tff-mapping new file mode 100755 index 00000000000..9541d2468b1 --- /dev/null +++ b/scripts/verify-tff-mapping @@ -0,0 +1,163 @@ +#!/usr/bin/env ruby + +require 'set' + +# These tests run a sanity check on the mapping file `tests.yml` +# used with the `test_file_finder` gem (`tff`) to identify matching test files. +# The verification depend on the presence of actual test files, +# so they would fail if one of the test files mentioned here is deleted. +# To minimize the chance of this test failing due to unrelated changes, +# the test files are chosen to be critical files that are unlikely to be deleted in a typical Merge Request +tests = [ + { + explanation: 'EE code should map to respective spec', + source: 'ee/app/controllers/admin/licenses_controller.rb', + expected: ['ee/spec/controllers/admin/licenses_controller_spec.rb'] + }, + + { + explanation: 'FOSS code should map to respective spec', + source: 'app/finders/admin/projects_finder.rb', + expected: ['spec/finders/admin/projects_finder_spec.rb'] + }, + + { + explanation: 'EE extension should map to its EE extension spec and its FOSS class spec', + source: 'ee/app/finders/ee/projects_finder.rb', + expected: ['ee/spec/finders/ee/projects_finder_spec.rb', 'spec/finders/projects_finder_spec.rb'] + }, + + { + explanation: 'Some EE extensions also map to its EE class spec, but this is not recommended: https://docs.gitlab.com/ee/development/ee_features.html#testing-ee-features-based-on-ce-features', + source: 'ee/app/models/ee/user.rb', + expected: ['ee/spec/models/user_spec.rb', 'spec/models/user_spec.rb'] + }, + + { + explanation: 'FOSS lib should map to respective spec', + source: 'lib/gitaly/server.rb', + expected: ['spec/lib/gitaly/server_spec.rb'] + }, + + { + explanation: 'EE lib should map to respective spec', + source: 'ee/lib/flipper_session.rb', + expected: ['ee/spec/lib/flipper_session_spec.rb'] + }, + + { + explanation: 'Tooling should map to respective spec', + source: 'tooling/lib/tooling/test_file_finder.rb', + expected: ['spec/tooling/lib/tooling/test_file_finder_spec.rb'] + }, + + { + explanation: 'FOSS spec code should map to itself', + source: 'spec/models/issue_spec.rb', + expected: ['spec/models/issue_spec.rb'] + }, + + { + explanation: 'EE spec code should map to itself', + source: 'ee/spec/models/user_spec.rb', + expected: ['ee/spec/models/user_spec.rb'] + }, + + { + explanation: 'EE extension spec should map to itself and the FOSS class spec', + source: 'ee/spec/services/ee/notification_service_spec.rb', + expected: ['ee/spec/services/ee/notification_service_spec.rb', 'spec/services/notification_service_spec.rb'] + }, + + { + explanation: 'FOSS factory should map to factories spec', + source: 'spec/factories/users.rb', + expected: ['spec/factories_spec.rb'] + }, + + { + explanation: 'EE factory should map to factories spec', + source: 'ee/spec/factories/users.rb', + expected: ['spec/factories_spec.rb'] + }, + + { + explanation: 'Initializers should map to respective spec', + source: 'config/initializers/action_mailer_hooks.rb', + expected: ['spec/initializers/action_mailer_hooks_spec.rb'] + }, + + { + explanation: 'FOSS views should map to respective spec', + source: 'app/views/admin/users/_user.html.haml', + expected: ['spec/views/admin/users/_user.html.haml_spec.rb'] + }, + + { + explanation: 'EE views should map to respective spec', + source: 'ee/app/views/admin/licenses/show.html.haml', + expected: ['ee/spec/views/admin/licenses/show.html.haml_spec.rb'] + }, + + { + explanation: 'DB structure should map to schema spec', + source: 'db/structure.sql', + expected: ['spec/db/schema_spec.rb'] + }, + + { + explanation: 'Migration should map to its non-timestamped spec', + source: 'db/migrate/20191023152913_add_default_and_free_plans.rb', + expected: ['spec/migrations/add_default_and_free_plans_spec.rb'] + }, + + { + explanation: 'Migration should map to its timestamped spec', + source: 'db/post_migrate/20190924152703_migrate_issue_trackers_data.rb', + expected: ['spec/migrations/20190924152703_migrate_issue_trackers_data_spec.rb'] + } +] + +class MappingTest + def initialize(explanation:, source:, expected:, mapping: 'tests.yml') + @explanation = explanation + @source = source + @mapping = mapping + @expected_set = Set.new(expected) + @actual_set = Set.new(actual) + end + + def passed? + expected_set.eql?(actual_set) + end + + def failed? + !passed? + end + + def failure_message + "#{explanation}: #{source}: Expected #{expected_set.to_a}, got #{actual_set.to_a}." + end + + private + + attr_reader :explanation, :source, :expected_set, :actual_set, :mapping + + def actual + `tff -f #{mapping} #{source}`.split(' ') + end +end + +results = tests.map { |test| MappingTest.new(test) } + +failed_tests = results.select(&:failed?) +if failed_tests.any? + puts <<~MESSAGE + tff mapping verification failed: + #{failed_tests.map(&:failure_message).join("\n")} + MESSAGE + + exit 1 +end + +puts 'tff mapping verification passed.' diff --git a/spec/frontend/design_management/components/design_notes/__snapshots__/design_note_spec.js.snap b/spec/frontend/design_management/components/design_notes/__snapshots__/design_note_spec.js.snap index b55bacb6fc5..d20d81c5230 100644 --- a/spec/frontend/design_management/components/design_notes/__snapshots__/design_note_spec.js.snap +++ b/spec/frontend/design_management/components/design_notes/__snapshots__/design_note_spec.js.snap @@ -46,7 +46,16 @@ exports[`Design note component should match the snapshot 1`] = ` class="system-note-message" /> - <!----> + <a + class="note-timestamp system-note-separator gl-display-block gl-mb-2" + href="#note_123" + > + <time-ago-tooltip-stub + cssclass="" + time="2019-07-26T15:02:20Z" + tooltipplacement="bottom" + /> + </a> </span> </div> diff --git a/spec/frontend/design_management/components/design_notes/design_note_spec.js b/spec/frontend/design_management/components/design_notes/design_note_spec.js index 8b32d3022ee..8eeaa6cd5ab 100644 --- a/spec/frontend/design_management/components/design_notes/design_note_spec.js +++ b/spec/frontend/design_management/components/design_notes/design_note_spec.js @@ -15,6 +15,7 @@ const note = { userPermissions: { adminNote: false, }, + createdAt: '2019-07-26T15:02:20Z', }; HTMLElement.prototype.scrollIntoView = scrollIntoViewMock; @@ -79,10 +80,7 @@ describe('Design note component', () => { it('should render a time ago tooltip if note has createdAt property', () => { createComponent({ - note: { - ...note, - createdAt: '2019-07-26T15:02:20Z', - }, + note, }); expect(wrapper.find(TimeAgoTooltip).exists()).toBe(true); diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js index f591393d721..6778a8f4a1f 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js @@ -125,7 +125,11 @@ describe('MRWidgetFailedToMerge', () => { }); it('renders refresh button', () => { - expect(vm.$el.querySelector('.js-refresh-button').textContent.trim()).toEqual('Refresh now'); + expect( + vm.$el + .querySelector('[data-testid="merge-request-failed-refresh-button"]') + .textContent.trim(), + ).toEqual('Refresh now'); }); it('renders remaining time', () => { diff --git a/spec/graphql/types/query_type_spec.rb b/spec/graphql/types/query_type_spec.rb index ab13162b406..d09fd34b551 100644 --- a/spec/graphql/types/query_type_spec.rb +++ b/spec/graphql/types/query_type_spec.rb @@ -20,6 +20,7 @@ RSpec.describe GitlabSchema.types['Query'] do milestone user users + issue ] expect(described_class).to have_graphql_fields(*expected_fields).at_least @@ -53,4 +54,12 @@ RSpec.describe GitlabSchema.types['Query'] do is_expected.to have_graphql_resolver(Resolvers::MetadataResolver) end end + + describe 'issue field' do + subject { described_class.fields['issue'] } + + it 'returns issue' do + is_expected.to have_graphql_type(Types::IssueType) + end + end end diff --git a/spec/requests/api/graphql/issue/issue_spec.rb b/spec/requests/api/graphql/issue/issue_spec.rb new file mode 100644 index 00000000000..1c9d6b25856 --- /dev/null +++ b/spec/requests/api/graphql/issue/issue_spec.rb @@ -0,0 +1,126 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Query.issue(id)' do + include GraphqlHelpers + + let_it_be(:project) { create(:project) } + let_it_be(:issue) { create(:issue, project: project) } + let_it_be(:current_user) { create(:user) } + + let(:issue_data) { graphql_data['issue'] } + + let_it_be(:issue_params) { { 'id' => issue.to_global_id.to_s } } + let(:issue_fields) { all_graphql_fields_for('Issue'.classify) } + + let(:query) do + graphql_query_for('issue', issue_params, issue_fields) + end + + it_behaves_like 'a working graphql query' do + before do + post_graphql(query, current_user: current_user) + end + end + + context 'when the user does not have access to the issue' do + it 'returns nil' do + project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE) + + post_graphql(query) + + expect(issue_data).to be nil + end + end + + context 'when the user does have access' do + before do + project.add_guest(current_user) + end + + it 'returns the issue' do + post_graphql(query, current_user: current_user) + + expect(issue_data).to include( + 'title' => issue.title, + 'description' => issue.description + ) + end + + context 'selecting any single field' do + where(:field) do + scalar_fields_of('Issue').map { |name| [name] } + end + + with_them do + it_behaves_like 'a working graphql query' do + let(:issue_fields) do + field + end + + before do + post_graphql(query, current_user: current_user) + end + + it "returns the Issue and field #{params['field']}" do + expect(issue_data.keys).to eq([field]) + end + end + end + end + + context 'selecting multiple fields' do + let(:issue_fields) { %w(title description) } + + it 'returns the Issue with the specified fields' do + post_graphql(query, current_user: current_user) + + expect(issue_data.keys).to eq( %w(title description) ) + expect(issue_data['title']).to eq(issue.title) + expect(issue_data['description']).to eq(issue.description) + end + end + + context 'when passed a non-Issue gid' do + let(:mr) {create(:merge_request)} + + it 'returns an error' do + gid = mr.to_global_id.to_s + issue_params['id'] = gid + + post_graphql(query, current_user: current_user) + + expect(graphql_errors).not_to be nil + expect(graphql_errors.first['message']).to eq("\"#{gid}\" does not represent an instance of Issue") + end + end + end + + context 'when there is a confidential issue' do + let!(:confidential_issue) do + create(:issue, :confidential, project: project) + end + + let(:issue_params) { { 'id' => confidential_issue.to_global_id.to_s } } + + context 'when the user cannot see confidential issues' do + it 'returns nil ' do + post_graphql(query, current_user: current_user) + + expect(issue_data).to be nil + end + end + + context 'when the user can see confidential issues' do + it 'returns the confidential issue' do + project.add_developer(current_user) + + post_graphql(query, current_user: current_user) + + expect(graphql_data.count).to eq(1) + expect(issue_data['confidential']).to be(true) + end + end + end +end diff --git a/spec/requests/api/issues/issues_spec.rb b/spec/requests/api/issues/issues_spec.rb index b638a65d65e..b8cbddd9ed4 100644 --- a/spec/requests/api/issues/issues_spec.rb +++ b/spec/requests/api/issues/issues_spec.rb @@ -87,6 +87,46 @@ RSpec.describe API::Issues do end end + describe 'GET /issues/:id' do + context 'when unauthorized' do + it 'returns unauthorized' do + get api("/issues/#{issue.id}" ) + + expect(response).to have_gitlab_http_status(:unauthorized) + end + end + + context 'when authorized' do + context 'as a normal user' do + it 'returns forbidden' do + get api("/issues/#{issue.id}", user ) + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + + context 'as an admin' do + context 'when issue exists' do + it 'returns the issue' do + get api("/issues/#{issue.id}", admin) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response.dig('author', 'id')).to eq(issue.author.id) + expect(json_response['description']).to eq(issue.description) + end + end + + context 'when issue does not exist' do + it 'returns 404' do + get api("/issues/0", admin) + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + end + end + describe 'GET /issues' do context 'when unauthenticated' do it 'returns an array of all issues' do @@ -128,6 +168,11 @@ RSpec.describe API::Issues do expect_paginated_array_response([issue.id, closed_issue.id]) end + it 'responds with a 401 instead of the specified issue' do + get api("/issues/#{issue.id}") + expect(response).to have_gitlab_http_status(:unauthorized) + end + context 'issues_statistics' do it 'returns authentication error without any scope' do get api('/issues_statistics') diff --git a/tests.yml b/tests.yml new file mode 100644 index 00000000000..d24cc44a403 --- /dev/null +++ b/tests.yml @@ -0,0 +1,54 @@ +mapping: + # EE code should map to respective spec + - source: ee/app/(.+)\.rb + test: ee/spec/%s_spec.rb + # FOSS code should map to respective spec + - source: app/(.+)\.rb + test: spec/%s_spec.rb + + # EE extension should also map to its FOSS class spec + - source: ee/app/(.*/)ee/(.+)\.rb + test: spec/%s%s_spec.rb + + # Some EE extensions also map to its EE class spec, but this is not recommended: + # https://docs.gitlab.com/ee/development/ee_features.html#testing-ee-features-based-on-ce-features + - source: ee/app/(.*/)ee/(.+)\.rb + test: ee/spec/%s%s_spec.rb + + # EE lib should map to respective spec + - source: ee/lib/(.+)\.rb + test: ee/spec/lib/%s_spec.rb + + # FOSS lib & tooling should map to respective spec + - source: (tooling/)?lib/(.+)\.rb + test: spec/%slib/%s_spec.rb + + # Initializers should map to respective spec + - source: config/initializers/(.+)\.rb + test: spec/initializers/%s_spec.rb + + # DB structure should map to schema spec + - source: db/structure.sql + test: spec/db/schema_spec.rb + + # Migration should map to either timestamped or non-timestamped spec + - source: db/(?:post_)?migrate/(?:[0-9]+)_(.+)\.rb + test: spec/migrations/%s_spec.rb + - source: db/(?:post_)?migrate/([0-9]+)_(.+)\.rb + test: spec/migrations/%s_%s_spec.rb + + # EE/FOSS views should map to respective spec + - source: (ee/)?app/views/(.+)\.haml + test: '%sspec/views/%s.haml_spec.rb' + + # EE/FOSS spec code should map to itself + - source: (ee/)?spec/(.+)_spec\.rb + test: '%sspec/%s_spec.rb' + + # EE extension spec should map to its FOSS class spec + - source: ee/spec/(.*/)ee/(.+)\.rb + test: spec/%s%s.rb + + # EE/FOSS factory should map to factories spec + - source: (ee/)?spec/factories/.+\.rb + test: spec/factories_spec.rb diff --git a/tooling/bin/find_foss_tests b/tooling/bin/find_foss_tests index c694210ad40..9cd8a616ad0 100755 --- a/tooling/bin/find_foss_tests +++ b/tooling/bin/find_foss_tests @@ -1,10 +1,8 @@ #!/usr/bin/env ruby # frozen_string_literal: true -require_relative '../../lib/gitlab/popen' -require_relative '../lib/tooling/test_file_finder' - require 'gitlab' +require 'test_file_finder' gitlab_token = ENV.fetch('DANGER_GITLAB_API_TOKEN', '') @@ -21,9 +19,7 @@ mr_iid = ENV.fetch('CI_MERGE_REQUEST_IID') mr_changes = Gitlab.merge_request_changes(mr_project_path, mr_iid) changed_files = mr_changes.changes.map { |change| change['new_path'] } -tests_to_run = changed_files.flat_map do |file| - test_files = Tooling::TestFileFinder.new(file, foss_test_only: true).test_files - test_files.select { |f| File.exist?(f) } -end +mapping = TestFileFinder::Mapping.load('tests.yml') +test_files = TestFileFinder::FileFinder.new(paths: changed_files, mapping: mapping).test_files -File.write(output_file, tests_to_run.uniq.join(' ')) +File.write(output_file, test_files.uniq.join(' ')) diff --git a/yarn.lock b/yarn.lock index aab6b97a34b..4c1ae0b398a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -843,15 +843,15 @@ eslint-plugin-vue "^6.2.1" vue-eslint-parser "^7.0.0" -"@gitlab/svgs@1.160.0": - version "1.160.0" - resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.160.0.tgz#1f6b3c9f587b7847be32b5a4ddc397fa5add8829" - integrity sha512-oBGeuQAdgd0UAha+YweeHiBsiukKX1fatzPFxD2f2UJodMJXZh/I8e+1yL68cLT/Sn0pmZBiYF2dzaDQHBbWkg== - -"@gitlab/ui@20.6.0": - version "20.6.0" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-20.6.0.tgz#54ca0dd156d807564fcefd5583cb730fde09b3c9" - integrity sha512-1QW9nXAD6HXCH/a+mj3qdiLX8sHtD5tOTqJeCluqL0b0y6pPs9uXkQv0AV0zvruggQ36tHQeer8SiU2Op+EOtQ== +"@gitlab/svgs@1.161.0": + version "1.161.0" + resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.161.0.tgz#661e8d19862dfba0e4c558e2eb6d64b402c1453e" + integrity sha512-qsbboEICn08ZoEoAX/TuYygsFaXlzsCY+CfmdOzqvJbOdfHhVXmrJBxd2hP2qqjTZm2PkbRRmn+03+ce1jvatQ== + +"@gitlab/ui@20.8.0": + version "20.8.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-20.8.0.tgz#00ecc698d2ed003f87fd59ea408f999c804d7dda" + integrity sha512-BHXDFtY1Zw6rIkUopAJQMoDpWlAKyDZiHR13U6Tv+Ot0G6p6tW6PVVHb12m+fycNxBDKdYisdLJwH8MKNjWRrA== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.3.0" |