summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md6
-rw-r--r--app/assets/javascripts/gpg_badges.js31
-rw-r--r--app/assets/javascripts/lib/utils/poll.js11
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js19
-rw-r--r--app/assets/javascripts/notes/stores/utils.js9
-rw-r--r--app/assets/javascripts/pages/projects/blob/show/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/show/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/tree/show/index.js6
-rw-r--r--app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue162
-rw-r--r--app/assets/javascripts/reports/store/actions.js67
-rw-r--r--app/assets/javascripts/reports/store/index.js13
-rw-r--r--app/assets/javascripts/reports/store/mutation_types.js5
-rw-r--r--app/assets/javascripts/reports/store/mutations.js26
-rw-r--r--app/assets/javascripts/reports/store/state.js28
-rw-r--r--app/assets/javascripts/vue_shared/components/clipboard_button.vue13
-rw-r--r--app/assets/stylesheets/framework/buttons.scss4
-rw-r--r--app/assets/stylesheets/pages/commits.scss6
-rw-r--r--app/finders/groups_finder.rb16
-rw-r--r--app/finders/personal_projects_finder.rb19
-rw-r--r--app/finders/projects_finder.rb17
-rw-r--r--app/helpers/button_helper.rb2
-rw-r--r--app/helpers/ci_status_helper.rb13
-rw-r--r--app/helpers/commits_helper.rb9
-rw-r--r--app/models/project.rb1
-rw-r--r--app/views/projects/blame/show.html.haml2
-rw-r--r--app/views/projects/blob/show.html.haml2
-rw-r--r--app/views/projects/commit/_commit_box.html.haml2
-rw-r--r--app/views/projects/commits/_commit.html.haml2
-rw-r--r--app/views/projects/services/prometheus/_show.html.haml2
-rw-r--r--app/views/projects/show.html.haml4
-rw-r--r--app/views/projects/tree/show.html.haml3
-rw-r--r--app/views/sherlock/queries/_general.html.haml4
-rw-r--r--changelogs/unreleased/29278-commits-page-tooltips.yml5
-rw-r--r--changelogs/unreleased/45318-vuex-store.yml5
-rw-r--r--changelogs/unreleased/api-minimal-access-level.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml5
-rw-r--r--changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml5
-rw-r--r--changelogs/unreleased/sh-freeze-banzai-filter-strings.yml5
-rw-r--r--changelogs/unreleased/tz-mr-refactor-memory-reduction.yml10
-rw-r--r--changelogs/unreleased/winh-tree-view-gpg.yml5
-rw-r--r--config/initializers/8_metrics.rb2
-rw-r--r--doc/api/groups.md10
-rw-r--r--doc/api/projects.md9
-rw-r--r--lib/api/groups.rb3
-rw-r--r--lib/api/helpers.rb1
-rw-r--r--lib/api/projects.rb1
-rw-r--r--lib/api/users.rb1
-rw-r--r--lib/banzai/filter/absolute_link_filter.rb2
-rw-r--r--lib/banzai/filter/abstract_reference_filter.rb2
-rw-r--r--lib/banzai/filter/ascii_doc_post_processing_filter.rb2
-rw-r--r--lib/banzai/filter/autolink_filter.rb2
-rw-r--r--lib/banzai/filter/blockquote_fence_filter.rb2
-rw-r--r--lib/banzai/filter/color_filter.rb2
-rw-r--r--lib/banzai/filter/commit_range_reference_filter.rb2
-rw-r--r--lib/banzai/filter/commit_reference_filter.rb2
-rw-r--r--lib/banzai/filter/commit_trailers_filter.rb2
-rw-r--r--lib/banzai/filter/emoji_filter.rb2
-rw-r--r--lib/banzai/filter/epic_reference_filter.rb2
-rw-r--r--lib/banzai/filter/external_issue_reference_filter.rb2
-rw-r--r--lib/banzai/filter/external_link_filter.rb2
-rw-r--r--lib/banzai/filter/gollum_tags_filter.rb2
-rw-r--r--lib/banzai/filter/html_entity_filter.rb2
-rw-r--r--lib/banzai/filter/image_lazy_load_filter.rb4
-rw-r--r--lib/banzai/filter/image_link_filter.rb2
-rw-r--r--lib/banzai/filter/inline_diff_filter.rb2
-rw-r--r--lib/banzai/filter/issuable_reference_filter.rb2
-rw-r--r--lib/banzai/filter/issuable_state_filter.rb2
-rw-r--r--lib/banzai/filter/issue_reference_filter.rb2
-rw-r--r--lib/banzai/filter/label_reference_filter.rb2
-rw-r--r--lib/banzai/filter/markdown_filter.rb2
-rw-r--r--lib/banzai/filter/math_filter.rb2
-rw-r--r--lib/banzai/filter/merge_request_reference_filter.rb2
-rw-r--r--lib/banzai/filter/mermaid_filter.rb2
-rw-r--r--lib/banzai/filter/milestone_reference_filter.rb2
-rw-r--r--lib/banzai/filter/plantuml_filter.rb2
-rw-r--r--lib/banzai/filter/redactor_filter.rb2
-rw-r--r--lib/banzai/filter/reference_filter.rb2
-rw-r--r--lib/banzai/filter/relative_link_filter.rb2
-rw-r--r--lib/banzai/filter/sanitization_filter.rb2
-rw-r--r--lib/banzai/filter/set_direction_filter.rb2
-rw-r--r--lib/banzai/filter/snippet_reference_filter.rb2
-rw-r--r--lib/banzai/filter/syntax_highlight_filter.rb4
-rw-r--r--lib/banzai/filter/table_of_contents_filter.rb4
-rw-r--r--lib/banzai/filter/task_list_filter.rb2
-rw-r--r--lib/banzai/filter/user_reference_filter.rb2
-rw-r--r--lib/banzai/filter/video_link_filter.rb2
-rw-r--r--lib/banzai/filter/wiki_link_filter.rb2
-rw-r--r--lib/banzai/filter/yaml_front_matter_filter.rb2
-rw-r--r--lib/gitlab/git/repository.rb4
-rw-r--r--lib/gitlab/import_export/merge_request_parser.rb1
-rw-r--r--locale/gitlab.pot2
-rw-r--r--spec/features/projects/commits/user_browses_commits_spec.rb1
-rw-r--r--spec/features/signed_commits_spec.rb10
-rw-r--r--spec/helpers/button_helper_spec.rb6
-rw-r--r--spec/javascripts/gpg_badges_spec.js76
-rw-r--r--spec/javascripts/reports/store/actions_spec.js130
-rw-r--r--spec/javascripts/reports/store/mutations_spec.js101
-rw-r--r--spec/javascripts/vue_shared/components/clipboard_button_spec.js2
-rw-r--r--spec/lib/banzai/filter/image_lazy_load_filter_spec.rb14
-rw-r--r--spec/lib/gitlab/hashed_storage/migrator_spec.rb2
-rw-r--r--spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb2
-rw-r--r--spec/migrations/migrate_stage_id_reference_in_background_spec.rb2
-rw-r--r--spec/migrations/migrate_stages_statuses_spec.rb2
-rw-r--r--spec/migrations/normalize_ldap_extern_uids_spec.rb4
-rw-r--r--spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb2
-rw-r--r--spec/migrations/schedule_merge_request_diff_migrations_spec.rb2
-rw-r--r--spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb2
-rw-r--r--spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb2
-rw-r--r--spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb2
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb4
-rw-r--r--spec/models/spam_log_spec.rb2
-rw-r--r--spec/requests/api/groups_spec.rb19
-rw-r--r--spec/requests/api/project_import_spec.rb2
-rw-r--r--spec/requests/api/projects_spec.rb30
-rw-r--r--spec/requests/api/users_spec.rb14
-rw-r--r--spec/services/groups/destroy_service_spec.rb2
-rw-r--r--spec/services/projects/create_from_template_service_spec.rb2
-rw-r--r--spec/services/projects/destroy_service_spec.rb14
-rw-r--r--spec/services/projects/housekeeping_service_spec.rb2
-rw-r--r--spec/services/users/destroy_service_spec.rb2
-rw-r--r--spec/workers/storage_migrator_worker_spec.rb2
121 files changed, 902 insertions, 232 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 631f80c6bd9..ad8022e972f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -376,8 +376,14 @@ on those issues. Please select someone with relevant experience from the
[GitLab team][team]. If there is nobody mentioned with that expertise look in
the commit history for the affected files to find someone.
+We also use [GitLab Triage] to automate some triaging policies. This is
+currently setup as a [scheduled pipeline] running on the [`gl-triage`] branch.
+
[described in our handbook]: https://about.gitlab.com/handbook/engineering/issue-triage/
[issue bash events]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17815
+[GitLab Triage]: https://gitlab.com/gitlab-org/gitlab-triage
+[scheduled pipeline]: https://gitlab.com/gitlab-org/gitlab-ce/pipeline_schedules/3732/edit
+[`gl-triage`]: https://gitlab.com/gitlab-org/gitlab-ce/tree/gl-triage
### Feature proposals
diff --git a/app/assets/javascripts/gpg_badges.js b/app/assets/javascripts/gpg_badges.js
index 029fd6a67d4..efba6fc1aff 100644
--- a/app/assets/javascripts/gpg_badges.js
+++ b/app/assets/javascripts/gpg_badges.js
@@ -1,23 +1,36 @@
import $ from 'jquery';
import { parseQueryStringIntoObject } from '~/lib/utils/common_utils';
import axios from '~/lib/utils/axios_utils';
-import flash from '~/flash';
+import createFlash from '~/flash';
import { __ } from '~/locale';
export default class GpgBadges {
static fetch() {
- const badges = $('.js-loading-gpg-badge');
const tag = $('.js-signature-container');
+ if (tag.length === 0) {
+ return Promise.resolve();
+ }
+
+ const badges = $('.js-loading-gpg-badge');
badges.html('<i class="fa fa-spinner fa-spin"></i>');
+ const displayError = () => createFlash(__('An error occurred while loading commit signatures'));
+
+ const endpoint = tag.data('signaturesPath');
+ if (!endpoint) {
+ displayError();
+ return Promise.reject(new Error('Missing commit signatures endpoint!'));
+ }
+
const params = parseQueryStringIntoObject(tag.serialize());
- return axios.get(tag.data('signaturesPath'), { params })
- .then(({ data }) => {
- data.signatures.forEach((signature) => {
- badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html);
- });
- })
- .catch(() => flash(__('An error occurred while loading commits')));
+ return axios
+ .get(endpoint, { params })
+ .then(({ data }) => {
+ data.signatures.forEach(signature => {
+ badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html);
+ });
+ })
+ .catch(displayError);
}
}
diff --git a/app/assets/javascripts/lib/utils/poll.js b/app/assets/javascripts/lib/utils/poll.js
index 7fca80c2fdb..91d8c30744f 100644
--- a/app/assets/javascripts/lib/utils/poll.js
+++ b/app/assets/javascripts/lib/utils/poll.js
@@ -38,7 +38,7 @@ import { normalizeHeaders } from './common_utils';
* } else {
* poll.stop();
* }
-* });
+ * });
*
* 1. Checks for response and headers before start polling
* 2. Interval is provided by `Poll-Interval` header.
@@ -51,8 +51,8 @@ export default class Poll {
constructor(options = {}) {
this.options = options;
this.options.data = options.data || {};
- this.options.notificationCallback = options.notificationCallback ||
- function notificationCallback() {};
+ this.options.notificationCallback =
+ options.notificationCallback || function notificationCallback() {};
this.intervalHeader = 'POLL-INTERVAL';
this.timeoutID = null;
@@ -63,6 +63,7 @@ export default class Poll {
const headers = normalizeHeaders(response.headers);
const pollInterval = parseInt(headers[this.intervalHeader], 10);
if (pollInterval > 0 && response.status === httpStatusCodes.OK && this.canPoll) {
+ clearTimeout(this.timeoutID);
this.timeoutID = setTimeout(() => {
this.makeRequest();
}, pollInterval);
@@ -77,11 +78,11 @@ export default class Poll {
notificationCallback(true);
return resource[method](data)
- .then((response) => {
+ .then(response => {
this.checkConditions(response);
notificationCallback(false);
})
- .catch((error) => {
+ .catch(error => {
notificationCallback(false);
if (error.status === httpStatusCodes.ABORTED) {
return;
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index ab6a95e2601..e1b159142c9 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -174,27 +174,19 @@ export default {
[types.UPDATE_NOTE](state, note) {
const noteObj = utils.findNoteObjectById(state.discussions, note.discussion_id);
-
if (noteObj.individual_note) {
noteObj.notes.splice(0, 1, note);
} else {
const comment = utils.findNoteObjectById(noteObj.notes, note.id);
- noteObj.notes.splice(noteObj.notes.indexOf(comment), 1, note);
+ Object.assign(comment, note);
}
},
[types.UPDATE_DISCUSSION](state, noteData) {
const note = noteData;
- let index = 0;
-
- state.discussions.forEach((n, i) => {
- if (n.id === note.id) {
- index = i;
- }
- });
-
+ const selectedDiscussion = state.discussions.find(n => n.id === note.id);
note.expanded = true; // override expand flag to prevent collapse
- state.discussions.splice(index, 1, note);
+ Object.assign(selectedDiscussion, note);
},
[types.CLOSE_ISSUE](state) {
@@ -215,12 +207,9 @@ export default {
[types.SET_DISCUSSION_DIFF_LINES](state, { discussionId, diffLines }) {
const discussion = utils.findNoteObjectById(state.discussions, discussionId);
- const index = state.discussions.indexOf(discussion);
- const discussionWithDiffLines = Object.assign({}, discussion, {
+ Object.assign(discussion, {
truncated_diff_lines: diffLines,
});
-
- state.discussions.splice(index, 1, discussionWithDiffLines);
},
};
diff --git a/app/assets/javascripts/notes/stores/utils.js b/app/assets/javascripts/notes/stores/utils.js
index a0e096ebfaf..c4a812c5af4 100644
--- a/app/assets/javascripts/notes/stores/utils.js
+++ b/app/assets/javascripts/notes/stores/utils.js
@@ -2,13 +2,11 @@ import AjaxCache from '~/lib/utils/ajax_cache';
const REGEX_QUICK_ACTIONS = /^\/\w+.*$/gm;
-export const findNoteObjectById = (notes, id) =>
- notes.filter(n => n.id === id)[0];
+export const findNoteObjectById = (notes, id) => notes.find(n => n.id === id);
export const getQuickActionText = note => {
let text = 'Applying command';
- const quickActions =
- AjaxCache.get(gl.GfmAutoComplete.dataSources.commands) || [];
+ const quickActions = AjaxCache.get(gl.GfmAutoComplete.dataSources.commands) || [];
const executedCommands = quickActions.filter(command => {
const commandRegex = new RegExp(`/${command.name}`);
@@ -29,5 +27,4 @@ export const getQuickActionText = note => {
export const hasQuickActions = note => REGEX_QUICK_ACTIONS.test(note);
-export const stripQuickActions = note =>
- note.replace(REGEX_QUICK_ACTIONS, '').trim();
+export const stripQuickActions = note => note.replace(REGEX_QUICK_ACTIONS, '').trim();
diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js
index 85c6862d629..84e5bb3c46e 100644
--- a/app/assets/javascripts/pages/projects/blob/show/index.js
+++ b/app/assets/javascripts/pages/projects/blob/show/index.js
@@ -2,6 +2,7 @@ import Vue from 'vue';
import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
import BlobViewer from '~/blob/viewer/index';
import initBlob from '~/pages/projects/init_blob';
+import GpgBadges from '~/gpg_badges';
document.addEventListener('DOMContentLoaded', () => {
new BlobViewer(); // eslint-disable-line no-new
@@ -26,4 +27,6 @@ document.addEventListener('DOMContentLoaded', () => {
},
});
}
+
+ GpgBadges.fetch();
});
diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js
index 3b0f0f960b8..d2dc0c4570e 100644
--- a/app/assets/javascripts/pages/projects/show/index.js
+++ b/app/assets/javascripts/pages/projects/show/index.js
@@ -7,6 +7,7 @@ import TreeView from '~/tree';
import BlobViewer from '~/blob/viewer/index';
import Activities from '~/activities';
import { ajaxGet } from '~/lib/utils/common_utils';
+import GpgBadges from '~/gpg_badges';
import Star from '../../../star';
import notificationsDropdown from '../../../notifications_dropdown';
@@ -38,4 +39,6 @@ document.addEventListener('DOMContentLoaded', () => {
$(treeSlider).waitForImages(() => {
ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
});
+
+ GpgBadges.fetch();
});
diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js
index 7ad082a5e61..33d69d891d8 100644
--- a/app/assets/javascripts/pages/projects/tree/show/index.js
+++ b/app/assets/javascripts/pages/projects/tree/show/index.js
@@ -2,6 +2,7 @@ import $ from 'jquery';
import Vue from 'vue';
import initBlob from '~/blob_edit/blob_bundle';
import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
+import GpgBadges from '~/gpg_badges';
import TreeView from '../../../../tree';
import ShortcutsNavigation from '../../../../shortcuts_navigation';
import BlobViewer from '../../../../blob/viewer';
@@ -14,7 +15,8 @@ document.addEventListener('DOMContentLoaded', () => {
new BlobViewer(); // eslint-disable-line no-new
new NewCommitForm($('.js-create-dir-form')); // eslint-disable-line no-new
$('#tree-slider').waitForImages(() =>
- ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath));
+ ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath),
+ );
initBlob();
const commitPipelineStatusEl = document.querySelector('.js-commit-pipeline-status');
@@ -36,4 +38,6 @@ document.addEventListener('DOMContentLoaded', () => {
},
});
}
+
+ GpgBadges.fetch();
});
diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
index a4c7c143e56..1c1e17563a1 100644
--- a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
+++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
@@ -1,27 +1,27 @@
<script>
- import Visibility from 'visibilityjs';
- import ciIcon from '~/vue_shared/components/ci_icon.vue';
- import loadingIcon from '~/vue_shared/components/loading_icon.vue';
- import Poll from '~/lib/utils/poll';
- import Flash from '~/flash';
- import { s__, sprintf } from '~/locale';
- import tooltip from '~/vue_shared/directives/tooltip';
- import CommitPipelineService from '../services/commit_pipeline_service';
+import Visibility from 'visibilityjs';
+import ciIcon from '~/vue_shared/components/ci_icon.vue';
+import loadingIcon from '~/vue_shared/components/loading_icon.vue';
+import Poll from '~/lib/utils/poll';
+import Flash from '~/flash';
+import { s__, sprintf } from '~/locale';
+import tooltip from '~/vue_shared/directives/tooltip';
+import CommitPipelineService from '../services/commit_pipeline_service';
- export default {
- directives: {
- tooltip,
+export default {
+ directives: {
+ tooltip,
+ },
+ components: {
+ ciIcon,
+ loadingIcon,
+ },
+ props: {
+ endpoint: {
+ type: String,
+ required: true,
},
- components: {
- ciIcon,
- loadingIcon,
- },
- props: {
- endpoint: {
- type: String,
- required: true,
- },
- /* This prop can be used to replace some of the `render_commit_status`
+ /* This prop can be used to replace some of the `render_commit_status`
used across GitLab, this way we could use this vue component and add a
realtime status where it makes sense
realtime: {
@@ -29,76 +29,77 @@
required: false,
default: true,
}, */
+ },
+ data() {
+ return {
+ ciStatus: {},
+ isLoading: true,
+ };
+ },
+ computed: {
+ statusTitle() {
+ return sprintf(s__('Commits|Commit: %{commitText}'), { commitText: this.ciStatus.text });
},
- data() {
- return {
- ciStatus: {},
- isLoading: true,
- };
- },
- computed: {
- statusTitle() {
- return sprintf(s__('Commits|Commit: %{commitText}'), { commitText: this.ciStatus.text });
- },
+ },
+ mounted() {
+ this.service = new CommitPipelineService(this.endpoint);
+ this.initPolling();
+ },
+ methods: {
+ successCallback(res) {
+ const { pipelines } = res.data;
+ if (pipelines.length > 0) {
+ // The pipeline entity always keeps the latest pipeline info on the `details.status`
+ this.ciStatus = pipelines[0].details.status;
+ }
+ this.isLoading = false;
},
- mounted() {
- this.service = new CommitPipelineService(this.endpoint);
- this.initPolling();
+ errorCallback() {
+ this.ciStatus = {
+ text: 'not found',
+ icon: 'status_notfound',
+ group: 'notfound',
+ };
+ this.isLoading = false;
+ Flash(s__('Something went wrong on our end'));
},
- methods: {
- successCallback(res) {
- const { pipelines } = res.data;
- if (pipelines.length > 0) {
- // The pipeline entity always keeps the latest pipeline info on the `details.status`
- this.ciStatus = pipelines[0].details.status;
- }
- this.isLoading = false;
- },
- errorCallback() {
- this.ciStatus = {
- text: 'not found',
- icon: 'status_notfound',
- group: 'notfound',
- };
- this.isLoading = false;
- Flash(s__('Something went wrong on our end'));
- },
- initPolling() {
- this.poll = new Poll({
- resource: this.service,
- method: 'fetchData',
- successCallback: response => this.successCallback(response),
- errorCallback: this.errorCallback,
- });
+ initPolling() {
+ this.poll = new Poll({
+ resource: this.service,
+ method: 'fetchData',
+ successCallback: response => this.successCallback(response),
+ errorCallback: this.errorCallback,
+ });
+
+ if (!Visibility.hidden()) {
+ this.isLoading = true;
+ this.poll.makeRequest();
+ } else {
+ this.fetchPipelineCommitData();
+ }
+ Visibility.change(() => {
if (!Visibility.hidden()) {
- this.isLoading = true;
- this.poll.makeRequest();
+ this.poll.restart();
} else {
- this.fetchPipelineCommitData();
+ this.poll.stop();
}
-
- Visibility.change(() => {
- if (!Visibility.hidden()) {
- this.poll.restart();
- } else {
- this.poll.stop();
- }
- });
- },
- fetchPipelineCommitData() {
- this.service.fetchData()
- .then(this.successCallback)
- .catch(this.errorCallback);
- },
+ });
},
- destroy() {
- this.poll.stop();
+ fetchPipelineCommitData() {
+ this.service
+ .fetchData()
+ .then(this.successCallback)
+ .catch(this.errorCallback);
},
- };
+ },
+ destroy() {
+ this.poll.stop();
+ },
+};
</script>
<template>
- <div>
+ <div class="ci-status-link">
<loading-icon
v-if="isLoading"
label="Loading pipeline status"
@@ -113,6 +114,7 @@
:title="statusTitle"
:aria-label="statusTitle"
:status="ciStatus"
+ :size="24"
data-container="body"
/>
</a>
diff --git a/app/assets/javascripts/reports/store/actions.js b/app/assets/javascripts/reports/store/actions.js
new file mode 100644
index 00000000000..15c077b0fd8
--- /dev/null
+++ b/app/assets/javascripts/reports/store/actions.js
@@ -0,0 +1,67 @@
+import Visibility from 'visibilityjs';
+import axios from '../../lib/utils/axios_utils';
+import Poll from '../../lib/utils/poll';
+import * as types from './mutation_types';
+
+export const setEndpoint = ({ commit }, endpoint) => commit(types.SET_ENDPOINT, endpoint);
+
+export const requestReports = ({ commit }) => commit(types.REQUEST_REPORTS);
+
+let eTagPoll;
+
+export const clearEtagPoll = () => {
+ eTagPoll = null;
+};
+
+export const stopPolling = () => {
+ if (eTagPoll) eTagPoll.stop();
+};
+
+export const restartPolling = () => {
+ if (eTagPoll) eTagPoll.restart();
+};
+
+/**
+ * We need to poll the reports endpoint while they are being parsed in the Backend.
+ * This can take up to one minute.
+ *
+ * Poll.js will handle etag response.
+ * While http status code is 204, it means it's parsing, and we'll keep polling
+ * When http status code is 200, it means parsing is done, we can show the results & stop polling
+ * When http status code is 500, it means parsing went wrong and we stop polling
+ */
+export const fetchReports = ({ state, dispatch }) => {
+ dispatch('requestReports');
+
+ eTagPoll = new Poll({
+ resource: {
+ getReports(endpoint) {
+ return axios.get(endpoint);
+ },
+ },
+ data: state.endpoint,
+ method: 'getReports',
+ successCallback: ({ data }) => dispatch('receiveReportsSuccess', data),
+ errorCallback: () => dispatch('receiveReportsError'),
+ });
+
+ if (!Visibility.hidden()) {
+ eTagPoll.makeRequest();
+ }
+
+ Visibility.change(() => {
+ if (!Visibility.hidden()) {
+ dispatch('restartPolling');
+ } else {
+ dispatch('stopPolling');
+ }
+ });
+};
+
+export const receiveReportsSuccess = ({ commit }, response) =>
+ commit(types.RECEIVE_REPORTS_SUCCESS, response);
+
+export const receiveReportsError = ({ commit }) => commit(types.RECEIVE_REPORTS_ERROR);
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/reports/store/index.js b/app/assets/javascripts/reports/store/index.js
new file mode 100644
index 00000000000..af4f9688fb4
--- /dev/null
+++ b/app/assets/javascripts/reports/store/index.js
@@ -0,0 +1,13 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import * as actions from './actions';
+import mutations from './mutations';
+import state from './state';
+
+Vue.use(Vuex);
+
+export default () => new Vuex.Store({
+ actions,
+ mutations,
+ state: state(),
+});
diff --git a/app/assets/javascripts/reports/store/mutation_types.js b/app/assets/javascripts/reports/store/mutation_types.js
new file mode 100644
index 00000000000..77722974c45
--- /dev/null
+++ b/app/assets/javascripts/reports/store/mutation_types.js
@@ -0,0 +1,5 @@
+export const SET_ENDPOINT = 'SET_ENDPOINT';
+
+export const REQUEST_REPORTS = 'REQUEST_REPORTS';
+export const RECEIVE_REPORTS_SUCCESS = 'RECEIVE_REPORTS_SUCCESS';
+export const RECEIVE_REPORTS_ERROR = 'RECEIVE_REPORTS_ERROR';
diff --git a/app/assets/javascripts/reports/store/mutations.js b/app/assets/javascripts/reports/store/mutations.js
new file mode 100644
index 00000000000..d9d301826cf
--- /dev/null
+++ b/app/assets/javascripts/reports/store/mutations.js
@@ -0,0 +1,26 @@
+/* eslint-disable no-param-reassign */
+import * as types from './mutation_types';
+
+export default {
+ [types.SET_ENDPOINT](state, endpoint) {
+ state.endpoint = endpoint;
+ },
+ [types.REQUEST_REPORTS](state) {
+ state.isLoading = true;
+ },
+ [types.RECEIVE_REPORTS_SUCCESS](state, response) {
+
+ state.isLoading = false;
+
+ state.summary.total = response.summary.total;
+ state.summary.resolved = response.summary.resolved;
+ state.summary.failed = response.summary.failed;
+
+ state.reports = response.suites;
+
+ },
+ [types.RECEIVE_REPORTS_ERROR](state) {
+ state.isLoading = false;
+ state.hasError = true;
+ },
+};
diff --git a/app/assets/javascripts/reports/store/state.js b/app/assets/javascripts/reports/store/state.js
new file mode 100644
index 00000000000..97f9d0a6859
--- /dev/null
+++ b/app/assets/javascripts/reports/store/state.js
@@ -0,0 +1,28 @@
+export default () => ({
+ endpoint: null,
+
+ isLoading: false,
+ hasError: false,
+
+ summary: {
+ total: 0,
+ resolved: 0,
+ failed: 0,
+ },
+
+ /**
+ * Each report will have the following format:
+ * {
+ * name: {String},
+ * summary: {
+ * total: {Number},
+ * resolved: {Number},
+ * failed: {Number},
+ * },
+ * new_failures: {Array.<Object>},
+ * resolved_failures: {Array.<Object>},
+ * existing_failures: {Array.<Object>},
+ * }
+ */
+ reports: [],
+});
diff --git a/app/assets/javascripts/vue_shared/components/clipboard_button.vue b/app/assets/javascripts/vue_shared/components/clipboard_button.vue
index dc5760bce28..d272bf3f55f 100644
--- a/app/assets/javascripts/vue_shared/components/clipboard_button.vue
+++ b/app/assets/javascripts/vue_shared/components/clipboard_button.vue
@@ -13,12 +13,19 @@
* />
*/
import tooltip from '../directives/tooltip';
+import Icon from '../components/icon.vue';
export default {
name: 'ClipboardButton',
+
directives: {
tooltip,
},
+
+ components: {
+ Icon,
+ },
+
props: {
text: {
type: String,
@@ -58,10 +65,6 @@ export default {
type="button"
class="btn"
>
- <i
- aria-hidden="true"
- class="fa fa-clipboard"
- >
- </i>
+ <icon name="duplicate" />
</button>
</template>
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 523fcb05a87..646cedd79ed 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -294,6 +294,10 @@
.btn-clipboard {
border: 0;
padding: 0 5px;
+
+ svg {
+ top: auto;
+ }
}
.input-group-prepend,
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index f75be4e01cd..63585e26022 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -205,7 +205,7 @@
> .ci-status-link,
> .btn,
> .commit-sha-group {
- margin-left: $gl-padding-8;
+ margin-left: $gl-padding;
}
}
@@ -235,10 +235,6 @@
fill: $gl-text-color-secondary;
}
- .fa-clipboard {
- color: $gl-text-color-secondary;
- }
-
:first-child {
border-bottom-left-radius: $border-radius-default;
border-top-left-radius: $border-radius-default;
diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb
index 0754123a3cf..0eeba1d2428 100644
--- a/app/finders/groups_finder.rb
+++ b/app/finders/groups_finder.rb
@@ -8,6 +8,7 @@
# owned: boolean
# parent: Group
# all_available: boolean (defaults to true)
+# min_access_level: integer
#
# Users with full private access can see all groups. The `owned` and `parent`
# params can be used to restrict the groups that are returned.
@@ -39,6 +40,7 @@ class GroupsFinder < UnionFinder
def all_groups
return [owned_groups] if params[:owned]
+ return [groups_with_min_access_level] if min_access_level?
return [Group.all] if current_user&.full_private_access? && all_available?
groups = []
@@ -56,6 +58,16 @@ class GroupsFinder < UnionFinder
current_user.groups
end
+ def groups_with_min_access_level
+ groups = current_user
+ .groups
+ .where('members.access_level >= ?', params[:min_access_level])
+
+ Gitlab::GroupHierarchy
+ .new(groups)
+ .base_and_descendants
+ end
+
def by_parent(groups)
return groups unless params[:parent]
@@ -73,4 +85,8 @@ class GroupsFinder < UnionFinder
def all_available?
params.fetch(:all_available, true)
end
+
+ def min_access_level?
+ current_user && params[:min_access_level].present?
+ end
end
diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb
index 5aea0cb8192..18adfea747f 100644
--- a/app/finders/personal_projects_finder.rb
+++ b/app/finders/personal_projects_finder.rb
@@ -1,6 +1,7 @@
class PersonalProjectsFinder < UnionFinder
- def initialize(user)
+ def initialize(user, params = {})
@user = user
+ @params = params
end
# Finds the projects belonging to the user in "@user", limited to either
@@ -8,6 +9,8 @@ class PersonalProjectsFinder < UnionFinder
#
# current_user - When given the list of projects is limited to those only
# visible by this user.
+ # params - Optional query parameters
+ # min_access_level: integer
#
# Returns an ActiveRecord::Relation.
def execute(current_user = nil)
@@ -19,11 +22,21 @@ class PersonalProjectsFinder < UnionFinder
private
def all_projects(current_user)
- projects = []
+ return [projects_with_min_access_level(current_user)] if current_user && min_access_level?
+ projects = []
projects << @user.personal_projects.visible_to_user(current_user) if current_user
projects << @user.personal_projects.public_to_user(current_user)
-
projects
end
+
+ def projects_with_min_access_level(current_user)
+ @user
+ .personal_projects
+ .visible_to_user_and_access_level(current_user, @params[:min_access_level])
+ end
+
+ def min_access_level?
+ @params[:min_access_level].present?
+ end
end
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index b06595081e7..cac6643eff3 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -17,6 +17,7 @@
# search: string
# non_archived: boolean
# archived: 'only' or boolean
+# min_access_level: integer
#
class ProjectsFinder < UnionFinder
include CustomAttributesFilter
@@ -34,7 +35,7 @@ class ProjectsFinder < UnionFinder
user = params.delete(:user)
collection =
if user
- PersonalProjectsFinder.new(user).execute(current_user)
+ PersonalProjectsFinder.new(user, finder_params).execute(current_user)
else
init_collection
end
@@ -65,6 +66,8 @@ class ProjectsFinder < UnionFinder
def collection_with_user
if owned_projects?
current_user.owned_projects
+ elsif min_access_level?
+ current_user.authorized_projects.where('project_authorizations.access_level >= ?', params[:min_access_level])
else
if private_only?
current_user.authorized_projects
@@ -76,7 +79,7 @@ class ProjectsFinder < UnionFinder
# Builds a collection for an anonymous user.
def collection_without_user
- if private_only? || owned_projects?
+ if private_only? || owned_projects? || min_access_level?
Project.none
else
Project.public_to_user
@@ -91,6 +94,10 @@ class ProjectsFinder < UnionFinder
params[:non_public].present?
end
+ def min_access_level?
+ params[:min_access_level].present?
+ end
+
def by_ids(items)
project_ids_relation ? items.where(id: project_ids_relation) : items
end
@@ -143,4 +150,10 @@ class ProjectsFinder < UnionFinder
projects
end
end
+
+ def finder_params
+ return {} unless min_access_level?
+
+ { min_access_level: params[:min_access_level] }
+ end
end
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index 3605d6a3c95..0171a880164 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -51,7 +51,7 @@ module ButtonHelper
}
content_tag :button, button_attributes do
- concat(icon('clipboard', 'aria-hidden': 'true')) unless hide_button_icon
+ concat(sprite_icon('duplicate')) unless hide_button_icon
concat(button_text)
end
end
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index f49b5c7b51a..330959e536d 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -56,7 +56,7 @@ module CiStatusHelper
status.humanize
end
- def ci_icon_for_status(status)
+ def ci_icon_for_status(status, size: 16)
if detailed_status?(status)
return sprite_icon(status.icon)
end
@@ -85,7 +85,7 @@ module CiStatusHelper
'status_canceled'
end
- sprite_icon(icon_name, size: 16)
+ sprite_icon(icon_name, size: size)
end
def pipeline_status_cache_key(pipeline_status)
@@ -111,7 +111,8 @@ module CiStatusHelper
'commit',
commit.status(ref),
path,
- tooltip_placement: tooltip_placement)
+ tooltip_placement: tooltip_placement,
+ icon_size: 24)
end
def render_pipeline_status(pipeline, tooltip_placement: 'left')
@@ -125,16 +126,16 @@ module CiStatusHelper
Ci::Runner.instance_type.blank?
end
- def render_status_with_link(type, status, path = nil, tooltip_placement: 'left', cssclass: '', container: 'body')
+ def render_status_with_link(type, status, path = nil, tooltip_placement: 'left', cssclass: '', container: 'body', icon_size: 16)
klass = "ci-status-link ci-status-icon-#{status.dasherize} #{cssclass}"
title = "#{type.titleize}: #{ci_label_for_status(status)}"
data = { toggle: 'tooltip', placement: tooltip_placement, container: container }
if path
- link_to ci_icon_for_status(status), path,
+ link_to ci_icon_for_status(status, size: icon_size), path,
class: klass, title: title, data: data
else
- content_tag :span, ci_icon_for_status(status),
+ content_tag :span, ci_icon_for_status(status, size: icon_size),
class: klass, title: title, data: data
end
end
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index e5c3be47801..89fe90fd801 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -145,15 +145,14 @@ module CommitsHelper
person_name
end
- options = {
- class: "commit-#{options[:source]}-link has-tooltip",
- title: source_email
+ link_options = {
+ class: "commit-#{options[:source]}-link"
}
if user.nil?
- mail_to(source_email, text, options)
+ mail_to(source_email, text, link_options)
else
- link_to(text, user_path(user), options)
+ link_to(text, user_path(user), link_options)
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 7d37c3b3893..f880d728839 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -327,6 +327,7 @@ class Project < ActiveRecord::Base
scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
scope :starred_by, ->(user) { joins(:users_star_projects).where('users_star_projects.user_id': user.id) }
scope :visible_to_user, ->(user) { where(id: user.authorized_projects.select(:id).reorder(nil)) }
+ scope :visible_to_user_and_access_level, ->(user, access_level) { where(id: user.authorized_projects.where('project_authorizations.access_level >= ?', access_level).select(:id).reorder(nil)) }
scope :archived, -> { where(archived: true) }
scope :non_archived, -> { where(archived: false) }
scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct }
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index e90916e340d..ef6f5c76de6 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -18,7 +18,7 @@
- commit = blame_group[:commit]
%td.blame-commit{ class: age_map_class(commit.committed_date, project_duration) }
.commit
- = author_avatar(commit, size: 36)
+ = author_avatar(commit, size: 36, has_tooltip: false)
.commit-row-title
%span.item-title.str-truncated-100
= link_to_markdown commit.title, project_commit_path(@project, commit.id), class: "cdark", title: commit.title
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index efb8175398b..5edab38bd64 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -3,6 +3,8 @@
- page_title @blob.path, @ref
+.js-signature-container{ data: { 'signatures-path': namespace_project_signatures_path } }
+
%div{ class: container_class }
= render 'projects/last_push'
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 886dd73c33b..78522393d4b 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -10,7 +10,7 @@
%span.d-none.d-sm-inline authored
#{time_ago_with_tooltip(@commit.authored_date)}
%span= s_('ByAuthor|by')
- = author_avatar(@commit, size: 24)
+ = author_avatar(@commit, size: 24, has_tooltip: false)
%strong
= commit_author_link(@commit, avatar: true, size: 24)
- if @commit.different_committer?
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 90e55fd0fb0..feaf44e8c0a 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -19,7 +19,7 @@
%li.commit.flex-row.js-toggle-container{ id: "commit-#{commit.short_id}" }
.avatar-cell.d-none.d-sm-block
- = author_avatar(commit, size: 36)
+ = author_avatar(commit, size: 36, has_tooltip: false)
.commit-detail.flex-list
.commit-content.qa-commit-content
diff --git a/app/views/projects/services/prometheus/_show.html.haml b/app/views/projects/services/prometheus/_show.html.haml
index 9741b783db3..1d0b0265bb7 100644
--- a/app/views/projects/services/prometheus/_show.html.haml
+++ b/app/views/projects/services/prometheus/_show.html.haml
@@ -7,4 +7,4 @@
= link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus_library/metrics'), target: '_blank', rel: "noopener noreferrer"
.col-lg-9
- = render_if_exists 'projects/services/prometheus/metrics', project: @project
+ = render 'projects/services/prometheus/metrics', project: @project
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index e28accd5b43..803ecca48f7 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -8,6 +8,10 @@
= render partial: 'flash_messages', locals: { project: @project }
+- if @project.repository_exists? && !@project.empty_repo?
+ - signatures_path = namespace_project_signatures_path(project_id: @project.path, id: @project.default_branch)
+ .js-signature-container{ data: { 'signatures-path': signatures_path } }
+
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
= render "projects/last_push"
diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml
index 3b4057e56d0..ace8120eeff 100644
--- a/app/views/projects/tree/show.html.haml
+++ b/app/views/projects/tree/show.html.haml
@@ -1,11 +1,14 @@
- @no_container = true
- breadcrumb_title _("Repository")
- @content_class = "limit-container-width" unless fluid_layout
+- signatures_path = namespace_project_signatures_path(namespace_id: @project.namespace.path, project_id: @project.path, id: @ref)
- page_title @path.presence || _("Files"), @ref
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits")
+.js-signature-container{ data: { 'signatures-path': signatures_path } }
+
%div{ class: [(container_class), ("limit-container-width" unless fluid_layout)] }
= render 'projects/last_push'
= render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id)
diff --git a/app/views/sherlock/queries/_general.html.haml b/app/views/sherlock/queries/_general.html.haml
index 37747faed62..ddc089b0bd7 100644
--- a/app/views/sherlock/queries/_general.html.haml
+++ b/app/views/sherlock/queries/_general.html.haml
@@ -27,7 +27,7 @@
.card-header
.float-right
%button.js-clipboard-trigger.btn.btn-sm{ title: t('sherlock.copy_to_clipboard'), type: :button }
- %i.fa.fa-clipboard
+ = sprite_icon('duplicate')
%pre.hidden
= @query.formatted_query
%strong
@@ -42,7 +42,7 @@
.card-header
.float-right
%button.js-clipboard-trigger.btn.btn-sm{ title: t('sherlock.copy_to_clipboard'), type: :button }
- %i.fa.fa-clipboard
+ = sprite_icon('duplicate')
%pre.hidden
= @query.explain
%strong
diff --git a/changelogs/unreleased/29278-commits-page-tooltips.yml b/changelogs/unreleased/29278-commits-page-tooltips.yml
new file mode 100644
index 00000000000..d54301a1cf0
--- /dev/null
+++ b/changelogs/unreleased/29278-commits-page-tooltips.yml
@@ -0,0 +1,5 @@
+---
+title: Remove tooltips from commit author avatar and name in commit lists
+merge_request: 20674
+author:
+type: other
diff --git a/changelogs/unreleased/45318-vuex-store.yml b/changelogs/unreleased/45318-vuex-store.yml
new file mode 100644
index 00000000000..5ea89034bce
--- /dev/null
+++ b/changelogs/unreleased/45318-vuex-store.yml
@@ -0,0 +1,5 @@
+---
+title: Adds Vuex store for reports section in MR widget
+merge_request: 20709
+author:
+type: added
diff --git a/changelogs/unreleased/api-minimal-access-level.yml b/changelogs/unreleased/api-minimal-access-level.yml
new file mode 100644
index 00000000000..43cab246d69
--- /dev/null
+++ b/changelogs/unreleased/api-minimal-access-level.yml
@@ -0,0 +1,5 @@
+---
+title: Add filter for minimal access level in groups and projects API
+merge_request: 20478
+author: Marko, Peter
+type: added
diff --git a/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml b/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml
new file mode 100644
index 00000000000..69e6b7d815a
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml
@@ -0,0 +1,5 @@
+---
+title: Replace 'Sidekiq::Testing.inline!' with 'perform_enqueued_jobs'
+merge_request: 20768
+author: "@blackst0ne"
+type: other
diff --git a/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml b/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml
new file mode 100644
index 00000000000..897d673e97d
--- /dev/null
+++ b/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen strings in remaining lib/banzai/filter/*.rb files
+merge_request: 20777
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml b/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml
new file mode 100644
index 00000000000..37b397ea49f
--- /dev/null
+++ b/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen strings in lib/banzai/filter/*.rb
+merge_request: 20775
+author:
+type: performance
diff --git a/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml b/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml
index 20b72c98bc1..16003fa9cad 100644
--- a/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml
+++ b/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml
@@ -1,5 +1,5 @@
----
-title: Reduces the client side memory footprint on merge requests
-merge_request: 20744
-author:
-type: performance
+---
+title: Reduces the client side memory footprint on merge requests
+merge_request: 20744
+author:
+type: performance
diff --git a/changelogs/unreleased/winh-tree-view-gpg.yml b/changelogs/unreleased/winh-tree-view-gpg.yml
new file mode 100644
index 00000000000..84d63814a47
--- /dev/null
+++ b/changelogs/unreleased/winh-tree-view-gpg.yml
@@ -0,0 +1,5 @@
+---
+title: Display GPG status on repository and blob pages
+merge_request: 20524
+author:
+type: changed
diff --git a/config/initializers/8_metrics.rb b/config/initializers/8_metrics.rb
index fe37b7710aa..4d8d35bf6cf 100644
--- a/config/initializers/8_metrics.rb
+++ b/config/initializers/8_metrics.rb
@@ -80,8 +80,6 @@ def instrument_classes(instrumentation)
instrumentation.instrument_instance_methods(RepositoryCheck::SingleRepositoryWorker)
- instrumentation.instrument_instance_methods(Rouge::Plugins::CommonMark)
- instrumentation.instrument_instance_methods(Rouge::Plugins::Redcarpet)
instrumentation.instrument_instance_methods(Rouge::Formatters::HTMLGitlab)
[:XML, :HTML].each do |namespace|
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 11de75039ee..87be36cc815 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -10,13 +10,14 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `skip_groups` | array of integers | no | Skip the group IDs passed |
-| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) |
+| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have precedence |
| `search` | string | no | Return the list of authorized groups matching the search criteria |
| `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
| `statistics` | boolean | no | Include group statistics (admins only) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
-| `owned` | boolean | no | Limit to groups owned by the current user |
+| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
+| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) |
```
GET /groups
@@ -94,13 +95,14 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) of the parent group |
| `skip_groups` | array of integers | no | Skip the group IDs passed |
-| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) |
+| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have precedence |
| `search` | string | no | Return the list of authorized groups matching the search criteria |
| `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
| `statistics` | boolean | no | Include group statistics (admins only) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
-| `owned` | boolean | no | Limit to groups owned by the current user |
+| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
+| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) |
```
GET /groups/:id/subgroups
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 9409afc88a8..f360b49c293 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -48,7 +48,7 @@ GET /projects
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of projects matching the search criteria |
| `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
-| `owned` | boolean | no | Limit by projects owned by the current user |
+| `owned` | boolean | no | Limit by projects explicitly owned by the current user |
| `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics |
@@ -57,6 +57,7 @@ GET /projects
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
| `wiki_checksum_failed` | boolean | no | Limit projects where the wiki checksum calculation has failed _([Introduced][ee-6137] in [GitLab Premium][eep] 11.2)_ |
| `repository_checksum_failed` | boolean | no | Limit projects where the repository checksum calculation has failed _([Introduced][ee-6137] in [GitLab Premium][eep] 11.2)_ |
+| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
When `simple=true` or the user is unauthenticated this returns something like:
@@ -273,13 +274,14 @@ GET /users/:user_id/projects
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of projects matching the search criteria |
| `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
-| `owned` | boolean | no | Limit by projects owned by the current user |
+| `owned` | boolean | no | Limit by projects explicitly owned by the current user |
| `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
+| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
```json
[
@@ -769,13 +771,14 @@ GET /projects/:id/forks
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of projects matching the search criteria |
| `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
-| `owned` | boolean | no | Limit by projects owned by the current user |
+| `owned` | boolean | no | Limit by projects explicitly owned by the current user |
| `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
+| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/forks"
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 797b04df059..b4f441f6a4f 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -34,11 +34,12 @@ module API
optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user'
optional :order_by, type: String, values: %w[name path id], default: 'name', desc: 'Order by name, path or id'
optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
+ optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Minimum access level of authenticated user'
use :pagination
end
def find_groups(params, parent_id = nil)
- find_params = params.slice(:all_available, :custom_attributes, :owned)
+ find_params = params.slice(:all_available, :custom_attributes, :owned, :min_access_level)
find_params[:parent] = find_group!(parent_id) if parent_id
find_params[:all_available] =
find_params.fetch(:all_available, current_user&.full_private_access?)
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index f7737468148..be17653dbb2 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -389,6 +389,7 @@ module API
finder_params[:search] = params[:search] if params[:search]
finder_params[:user] = params.delete(:user) if params[:user]
finder_params[:custom_attributes] = params[:custom_attributes] if params[:custom_attributes]
+ finder_params[:min_access_level] = params[:min_access_level] if params[:min_access_level]
finder_params
end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 889e3d4f819..eadde7b17bb 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -54,6 +54,7 @@ module API
optional :membership, type: Boolean, default: false, desc: 'Limit by projects that the current user is a member of'
optional :with_issues_enabled, type: Boolean, default: false, desc: 'Limit by enabled issues feature'
optional :with_merge_requests_enabled, type: Boolean, default: false, desc: 'Limit by enabled merge requests feature'
+ optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user'
use :optional_filter_params_ee
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 5aaaf104dff..6da6c2b43de 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -42,6 +42,7 @@ module API
optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups'
optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
optional :avatar, type: File, desc: 'Avatar image for user'
+ optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user'
all_or_none_of :extern_uid, :provider
end
diff --git a/lib/banzai/filter/absolute_link_filter.rb b/lib/banzai/filter/absolute_link_filter.rb
index 1ec6201523f..04ec568eee3 100644
--- a/lib/banzai/filter/absolute_link_filter.rb
+++ b/lib/banzai/filter/absolute_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index b39b11009b3..ad0806df8e6 100644
--- a/lib/banzai/filter/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# Issues, Merge Requests, Snippets, Commits and Commit Ranges share
diff --git a/lib/banzai/filter/ascii_doc_post_processing_filter.rb b/lib/banzai/filter/ascii_doc_post_processing_filter.rb
index c9fcf057c5f..88439f06b5f 100644
--- a/lib/banzai/filter/ascii_doc_post_processing_filter.rb
+++ b/lib/banzai/filter/ascii_doc_post_processing_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class AsciiDocPostProcessingFilter < HTML::Pipeline::Filter
diff --git a/lib/banzai/filter/autolink_filter.rb b/lib/banzai/filter/autolink_filter.rb
index 4a143baeef6..deda4b1872e 100644
--- a/lib/banzai/filter/autolink_filter.rb
+++ b/lib/banzai/filter/autolink_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/blockquote_fence_filter.rb b/lib/banzai/filter/blockquote_fence_filter.rb
index 7108e828c6d..ad367cc5efe 100644
--- a/lib/banzai/filter/blockquote_fence_filter.rb
+++ b/lib/banzai/filter/blockquote_fence_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class BlockquoteFenceFilter < HTML::Pipeline::TextFilter
diff --git a/lib/banzai/filter/color_filter.rb b/lib/banzai/filter/color_filter.rb
index 6ab29ac281f..6d9bdb9cbd3 100644
--- a/lib/banzai/filter/color_filter.rb
+++ b/lib/banzai/filter/color_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that renders `color` followed by a color "chip".
diff --git a/lib/banzai/filter/commit_range_reference_filter.rb b/lib/banzai/filter/commit_range_reference_filter.rb
index 01b3b0dafb9..d6b46236a49 100644
--- a/lib/banzai/filter/commit_range_reference_filter.rb
+++ b/lib/banzai/filter/commit_range_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces commit range references with links.
diff --git a/lib/banzai/filter/commit_reference_filter.rb b/lib/banzai/filter/commit_reference_filter.rb
index 8cd92a1adba..c3e5ac41cb8 100644
--- a/lib/banzai/filter/commit_reference_filter.rb
+++ b/lib/banzai/filter/commit_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces commit references with links.
diff --git a/lib/banzai/filter/commit_trailers_filter.rb b/lib/banzai/filter/commit_trailers_filter.rb
index 7b55e8b36f6..f49c4b403db 100644
--- a/lib/banzai/filter/commit_trailers_filter.rb
+++ b/lib/banzai/filter/commit_trailers_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces users' names and emails in commit trailers
diff --git a/lib/banzai/filter/emoji_filter.rb b/lib/banzai/filter/emoji_filter.rb
index 4eccd9d5ed5..c87948a30bf 100644
--- a/lib/banzai/filter/emoji_filter.rb
+++ b/lib/banzai/filter/emoji_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces :emoji: and unicode with images.
diff --git a/lib/banzai/filter/epic_reference_filter.rb b/lib/banzai/filter/epic_reference_filter.rb
index 265924abe24..e06e2fb3870 100644
--- a/lib/banzai/filter/epic_reference_filter.rb
+++ b/lib/banzai/filter/epic_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# The actual filter is implemented in the EE mixin
diff --git a/lib/banzai/filter/external_issue_reference_filter.rb b/lib/banzai/filter/external_issue_reference_filter.rb
index ed01a72ff9f..b4a7a44e109 100644
--- a/lib/banzai/filter/external_issue_reference_filter.rb
+++ b/lib/banzai/filter/external_issue_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces external issue tracker references with links.
diff --git a/lib/banzai/filter/external_link_filter.rb b/lib/banzai/filter/external_link_filter.rb
index d6327ef31cb..2e6d742de27 100644
--- a/lib/banzai/filter/external_link_filter.rb
+++ b/lib/banzai/filter/external_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML Filter to modify the attributes of external links
diff --git a/lib/banzai/filter/gollum_tags_filter.rb b/lib/banzai/filter/gollum_tags_filter.rb
index bb9f488cd87..0c1bbd2d250 100644
--- a/lib/banzai/filter/gollum_tags_filter.rb
+++ b/lib/banzai/filter/gollum_tags_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML Filter for parsing Gollum's tags in HTML. It's only parses the
diff --git a/lib/banzai/filter/html_entity_filter.rb b/lib/banzai/filter/html_entity_filter.rb
index e008fd428b0..406c2d3c96b 100644
--- a/lib/banzai/filter/html_entity_filter.rb
+++ b/lib/banzai/filter/html_entity_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'erb'
module Banzai
diff --git a/lib/banzai/filter/image_lazy_load_filter.rb b/lib/banzai/filter/image_lazy_load_filter.rb
index 4cd9b02b76c..afaee70f351 100644
--- a/lib/banzai/filter/image_lazy_load_filter.rb
+++ b/lib/banzai/filter/image_lazy_load_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that moves the value of image `src` attributes to `data-src`
@@ -5,7 +7,7 @@ module Banzai
class ImageLazyLoadFilter < HTML::Pipeline::Filter
def call
doc.xpath('descendant-or-self::img').each do |img|
- img['class'] ||= '' << 'lazy'
+ img.add_class('lazy')
img['data-src'] = img['src']
img['src'] = LazyImageTagHelper.placeholder_image
end
diff --git a/lib/banzai/filter/image_link_filter.rb b/lib/banzai/filter/image_link_filter.rb
index f318c425962..884a94fb761 100644
--- a/lib/banzai/filter/image_link_filter.rb
+++ b/lib/banzai/filter/image_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that wraps links around inline images.
diff --git a/lib/banzai/filter/inline_diff_filter.rb b/lib/banzai/filter/inline_diff_filter.rb
index 73e82a4d7e3..e9ddc6e0e3d 100644
--- a/lib/banzai/filter/inline_diff_filter.rb
+++ b/lib/banzai/filter/inline_diff_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class InlineDiffFilter < HTML::Pipeline::Filter
diff --git a/lib/banzai/filter/issuable_reference_filter.rb b/lib/banzai/filter/issuable_reference_filter.rb
index 7addf09be73..2963cba91e8 100644
--- a/lib/banzai/filter/issuable_reference_filter.rb
+++ b/lib/banzai/filter/issuable_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class IssuableReferenceFilter < AbstractReferenceFilter
diff --git a/lib/banzai/filter/issuable_state_filter.rb b/lib/banzai/filter/issuable_state_filter.rb
index 1a415232545..d7fe012883d 100644
--- a/lib/banzai/filter/issuable_state_filter.rb
+++ b/lib/banzai/filter/issuable_state_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that appends state information to issuable links.
diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb
index 6877cae8c55..f85be042999 100644
--- a/lib/banzai/filter/issue_reference_filter.rb
+++ b/lib/banzai/filter/issue_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces issue references with links. References to
diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb
index a5f38046a43..b92e9e55bb9 100644
--- a/lib/banzai/filter/label_reference_filter.rb
+++ b/lib/banzai/filter/label_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces label references with links.
diff --git a/lib/banzai/filter/markdown_filter.rb b/lib/banzai/filter/markdown_filter.rb
index 944363f17d3..cdf758472c1 100644
--- a/lib/banzai/filter/markdown_filter.rb
+++ b/lib/banzai/filter/markdown_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class MarkdownFilter < HTML::Pipeline::TextFilter
diff --git a/lib/banzai/filter/math_filter.rb b/lib/banzai/filter/math_filter.rb
index b6e784c886b..9d1bc3cf60c 100644
--- a/lib/banzai/filter/math_filter.rb
+++ b/lib/banzai/filter/math_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/merge_request_reference_filter.rb b/lib/banzai/filter/merge_request_reference_filter.rb
index 10c40568006..7098767b583 100644
--- a/lib/banzai/filter/merge_request_reference_filter.rb
+++ b/lib/banzai/filter/merge_request_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces merge request references with links. References
diff --git a/lib/banzai/filter/mermaid_filter.rb b/lib/banzai/filter/mermaid_filter.rb
index 65c131e08d9..7c8b165a330 100644
--- a/lib/banzai/filter/mermaid_filter.rb
+++ b/lib/banzai/filter/mermaid_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class MermaidFilter < HTML::Pipeline::Filter
diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb
index af8448937b3..328c8c1803b 100644
--- a/lib/banzai/filter/milestone_reference_filter.rb
+++ b/lib/banzai/filter/milestone_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces milestone references with links.
diff --git a/lib/banzai/filter/plantuml_filter.rb b/lib/banzai/filter/plantuml_filter.rb
index 28933c78966..caba8955bac 100644
--- a/lib/banzai/filter/plantuml_filter.rb
+++ b/lib/banzai/filter/plantuml_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "nokogiri"
require "asciidoctor-plantuml/plantuml"
diff --git a/lib/banzai/filter/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb
index caf11fe94c4..1f091f594f8 100644
--- a/lib/banzai/filter/redactor_filter.rb
+++ b/lib/banzai/filter/redactor_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that removes references to records that the current user does
diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb
index 2411dd2cdfc..e5164e7f72a 100644
--- a/lib/banzai/filter/reference_filter.rb
+++ b/lib/banzai/filter/reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# Base class for GitLab Flavored Markdown reference filters.
diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb
index 262458a872a..8e838d04bad 100644
--- a/lib/banzai/filter/relative_link_filter.rb
+++ b/lib/banzai/filter/relative_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb
index 8275bb9e149..80b9d3d045f 100644
--- a/lib/banzai/filter/sanitization_filter.rb
+++ b/lib/banzai/filter/sanitization_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# Sanitize HTML
diff --git a/lib/banzai/filter/set_direction_filter.rb b/lib/banzai/filter/set_direction_filter.rb
index c2976aeb7c6..45b259a2faf 100644
--- a/lib/banzai/filter/set_direction_filter.rb
+++ b/lib/banzai/filter/set_direction_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that sets dir="auto" for RTL languages support
diff --git a/lib/banzai/filter/snippet_reference_filter.rb b/lib/banzai/filter/snippet_reference_filter.rb
index 881e10afb9f..f4b6edb6174 100644
--- a/lib/banzai/filter/snippet_reference_filter.rb
+++ b/lib/banzai/filter/snippet_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces snippet references with links. References to
diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index 6dbf0d68fe8..8a7f9045c24 100644
--- a/lib/banzai/filter/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rouge/plugins/common_mark'
require 'rouge/plugins/redcarpet'
@@ -15,7 +17,7 @@ module Banzai
end
def highlight_node(node)
- css_classes = 'code highlight js-syntax-highlight'
+ css_classes = +'code highlight js-syntax-highlight'
lang = node.attr('lang')
retried = false
diff --git a/lib/banzai/filter/table_of_contents_filter.rb b/lib/banzai/filter/table_of_contents_filter.rb
index b32660a8341..c6d1e028eaa 100644
--- a/lib/banzai/filter/table_of_contents_filter.rb
+++ b/lib/banzai/filter/table_of_contents_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that adds an anchor child element to all Headers in a
@@ -19,7 +21,7 @@ module Banzai
def call
return doc if context[:no_header_anchors]
- result[:toc] = ""
+ result[:toc] = +""
headers = Hash.new(0)
header_root = current_header = HeaderNode.new
diff --git a/lib/banzai/filter/task_list_filter.rb b/lib/banzai/filter/task_list_filter.rb
index 9fa5f589f3e..ef35a49edcb 100644
--- a/lib/banzai/filter/task_list_filter.rb
+++ b/lib/banzai/filter/task_list_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'task_list/filter'
module Banzai
diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb
index c7fa8a8119f..11960047e5b 100644
--- a/lib/banzai/filter/user_reference_filter.rb
+++ b/lib/banzai/filter/user_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces user or group references with links.
diff --git a/lib/banzai/filter/video_link_filter.rb b/lib/banzai/filter/video_link_filter.rb
index 35cb10eae5d..0fb59c914c3 100644
--- a/lib/banzai/filter/video_link_filter.rb
+++ b/lib/banzai/filter/video_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# Find every image that isn't already wrapped in an `a` tag, and that has
diff --git a/lib/banzai/filter/wiki_link_filter.rb b/lib/banzai/filter/wiki_link_filter.rb
index 269d5bf74fa..870721f895d 100644
--- a/lib/banzai/filter/wiki_link_filter.rb
+++ b/lib/banzai/filter/wiki_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/yaml_front_matter_filter.rb b/lib/banzai/filter/yaml_front_matter_filter.rb
index 58e3e81209e..295964dd75d 100644
--- a/lib/banzai/filter/yaml_front_matter_filter.rb
+++ b/lib/banzai/filter/yaml_front_matter_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class YamlFrontMatterFilter < HTML::Pipeline::Filter
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 21ac43f80fd..7bd5927d15e 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -826,6 +826,10 @@ module Gitlab
end
end
+ # This method, fetch_ref, is used from within
+ # Gitlab::Git::OperationService. OperationService will eventually only
+ # exist in gitaly-ruby. When we delete OperationService from gitlab-ce
+ # we can also remove fetch_ref.
def fetch_ref(source_repository, source_ref:, target_ref:)
Gitlab::Git.check_namespace!(source_repository)
source_repository = RemoteRepository.new(source_repository) unless source_repository.is_a?(RemoteRepository)
diff --git a/lib/gitlab/import_export/merge_request_parser.rb b/lib/gitlab/import_export/merge_request_parser.rb
index f3d7407383c..d0527f014a7 100644
--- a/lib/gitlab/import_export/merge_request_parser.rb
+++ b/lib/gitlab/import_export/merge_request_parser.rb
@@ -25,6 +25,7 @@ module Gitlab
@project.repository.create_branch(@merge_request.target_branch, @merge_request.target_branch_sha)
end
+ # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1295
def fetch_ref
@project.repository.fetch_ref(@project.repository, source_ref: @diff_head_sha, target_ref: @merge_request.source_branch)
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 75b88a2cb2f..09a35b5da07 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -465,7 +465,7 @@ msgstr ""
msgid "An error occurred while importing project: ${details}"
msgstr ""
-msgid "An error occurred while loading commits"
+msgid "An error occurred while loading commit signatures"
msgstr ""
msgid "An error occurred while loading diff"
diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb
index 23d8d606790..534cfe1eb12 100644
--- a/spec/features/projects/commits/user_browses_commits_spec.rb
+++ b/spec/features/projects/commits/user_browses_commits_spec.rb
@@ -238,6 +238,5 @@ def check_author_link(email, author)
author_link = find('.commit-author-link')
expect(author_link['href']).to eq(user_path(author))
- expect(author_link['title']).to eq(email)
expect(find('.commit-author-name').text).to eq(author.name)
end
diff --git a/spec/features/signed_commits_spec.rb b/spec/features/signed_commits_spec.rb
index 3d05474dca2..5003eb508c2 100644
--- a/spec/features/signed_commits_spec.rb
+++ b/spec/features/signed_commits_spec.rb
@@ -7,7 +7,7 @@ describe 'GPG signed commits', :js do
user = create :user, email: 'unrelated.user@example.org'
project.add_maintainer(user)
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
create :gpg_key, key: GpgHelpers::User1.public_key, user: user
end
@@ -21,7 +21,7 @@ describe 'GPG signed commits', :js do
end
# user changes his email which makes the gpg key verified
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
user.skip_reconfirmation!
user.update!(email: GpgHelpers::User1.emails.first)
end
@@ -48,7 +48,7 @@ describe 'GPG signed commits', :js do
end
# user adds the gpg key which makes the signature valid
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
create :gpg_key, key: GpgHelpers::User1.public_key, user: user
end
@@ -66,7 +66,7 @@ describe 'GPG signed commits', :js do
end
let(:user_1_key) do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
create :gpg_key, key: GpgHelpers::User1.public_key, user: user_1
end
end
@@ -79,7 +79,7 @@ describe 'GPG signed commits', :js do
end
let(:user_2_key) do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
create :gpg_key, key: GpgHelpers::User2.public_key, user: user_2
end
end
diff --git a/spec/helpers/button_helper_spec.rb b/spec/helpers/button_helper_spec.rb
index fee8df10129..630f3eff258 100644
--- a/spec/helpers/button_helper_spec.rb
+++ b/spec/helpers/button_helper_spec.rb
@@ -121,6 +121,8 @@ describe ButtonHelper do
end
describe 'clipboard_button' do
+ include IconsHelper
+
let(:user) { create(:user) }
let(:project) { build_stubbed(:project) }
@@ -145,7 +147,7 @@ describe ButtonHelper do
expect(element.attr('data-clipboard-text')).to eq(nil)
expect(element.inner_text).to eq("")
- expect(element).to have_selector('.fa.fa-clipboard')
+ expect(element.to_html).to include sprite_icon('duplicate')
end
end
@@ -178,7 +180,7 @@ describe ButtonHelper do
context 'with `hide_button_icon` attribute provided' do
it 'shows copy to clipboard button without tooltip support' do
- expect(element(hide_button_icon: true)).not_to have_selector('.fa.fa-clipboard')
+ expect(element(hide_button_icon: true).to_html).not_to include sprite_icon('duplicate')
end
end
end
diff --git a/spec/javascripts/gpg_badges_spec.js b/spec/javascripts/gpg_badges_spec.js
index 97c771dcfd3..78330dd9633 100644
--- a/spec/javascripts/gpg_badges_spec.js
+++ b/spec/javascripts/gpg_badges_spec.js
@@ -1,23 +1,27 @@
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import GpgBadges from '~/gpg_badges';
+import { TEST_HOST } from 'spec/test_constants';
describe('GpgBadges', () => {
let mock;
const dummyCommitSha = 'n0m0rec0ffee';
const dummyBadgeHtml = 'dummy html';
const dummyResponse = {
- signatures: [{
- commit_sha: dummyCommitSha,
- html: dummyBadgeHtml,
- }],
+ signatures: [
+ {
+ commit_sha: dummyCommitSha,
+ html: dummyBadgeHtml,
+ },
+ ],
};
+ const dummyUrl = `${TEST_HOST}/dummy/signatures`;
beforeEach(() => {
mock = new MockAdapter(axios);
setFixtures(`
<form
- class="commits-search-form js-signature-container" data-signatures-path="/hello" action="/hello"
+ class="commits-search-form js-signature-container" data-signatures-path="${dummyUrl}" action="${dummyUrl}"
method="get">
<input name="utf8" type="hidden" value="✓">
<input type="search" name="search" id="commits-search"class="form-control search-text-input input-short">
@@ -32,25 +36,55 @@ describe('GpgBadges', () => {
mock.restore();
});
- it('displays a loading spinner', (done) => {
- mock.onGet('/hello').reply(200);
+ it('does not make a request if there is no container element', done => {
+ setFixtures('');
+ spyOn(axios, 'get');
- GpgBadges.fetch().then(() => {
- expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null);
- const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin');
- expect(spinners.length).toBe(1);
- done();
- }).catch(done.fail);
+ GpgBadges.fetch()
+ .then(() => {
+ expect(axios.get).not.toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('replaces the loading spinner', (done) => {
- mock.onGet('/hello').reply(200, dummyResponse);
+ it('throws an error if the endpoint is missing', done => {
+ setFixtures('<div class="js-signature-container"></div>');
+ spyOn(axios, 'get');
- GpgBadges.fetch().then(() => {
- expect(document.querySelector('.js-loading-gpg-badge')).toBe(null);
- const parentContainer = document.querySelector('.parent-container');
- expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml);
- done();
- }).catch(done.fail);
+ GpgBadges.fetch()
+ .then(() => done.fail('Expected error to be thrown'))
+ .catch(error => {
+ expect(error.message).toBe('Missing commit signatures endpoint!');
+ expect(axios.get).not.toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('displays a loading spinner', done => {
+ mock.onGet(dummyUrl).replyOnce(200);
+
+ GpgBadges.fetch()
+ .then(() => {
+ expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null);
+ const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin');
+ expect(spinners.length).toBe(1);
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('replaces the loading spinner', done => {
+ mock.onGet(dummyUrl).replyOnce(200, dummyResponse);
+
+ GpgBadges.fetch()
+ .then(() => {
+ expect(document.querySelector('.js-loading-gpg-badge')).toBe(null);
+ const parentContainer = document.querySelector('.parent-container');
+ expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml);
+ done();
+ })
+ .catch(done.fail);
});
});
diff --git a/spec/javascripts/reports/store/actions_spec.js b/spec/javascripts/reports/store/actions_spec.js
new file mode 100644
index 00000000000..c714c5af156
--- /dev/null
+++ b/spec/javascripts/reports/store/actions_spec.js
@@ -0,0 +1,130 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import {
+ setEndpoint,
+ requestReports,
+ fetchReports,
+ stopPolling,
+ clearEtagPoll,
+ receiveReportsSuccess,
+ receiveReportsError,
+} from '~/reports/store/actions';
+import state from '~/reports/store/state';
+import * as types from '~/reports/store/mutation_types';
+import testAction from 'spec/helpers/vuex_action_helper';
+import { TEST_HOST } from 'spec/test_constants';
+
+describe('Reports Store Actions', () => {
+ let mockedState;
+
+ beforeEach(() => {
+ mockedState = state();
+ });
+
+ describe('setEndpoint', () => {
+ it('should commit SET_ENDPOINT mutation', done => {
+ testAction(
+ setEndpoint,
+ 'endpoint.json',
+ mockedState,
+ [{ type: types.SET_ENDPOINT, payload: 'endpoint.json' }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('requestReports', () => {
+ it('should commit REQUEST_REPORTS mutation', done => {
+ testAction(requestReports, null, mockedState, [{ type: types.REQUEST_REPORTS }], [], done);
+ });
+ });
+
+ describe('fetchReports', () => {
+ let mock;
+
+ beforeEach(() => {
+ mockedState.endpoint = `${TEST_HOST}/endpoint.json`;
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ stopPolling();
+ clearEtagPoll();
+ });
+
+ describe('success', () => {
+ it('dispatches requestReports and receiveReportsSuccess ', done => {
+ mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, { summary: {}, suites: [{ name: 'rspec' }] });
+
+ testAction(
+ fetchReports,
+ null,
+ mockedState,
+ [],
+ [
+ {
+ type: 'requestReports',
+ },
+ {
+ payload: { summary: {}, suites: [{ name: 'rspec' }] },
+ type: 'receiveReportsSuccess',
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500);
+ });
+
+ it('dispatches requestReports and receiveReportsError ', done => {
+ testAction(
+ fetchReports,
+ null,
+ mockedState,
+ [],
+ [
+ {
+ type: 'requestReports',
+ },
+ {
+ type: 'receiveReportsError',
+ },
+ ],
+ done,
+ );
+ });
+ });
+ });
+
+ describe('receiveReportsSuccess', () => {
+ it('should commit RECEIVE_REPORTS_SUCCESS mutation', done => {
+ testAction(
+ receiveReportsSuccess,
+ { summary: {} },
+ mockedState,
+ [{ type: types.RECEIVE_REPORTS_SUCCESS, payload: { summary: {} } }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveReportsError', () => {
+ it('should commit RECEIVE_REPORTS_ERROR mutation', done => {
+ testAction(
+ receiveReportsError,
+ null,
+ mockedState,
+ [{ type: types.RECEIVE_REPORTS_ERROR }],
+ [],
+ done,
+ );
+ });
+ });
+});
diff --git a/spec/javascripts/reports/store/mutations_spec.js b/spec/javascripts/reports/store/mutations_spec.js
new file mode 100644
index 00000000000..3e0b15438c3
--- /dev/null
+++ b/spec/javascripts/reports/store/mutations_spec.js
@@ -0,0 +1,101 @@
+import state from '~/reports/store/state';
+import mutations from '~/reports/store/mutations';
+import * as types from '~/reports/store/mutation_types';
+
+describe('Reports Store Mutations', () => {
+ let stateCopy;
+
+ beforeEach(() => {
+ stateCopy = state();
+ });
+
+ describe('SET_ENDPOINT', () => {
+ it('should set endpoint', () => {
+ mutations[types.SET_ENDPOINT](stateCopy, 'endpoint.json');
+ expect(stateCopy.endpoint).toEqual('endpoint.json');
+ });
+ });
+
+ describe('REQUEST_REPORTS', () => {
+ it('should set isLoading to true', () => {
+ mutations[types.REQUEST_REPORTS](stateCopy);
+ expect(stateCopy.isLoading).toEqual(true);
+ });
+ });
+
+ describe('RECEIVE_REPORTS_SUCCESS', () => {
+ const mockedResponse = {
+ summary: {
+ total: 14,
+ resolved: 0,
+ failed: 7,
+ },
+ suites: [
+ {
+ name: 'build:linux',
+ summary: {
+ total: 2,
+ resolved: 0,
+ failed: 1,
+ },
+ new_failures: [
+ {
+ name: 'StringHelper#concatenate when a is git and b is lab returns summary',
+ execution_time: 0.0092435,
+ system_output:
+ 'Failure/Error: is_expected.to eq(\'gitlab\')',
+ },
+ ],
+ resolved_failures: [
+ {
+ name: 'StringHelper#concatenate when a is git and b is lab returns summary',
+ execution_time: 0.009235,
+ system_output:
+ 'Failure/Error: is_expected.to eq(\'gitlab\')',
+ },
+ ],
+ existing_failures: [
+ {
+ name: 'StringHelper#concatenate when a is git and b is lab returns summary',
+ execution_time: 1232.08,
+ system_output:
+ 'Failure/Error: is_expected.to eq(\'gitlab\')',
+ },
+ ],
+ },
+ ],
+ };
+
+ beforeEach(() => {
+ mutations[types.RECEIVE_REPORTS_SUCCESS](stateCopy, mockedResponse);
+ });
+
+ it('should reset isLoading', () => {
+ expect(stateCopy.isLoading).toEqual(false);
+ });
+
+ it('should set summary counts', () => {
+ expect(stateCopy.summary.total).toEqual(mockedResponse.summary.total);
+ expect(stateCopy.summary.resolved).toEqual(mockedResponse.summary.resolved);
+ expect(stateCopy.summary.failed).toEqual(mockedResponse.summary.failed);
+ });
+
+ it('should set reports', () => {
+ expect(stateCopy.reports).toEqual(mockedResponse.suites);
+ });
+ });
+
+ describe('RECEIVE_REPORTS_ERROR', () => {
+ beforeEach(() => {
+ mutations[types.RECEIVE_REPORTS_ERROR](stateCopy);
+ });
+ it('should reset isLoading', () => {
+ expect(stateCopy.isLoading).toEqual(false);
+ });
+
+ it('should set hasError to true', () => {
+ expect(stateCopy.hasError).toEqual(true);
+ });
+
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/clipboard_button_spec.js b/spec/javascripts/vue_shared/components/clipboard_button_spec.js
index 97f0fbb04db..e135690349e 100644
--- a/spec/javascripts/vue_shared/components/clipboard_button_spec.js
+++ b/spec/javascripts/vue_shared/components/clipboard_button_spec.js
@@ -21,7 +21,7 @@ describe('clipboard button', () => {
it('renders a button for clipboard', () => {
expect(vm.$el.tagName).toEqual('BUTTON');
expect(vm.$el.getAttribute('data-clipboard-text')).toEqual('copy me');
- expect(vm.$el.querySelector('i').className).toEqual('fa fa-clipboard');
+ expect(vm.$el).toHaveSpriteIcon('duplicate');
});
it('should have a tooltip with default values', () => {
diff --git a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
index 41f957c4e00..d06c5535309 100644
--- a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
+++ b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
@@ -7,6 +7,20 @@ describe Banzai::Filter::ImageLazyLoadFilter do
%(<img src="#{path}" />)
end
+ def image_with_class(path, class_attr = nil)
+ %(<img src="#{path}" class="#{class_attr}"/>)
+ end
+
+ it 'adds a class attribute' do
+ doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
+ expect(doc.at_css('img')['class']).to eq 'lazy'
+ end
+
+ it 'appends to the current class attribute' do
+ doc = filter(image_with_class('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg', 'test'))
+ expect(doc.at_css('img')['class']).to eq 'test lazy'
+ end
+
it 'transforms the image src to a data-src' do
doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
expect(doc.at_css('img')['data-src']).to eq '/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'
diff --git a/spec/lib/gitlab/hashed_storage/migrator_spec.rb b/spec/lib/gitlab/hashed_storage/migrator_spec.rb
index 813ae43b4d3..7eac2cacb90 100644
--- a/spec/lib/gitlab/hashed_storage/migrator_spec.rb
+++ b/spec/lib/gitlab/hashed_storage/migrator_spec.rb
@@ -65,7 +65,7 @@ describe Gitlab::HashedStorage::Migrator do
end
it 'migrate project' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
subject.migrate(project)
end
diff --git a/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb b/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb
index 4395e2f8264..5c6f213e15b 100644
--- a/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb
+++ b/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb
@@ -31,7 +31,7 @@ describe ScheduleSetConfidentialNoteEventsOnServices, :migration, :sidekiq do
end
it 'correctly processes services' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(services_table.where(confidential_note_events: nil).count).to eq 4
expect(services_table.where(confidential_note_events: true).count).to eq 1
diff --git a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb b/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
index a837498e1b1..dd6f5325750 100644
--- a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
+++ b/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
@@ -44,7 +44,7 @@ describe MigrateStageIdReferenceInBackground, :migration, :sidekiq do
end
it 'schedules background migrations' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(jobs.where(stage_id: nil).count).to eq 5
migrate!
diff --git a/spec/migrations/migrate_stages_statuses_spec.rb b/spec/migrations/migrate_stages_statuses_spec.rb
index ce35276cbf5..5483e24fce7 100644
--- a/spec/migrations/migrate_stages_statuses_spec.rb
+++ b/spec/migrations/migrate_stages_statuses_spec.rb
@@ -34,7 +34,7 @@ describe MigrateStagesStatuses, :sidekiq, :migration do
end
it 'correctly migrates stages statuses' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(stages.where(status: nil).count).to eq 3
migrate!
diff --git a/spec/migrations/normalize_ldap_extern_uids_spec.rb b/spec/migrations/normalize_ldap_extern_uids_spec.rb
index 56a78f52802..c6ea1e3e49e 100644
--- a/spec/migrations/normalize_ldap_extern_uids_spec.rb
+++ b/spec/migrations/normalize_ldap_extern_uids_spec.rb
@@ -38,7 +38,7 @@ describe NormalizeLdapExternUids, :migration, :sidekiq do
end
it 'migrates the LDAP identities' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
migrate!
identities.where(id: 1..4).each do |identity|
expect(identity.extern_uid).to eq("uid=foo #{identity.id},ou=people,dc=example,dc=com")
@@ -47,7 +47,7 @@ describe NormalizeLdapExternUids, :migration, :sidekiq do
end
it 'does not modify non-LDAP identities' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
migrate!
identity = identities.last
expect(identity.extern_uid).to eq(" uid = foo 5, ou = People, dc = example, dc = com ")
diff --git a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
index ed306fb3d62..96bef107599 100644
--- a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
+++ b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
@@ -20,7 +20,7 @@ describe ScheduleCreateGpgKeySubkeysFromGpgKeys, :migration, :sidekiq do
end
it 'schedules background migrations' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(GpgKeySubkey.count).to eq(0)
migrate!
diff --git a/spec/migrations/schedule_merge_request_diff_migrations_spec.rb b/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
index d230f064444..9f7e47bae0d 100644
--- a/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
+++ b/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
@@ -33,7 +33,7 @@ describe ScheduleMergeRequestDiffMigrations, :migration, :sidekiq do
end
it 'schedules background migrations' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
non_empty = 'st_commits IS NOT NULL OR st_diffs IS NOT NULL'
expect(merge_request_diffs.where(non_empty).count).to eq 3
diff --git a/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
index 1aab4ae1650..5bcb923af7b 100644
--- a/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
+++ b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
@@ -33,7 +33,7 @@ describe ScheduleMergeRequestDiffMigrationsTakeTwo, :migration, :sidekiq do
end
it 'migrates the data' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
non_empty = 'st_commits IS NOT NULL OR st_diffs IS NOT NULL'
expect(merge_request_diffs.where(non_empty).count).to eq 3
diff --git a/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb b/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb
index c9fdbe95d13..76fe16581ac 100644
--- a/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb
+++ b/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb
@@ -53,7 +53,7 @@ describe ScheduleMergeRequestLatestMergeRequestDiffIdMigrations, :migration, :si
end
it 'schedules background migrations' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(merge_requests_table.where(latest_merge_request_diff_id: nil).count).to eq 3
migrate!
diff --git a/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb b/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb
index 027f4a91c90..fa4ddd5fbc7 100644
--- a/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb
+++ b/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb
@@ -31,7 +31,7 @@ describe ScheduleSetConfidentialNoteEventsOnWebhooks, :migration, :sidekiq do
end
it 'correctly processes web hooks' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(web_hooks_table.where(confidential_note_events: nil).count).to eq 4
expect(web_hooks_table.where(confidential_note_events: true).count).to eq 1
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index 774a638b430..915bf134d57 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -179,7 +179,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
it 'migrates data to object storage' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
subject
build_trace_chunk.reload
@@ -201,7 +201,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
it 'does not migrate data to object storage' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
data_store = build_trace_chunk.data_store
subject
diff --git a/spec/models/spam_log_spec.rb b/spec/models/spam_log_spec.rb
index 0d6b4384ada..90a2caaeb88 100644
--- a/spec/models/spam_log_spec.rb
+++ b/spec/models/spam_log_spec.rb
@@ -22,7 +22,7 @@ describe SpamLog do
spam_log = build(:spam_log)
user = spam_log.user
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
spam_log.remove_user(deleted_by: admin)
end
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 65b387a2170..3a8948f8477 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -226,6 +226,25 @@ describe API::Groups do
expect(json_response.first['name']).to eq(group2.name)
end
end
+
+ context 'when using min_access_level in the request' do
+ let!(:group3) { create(:group, :private) }
+ let(:response_groups) { json_response.map { |group| group['id'] } }
+
+ before do
+ group1.add_developer(user2)
+ group3.add_master(user2)
+ end
+
+ it 'returns an array of groups the user has at least master access' do
+ get api('/groups', user2), min_access_level: 40
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(response_groups).to eq([group2.id, group3.id])
+ end
+ end
end
describe "GET /groups/:id" do
diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb
index 41243854ebc..55332f56508 100644
--- a/spec/requests/api/project_import_spec.rb
+++ b/spec/requests/api/project_import_spec.rb
@@ -102,7 +102,7 @@ describe API::ProjectImport do
it 'correctly overrides params during the import' do
override_params = { 'description' => 'Hello world' }
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
post api('/projects/import', user),
path: 'test-import',
file: fixture_file_upload(file),
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 5ac008c7e40..71e3436fa76 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -400,6 +400,22 @@ describe API::Projects do
end
end
end
+
+ context 'and with min_access_level' do
+ before do
+ project2.add_master(user2)
+ project3.add_developer(user2)
+ project4.add_reporter(user2)
+ end
+
+ it 'returns an array of groups the user has at least developer access' do
+ get api('/projects', user2), { min_access_level: 30 }
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |project| project['id'] }).to contain_exactly(project2.id, project3.id)
+ end
+ end
end
context 'when authenticated as a different user' do
@@ -681,6 +697,20 @@ describe API::Projects do
expect(json_response).to be_an Array
expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id)
end
+
+ it 'returns projects filetered by minimal access level' do
+ private_project1 = create(:project, :private, name: 'private_project1', creator_id: user4.id, namespace: user4.namespace)
+ private_project2 = create(:project, :private, name: 'private_project2', creator_id: user4.id, namespace: user4.namespace)
+ private_project1.add_developer(user2)
+ private_project2.add_reporter(user2)
+
+ get api("/users/#{user4.id}/projects/", user2), { min_access_level: 30 }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |project| project['id'] }).to contain_exactly(private_project1.id)
+ end
end
describe 'POST /projects/user/:id' do
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index a97c3f3461a..b3079c0a77b 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -1067,7 +1067,7 @@ describe API::Users do
end
it "deletes user" do
- Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) }
+ perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
expect(response).to have_gitlab_http_status(204)
expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound
@@ -1079,30 +1079,30 @@ describe API::Users do
end
it "does not delete for unauthenticated user" do
- Sidekiq::Testing.inline! { delete api("/users/#{user.id}") }
+ perform_enqueued_jobs { delete api("/users/#{user.id}") }
expect(response).to have_gitlab_http_status(401)
end
it "is not available for non admin users" do
- Sidekiq::Testing.inline! { delete api("/users/#{user.id}", user) }
+ perform_enqueued_jobs { delete api("/users/#{user.id}", user) }
expect(response).to have_gitlab_http_status(403)
end
it "returns 404 for non-existing user" do
- Sidekiq::Testing.inline! { delete api("/users/999999", admin) }
+ perform_enqueued_jobs { delete api("/users/999999", admin) }
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
end
it "returns a 404 for invalid ID" do
- Sidekiq::Testing.inline! { delete api("/users/ASDF", admin) }
+ perform_enqueued_jobs { delete api("/users/ASDF", admin) }
expect(response).to have_gitlab_http_status(404)
end
context "hard delete disabled" do
it "moves contributions to the ghost user" do
- Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) }
+ perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
expect(response).to have_gitlab_http_status(204)
expect(issue.reload).to be_persisted
@@ -1112,7 +1112,7 @@ describe API::Users do
context "hard delete enabled" do
it "removes contributions" do
- Sidekiq::Testing.inline! { delete api("/users/#{user.id}?hard_delete=true", admin) }
+ perform_enqueued_jobs { delete api("/users/#{user.id}?hard_delete=true", admin) }
expect(response).to have_gitlab_http_status(204)
expect(Issue.exists?(issue.id)).to be_falsy
diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb
index a9baccd061a..b54491cf5f9 100644
--- a/spec/services/groups/destroy_service_spec.rb
+++ b/spec/services/groups/destroy_service_spec.rb
@@ -49,7 +49,7 @@ describe Groups::DestroyService do
context 'Sidekiq inline' do
before do
# Run sidekiq immediately to check that renamed dir will be removed
- Sidekiq::Testing.inline! { destroy_group(group, user, async) }
+ perform_enqueued_jobs { destroy_group(group, user, async) }
end
it 'verifies that paths have been deleted' do
diff --git a/spec/services/projects/create_from_template_service_spec.rb b/spec/services/projects/create_from_template_service_spec.rb
index 9aa9237d875..a43da01f37e 100644
--- a/spec/services/projects/create_from_template_service_spec.rb
+++ b/spec/services/projects/create_from_template_service_spec.rb
@@ -28,7 +28,7 @@ describe Projects::CreateFromTemplateService do
context 'the result project' do
before do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
@project = subject.execute
end
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index 38660ad7a01..e428808ab68 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -45,18 +45,18 @@ describe Projects::DestroyService do
shared_examples 'handles errors thrown during async destroy' do |error_message|
it 'does not allow the error to bubble up' do
expect do
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
end.not_to raise_error
end
it 'unmarks the project as "pending deletion"' do
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
expect(project.reload.pending_delete).to be(false)
end
it 'stores an error message in `projects.delete_error`' do
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
expect(project.reload.delete_error).to be_present
expect(project.delete_error).to include(error_message)
@@ -66,7 +66,7 @@ describe Projects::DestroyService do
context 'Sidekiq inline' do
before do
# Run sidekiq immediatly to check that renamed repository will be removed
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
end
context 'when has remote mirrors' do
@@ -110,7 +110,7 @@ describe Projects::DestroyService do
end
it 'keeps project team intact upon an error' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
begin
destroy_project(project, user, {})
rescue ::Redis::CannotConnectError
@@ -128,7 +128,7 @@ describe Projects::DestroyService do
before do
project.project_feature.update_attribute("issues_access_level", ProjectFeature::PRIVATE)
# Run sidekiq immediately to check that renamed repository will be removed
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
end
it_behaves_like 'deleting the project'
@@ -172,7 +172,7 @@ describe Projects::DestroyService do
it 'allows error to bubble up and rolls back project deletion' do
expect do
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
end.to raise_error(Exception, 'Other error message')
expect(project.reload.pending_delete).to be(false)
diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb
index 1cf373d1d72..18ecef1c0a1 100644
--- a/spec/services/projects/housekeeping_service_spec.rb
+++ b/spec/services/projects/housekeeping_service_spec.rb
@@ -35,7 +35,7 @@ describe Projects::HousekeepingService do
allow(subject).to receive(:gc_period).and_return(1)
project.increment_pushes_since_gc
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect { subject.execute }.to change { project.pushes_since_gc }.to(0)
end
end
diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb
index f82d4b483e7..3bae8bfbd42 100644
--- a/spec/services/users/destroy_service_spec.rb
+++ b/spec/services/users/destroy_service_spec.rb
@@ -173,7 +173,7 @@ describe Users::DestroyService do
describe "user personal's repository removal" do
before do
- Sidekiq::Testing.inline! { service.execute(user) }
+ perform_enqueued_jobs { service.execute(user) }
end
context 'legacy storage' do
diff --git a/spec/workers/storage_migrator_worker_spec.rb b/spec/workers/storage_migrator_worker_spec.rb
index 815432aacce..808084c8f7c 100644
--- a/spec/workers/storage_migrator_worker_spec.rb
+++ b/spec/workers/storage_migrator_worker_spec.rb
@@ -13,7 +13,7 @@ describe StorageMigratorWorker do
end
it 'migrates projects in the specified range' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
worker.perform(ids.min, ids.max)
end