summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-12-20 09:08:36 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-12-20 09:08:36 +0000
commit19e00b948726c0f7ca27dd92200493803499a4e1 (patch)
tree0df898db4ba20af4b4de2baf39285fe4d113d148 /app
parentca5ebd2044ce696cc1aafc8a80a606e20f2c9e4b (diff)
downloadgitlab-ce-19e00b948726c0f7ca27dd92200493803499a4e1.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/invite_members/components/import_project_members_modal.vue23
-rw-r--r--app/assets/javascripts/invite_members/components/invite_groups_modal.vue22
-rw-r--r--app/assets/javascripts/invite_members/components/invite_members_modal.vue22
-rw-r--r--app/assets/javascripts/invite_members/constants.js1
-rw-r--r--app/assets/javascripts/invite_members/init_import_project_members_modal.js4
-rw-r--r--app/assets/javascripts/invite_members/init_invite_groups_modal.js1
-rw-r--r--app/assets/javascripts/invite_members/init_invite_members_modal.js1
-rw-r--r--app/assets/javascripts/invite_members/utils/trigger_successful_invite_alert.js23
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql6
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql6
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue4
-rw-r--r--app/assets/javascripts/pipeline_wizard/components/wrapper.vue12
-rw-r--r--app/controllers/groups/usage_quotas_controller.rb2
-rw-r--r--app/controllers/projects/pages_controller.rb6
-rw-r--r--app/models/concerns/counter_attribute.rb8
-rw-r--r--app/models/project.rb6
-rw-r--r--app/models/project_statistics.rb20
-rw-r--r--app/services/ci/job_artifacts/delete_service.rb3
-rw-r--r--app/services/ci/job_artifacts/destroy_associations_service.rb12
-rw-r--r--app/services/ci/job_artifacts/destroy_batch_service.rb21
-rw-r--r--app/views/groups/_invite_groups_modal.html.haml2
-rw-r--r--app/views/groups/_invite_members_modal.html.haml1
-rw-r--r--app/views/groups/group_members/index.html.haml4
-rw-r--r--app/views/projects/_invite_groups_modal.html.haml2
-rw-r--r--app/views/projects/_invite_members_modal.html.haml1
-rw-r--r--app/views/projects/pages/new.html.haml18
-rw-r--r--app/views/projects/project_members/index.html.haml6
-rw-r--r--app/workers/all_queues.yml2
-rw-r--r--app/workers/update_highest_role_worker.rb2
29 files changed, 193 insertions, 48 deletions
diff --git a/app/assets/javascripts/invite_members/components/import_project_members_modal.vue b/app/assets/javascripts/invite_members/components/import_project_members_modal.vue
index 31b7fd4cc42..b4e9a3a1559 100644
--- a/app/assets/javascripts/invite_members/components/import_project_members_modal.vue
+++ b/app/assets/javascripts/invite_members/components/import_project_members_modal.vue
@@ -5,6 +5,10 @@ import { importProjectMembers } from '~/api/projects_api';
import { BV_SHOW_MODAL } from '~/lib/utils/constants';
import { s__, __, sprintf } from '~/locale';
import eventHub from '../event_hub';
+import {
+ displaySuccessfulInvitationAlert,
+ reloadOnInvitationSuccess,
+} from '../utils/trigger_successful_invite_alert';
import ProjectSelect from './project_select.vue';
export default {
@@ -24,6 +28,11 @@ export default {
type: String,
required: true,
},
+ reloadPageOnSubmit: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -59,6 +68,10 @@ export default {
},
},
mounted() {
+ if (this.reloadPageOnSubmit) {
+ displaySuccessfulInvitationAlert();
+ }
+
eventHub.$on('openProjectMembersModal', () => {
this.openModal();
});
@@ -74,16 +87,22 @@ export default {
submitImport() {
this.isLoading = true;
return importProjectMembers(this.projectId, this.projectToBeImported.id)
- .then(this.showToastMessage)
+ .then(this.onInviteSuccess)
.catch(this.showErrorAlert)
.finally(() => {
this.isLoading = false;
this.projectToBeImported = {};
});
},
+ onInviteSuccess() {
+ if (this.reloadPageOnSubmit) {
+ reloadOnInvitationSuccess();
+ } else {
+ this.showToastMessage();
+ }
+ },
showToastMessage() {
this.$toast.show(this.$options.i18n.successMessage, this.$options.toastOptions);
-
this.closeModal();
},
showErrorAlert() {
diff --git a/app/assets/javascripts/invite_members/components/invite_groups_modal.vue b/app/assets/javascripts/invite_members/components/invite_groups_modal.vue
index ceb8e569d3f..3be3b9df747 100644
--- a/app/assets/javascripts/invite_members/components/invite_groups_modal.vue
+++ b/app/assets/javascripts/invite_members/components/invite_groups_modal.vue
@@ -6,6 +6,10 @@ import InviteModalBase from 'ee_else_ce/invite_members/components/invite_modal_b
import { GROUP_FILTERS, GROUP_MODAL_LABELS } from '../constants';
import eventHub from '../event_hub';
import { getInvalidFeedbackMessage } from '../utils/get_invalid_feedback_message';
+import {
+ displaySuccessfulInvitationAlert,
+ reloadOnInvitationSuccess,
+} from '../utils/trigger_successful_invite_alert';
import GroupSelect from './group_select.vue';
import InviteGroupNotification from './invite_group_notification.vue';
@@ -67,6 +71,11 @@ export default {
type: Boolean,
required: true,
},
+ reloadPageOnSubmit: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -95,6 +104,10 @@ export default {
},
},
mounted() {
+ if (this.reloadPageOnSubmit) {
+ displaySuccessfulInvitationAlert();
+ }
+
eventHub.$on('openGroupModal', () => {
this.openModal();
});
@@ -124,7 +137,7 @@ export default {
expires_at: expiresAt,
})
.then(() => {
- this.showSuccessMessage();
+ this.onInviteSuccess();
})
.catch((e) => {
this.showInvalidFeedbackMessage(e);
@@ -138,6 +151,13 @@ export default {
this.isLoading = false;
this.groupToBeSharedWith = {};
},
+ onInviteSuccess() {
+ if (this.reloadPageOnSubmit) {
+ reloadOnInvitationSuccess();
+ } else {
+ this.showSuccessMessage();
+ }
+ },
showSuccessMessage() {
this.$toast.show(this.$options.labels.toastMessageSuccessful, this.toastOptions);
this.closeModal();
diff --git a/app/assets/javascripts/invite_members/components/invite_members_modal.vue b/app/assets/javascripts/invite_members/components/invite_members_modal.vue
index 4c5e4fb1021..fbb547c28ff 100644
--- a/app/assets/javascripts/invite_members/components/invite_members_modal.vue
+++ b/app/assets/javascripts/invite_members/components/invite_members_modal.vue
@@ -29,6 +29,10 @@ import eventHub from '../event_hub';
import { responseFromSuccess } from '../utils/response_message_parser';
import { memberName } from '../utils/member_utils';
import { getInvalidFeedbackMessage } from '../utils/get_invalid_feedback_message';
+import {
+ displaySuccessfulInvitationAlert,
+ reloadOnInvitationSuccess,
+} from '../utils/trigger_successful_invite_alert';
import ModalConfetti from './confetti.vue';
import MembersTokenSelect from './members_token_select.vue';
import UserLimitNotification from './user_limit_notification.vue';
@@ -107,6 +111,11 @@ export default {
required: false,
default: () => ({}),
},
+ reloadPageOnSubmit: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -225,6 +234,10 @@ export default {
},
},
mounted() {
+ if (this.reloadPageOnSubmit) {
+ displaySuccessfulInvitationAlert();
+ }
+
eventHub.$on('openModal', (options) => {
this.openModal(options);
if (this.isOnLearnGitlab) {
@@ -306,7 +319,7 @@ export default {
if (error) {
this.showMemberErrors(message);
} else {
- this.showSuccessMessage();
+ this.onInviteSuccess();
}
})
.catch((e) => this.showInvalidFeedbackMessage(e))
@@ -339,6 +352,13 @@ export default {
changeSelectedTaskProject(project) {
this.selectedTaskProject = project;
},
+ onInviteSuccess() {
+ if (this.reloadPageOnSubmit) {
+ reloadOnInvitationSuccess();
+ } else {
+ this.showSuccessMessage();
+ }
+ },
showSuccessMessage() {
if (this.isOnLearnGitlab) {
eventHub.$emit('showSuccessfulInvitationsAlert');
diff --git a/app/assets/javascripts/invite_members/constants.js b/app/assets/javascripts/invite_members/constants.js
index 3b522f9a7d9..a894eb24d38 100644
--- a/app/assets/javascripts/invite_members/constants.js
+++ b/app/assets/javascripts/invite_members/constants.js
@@ -9,6 +9,7 @@ export const INVITE_MEMBERS_FOR_TASK = {
view: 'modal_opened_from_email',
submit: 'submit',
};
+export const TOAST_MESSAGE_LOCALSTORAGE_KEY = 'members_invited_successfully';
export const GROUP_FILTERS = {
ALL: 'all',
diff --git a/app/assets/javascripts/invite_members/init_import_project_members_modal.js b/app/assets/javascripts/invite_members/init_import_project_members_modal.js
index daaa1315884..227d8395250 100644
--- a/app/assets/javascripts/invite_members/init_import_project_members_modal.js
+++ b/app/assets/javascripts/invite_members/init_import_project_members_modal.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import ImportProjectMembersModal from '~/invite_members/components/import_project_members_modal.vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
export default function initImportProjectMembersModal() {
const el = document.querySelector('.js-import-project-members-modal');
@@ -8,7 +9,7 @@ export default function initImportProjectMembersModal() {
return false;
}
- const { projectId, projectName } = el.dataset;
+ const { projectId, projectName, reloadPageOnSubmit } = el.dataset;
return new Vue({
el,
@@ -17,6 +18,7 @@ export default function initImportProjectMembersModal() {
props: {
projectId,
projectName,
+ reloadPageOnSubmit: parseBoolean(reloadPageOnSubmit),
},
}),
});
diff --git a/app/assets/javascripts/invite_members/init_invite_groups_modal.js b/app/assets/javascripts/invite_members/init_invite_groups_modal.js
index 320e53d3e3c..53b756b610f 100644
--- a/app/assets/javascripts/invite_members/init_invite_groups_modal.js
+++ b/app/assets/javascripts/invite_members/init_invite_groups_modal.js
@@ -42,6 +42,7 @@ export default function initInviteGroupsModal() {
groupSelectParentId: parseInt(el.dataset.parentId, 10),
invalidGroups: JSON.parse(el.dataset.invalidGroups || '[]'),
freeUserCapEnabled: parseBoolean(el.dataset.freeUserCapEnabled),
+ reloadPageOnSubmit: parseBoolean(el.dataset.reloadPageOnSubmit),
},
}),
});
diff --git a/app/assets/javascripts/invite_members/init_invite_members_modal.js b/app/assets/javascripts/invite_members/init_invite_members_modal.js
index a4be3f205a3..842ab07f368 100644
--- a/app/assets/javascripts/invite_members/init_invite_members_modal.js
+++ b/app/assets/javascripts/invite_members/init_invite_members_modal.js
@@ -41,6 +41,7 @@ export default (function initInviteMembersModal() {
usersLimitDataset: convertObjectPropsToCamelCase(
JSON.parse(el.dataset.usersLimitDataset || '{}'),
),
+ reloadPageOnSubmit: parseBoolean(el.dataset.reloadPageOnSubmit),
},
}),
});
diff --git a/app/assets/javascripts/invite_members/utils/trigger_successful_invite_alert.js b/app/assets/javascripts/invite_members/utils/trigger_successful_invite_alert.js
new file mode 100644
index 00000000000..4d3a7951265
--- /dev/null
+++ b/app/assets/javascripts/invite_members/utils/trigger_successful_invite_alert.js
@@ -0,0 +1,23 @@
+import { createAlert } from '~/flash';
+import AccessorUtilities from '~/lib/utils/accessor';
+
+import { TOAST_MESSAGE_LOCALSTORAGE_KEY, TOAST_MESSAGE_SUCCESSFUL } from '../constants';
+
+export function displaySuccessfulInvitationAlert() {
+ if (!AccessorUtilities.canUseLocalStorage()) {
+ return;
+ }
+
+ const showAlert = Boolean(localStorage.getItem(TOAST_MESSAGE_LOCALSTORAGE_KEY));
+ if (showAlert) {
+ localStorage.removeItem(TOAST_MESSAGE_LOCALSTORAGE_KEY);
+ createAlert({ message: TOAST_MESSAGE_SUCCESSFUL, variant: 'info' });
+ }
+}
+
+export function reloadOnInvitationSuccess() {
+ if (AccessorUtilities.canUseLocalStorage()) {
+ localStorage.setItem(TOAST_MESSAGE_LOCALSTORAGE_KEY, 'true');
+ }
+ window.location.reload();
+}
diff --git a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql
index f1fc27dcb2a..4a8786b04b1 100644
--- a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql
+++ b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql
@@ -7,6 +7,12 @@ mutation CreateTimelineEvent($input: TimelineEventCreateInput!) {
action
occurredAt
createdAt
+ timelineEventTags {
+ nodes {
+ id
+ name
+ }
+ }
}
errors
}
diff --git a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql
index bc4e8414bfc..baeb81745ab 100644
--- a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql
+++ b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql
@@ -9,6 +9,12 @@ query GetTimelineEvents($fullPath: ID!, $incidentId: IssueID!) {
action
occurredAt
createdAt
+ timelineEventTags {
+ nodes {
+ id
+ name
+ }
+ }
}
}
}
diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue
index fad4f07cfc3..c6b93201c97 100644
--- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue
+++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue
@@ -50,6 +50,9 @@ export default {
},
},
methods: {
+ getFirstTag(eventTag) {
+ return eventTag.nodes?.[0]?.name;
+ },
handleEditSelection(event) {
this.eventToEdit = event.id;
this.$emit('hide-new-incident-timeline-event-form');
@@ -161,6 +164,7 @@ export default {
:action="event.action"
:occurred-at="event.occurredAt"
:note-html="event.noteHtml"
+ :event-tag="getFirstTag(event.timelineEventTags)"
@delete="handleDelete(event)"
@edit="handleEditSelection(event)"
/>
diff --git a/app/assets/javascripts/pipeline_wizard/components/wrapper.vue b/app/assets/javascripts/pipeline_wizard/components/wrapper.vue
index adeb4ae598b..ab837d04d9a 100644
--- a/app/assets/javascripts/pipeline_wizard/components/wrapper.vue
+++ b/app/assets/javascripts/pipeline_wizard/components/wrapper.vue
@@ -6,6 +6,7 @@ import { merge } from '~/lib/utils/yaml';
import { __ } from '~/locale';
import { isValidStepSeq } from '~/pipeline_wizard/validators';
import Tracking from '~/tracking';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import YamlEditor from './editor.vue';
import WizardStep from './step.vue';
import CommitStep from './commit.vue';
@@ -28,7 +29,7 @@ export default {
WizardStep,
CommitStep,
},
- mixins: [trackingMixin],
+ mixins: [trackingMixin, glFeatureFlagsMixin()],
props: {
steps: {
type: Object,
@@ -91,6 +92,11 @@ export default {
category: `pipeline_wizard:${this.templateId}`,
};
},
+ trackingExtraData() {
+ return {
+ features: this.glFeatures,
+ };
+ },
},
watch: {
isLastStep(value) {
@@ -125,6 +131,7 @@ export default {
extra: {
fromStep: this.currentStepIndex + 1,
toStep: this.currentStepIndex,
+ ...this.trackingExtraData,
},
});
},
@@ -136,6 +143,7 @@ export default {
extra: {
fromStep: this.currentStepIndex - 1,
toStep: this.currentStepIndex,
+ ...this.trackingExtraData,
},
});
},
@@ -144,6 +152,7 @@ export default {
this.track('click_button', {
label: 'pipeline_wizard_commit',
property: 'commit',
+ extra: this.trackingExtraData,
});
},
onEditorTouched() {
@@ -151,6 +160,7 @@ export default {
label: 'pipeline_wizard_editor_interaction',
extra: {
currentStep: this.currentStepIndex,
+ ...this.trackingExtraData,
},
});
},
diff --git a/app/controllers/groups/usage_quotas_controller.rb b/app/controllers/groups/usage_quotas_controller.rb
index 9954805f862..29878f0001d 100644
--- a/app/controllers/groups/usage_quotas_controller.rb
+++ b/app/controllers/groups/usage_quotas_controller.rb
@@ -5,7 +5,7 @@ module Groups
before_action :authorize_read_usage_quotas!
before_action :verify_usage_quotas_enabled!
- feature_category :subscription_usage_reports
+ feature_category :subscription_cost_management
urgency :low
def index
diff --git a/app/controllers/projects/pages_controller.rb b/app/controllers/projects/pages_controller.rb
index 0e990b64cd6..2fd1e4b0ee0 100644
--- a/app/controllers/projects/pages_controller.rb
+++ b/app/controllers/projects/pages_controller.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class Projects::PagesController < Projects::ApplicationController
- layout 'project_settings'
+ layout 'project_settings' if Feature.disabled?(:show_pages_in_deployments_menu, @project, type: :experiment)
before_action :require_pages_enabled!
before_action :authorize_read_pages!, only: [:show]
@@ -10,6 +10,10 @@ class Projects::PagesController < Projects::ApplicationController
feature_category :pages
+ before_action do
+ push_frontend_feature_flag(:show_pages_in_deployments_menu, project, type: :experiment)
+ end
+
def new
@pipeline_wizard_data = {
project_path: @project.full_path,
diff --git a/app/models/concerns/counter_attribute.rb b/app/models/concerns/counter_attribute.rb
index f1efbba67e1..1f838ee911f 100644
--- a/app/models/concerns/counter_attribute.rb
+++ b/app/models/concerns/counter_attribute.rb
@@ -97,6 +97,14 @@ module CounterAttribute
end
end
+ def bulk_increment_counter(attribute, increments)
+ run_after_commit_or_now do
+ new_value = counter(attribute).bulk_increment(increments)
+
+ log_increment_counter(attribute, increments.sum, new_value)
+ end
+ end
+
def update_counters_with_lease(increments)
detect_race_on_record(log_fields: { caller: __method__, attributes: increments.keys }) do
self.class.update_counters(id, increments)
diff --git a/app/models/project.rb b/app/models/project.rb
index 73dbb55a07b..3a664129993 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -2920,12 +2920,6 @@ class Project < ApplicationRecord
Gitlab::Routing.url_helpers.activity_project_path(self)
end
- def increment_statistic_value(statistic, delta)
- return if pending_delete?
-
- ProjectStatistics.increment_statistic(self, statistic, delta)
- end
-
def ci_forward_deployment_enabled?
return false unless ci_cd_settings
diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb
index 506f6305791..bbb1726c255 100644
--- a/app/models/project_statistics.rb
+++ b/app/models/project_statistics.rb
@@ -124,17 +124,37 @@ class ProjectStatistics < ApplicationRecord
#
# For non-counter attributes, storage_size is updated depending on key => [columns] in INCREMENTABLE_COLUMNS
def self.increment_statistic(project, key, amount)
+ return if project.pending_delete?
+
project.statistics.try do |project_statistics|
project_statistics.increment_statistic(key, amount)
end
end
+ def self.bulk_increment_statistic(project, key, amounts)
+ unless Feature.enabled?(:project_statistics_bulk_increment, type: :development)
+ return increment_statistic(project, key, amounts.sum)
+ end
+
+ return if project.pending_delete?
+
+ project.statistics.try do |project_statistics|
+ project_statistics.bulk_increment_statistic(key, amounts)
+ end
+ end
+
def increment_statistic(key, amount)
raise ArgumentError, "Cannot increment attribute: #{key}" unless incrementable_attribute?(key)
increment_counter(key, amount)
end
+ def bulk_increment_statistic(key, increments)
+ raise ArgumentError, "Cannot increment attribute: #{key}" unless incrementable_attribute?(key)
+
+ bulk_increment_counter(key, increments)
+ end
+
private
def incrementable_attribute?(key)
diff --git a/app/services/ci/job_artifacts/delete_service.rb b/app/services/ci/job_artifacts/delete_service.rb
index c9d590eccc4..fc5c6b12389 100644
--- a/app/services/ci/job_artifacts/delete_service.rb
+++ b/app/services/ci/job_artifacts/delete_service.rb
@@ -26,8 +26,7 @@ module Ci
if result.fetch(:status) == :success
ServiceResponse.success(payload:
{
- destroyed_artifacts_count: result.fetch(:destroyed_artifacts_count),
- statistics_updates: result.fetch(:statistics_updates)
+ destroyed_artifacts_count: result.fetch(:destroyed_artifacts_count)
})
else
ServiceResponse.error(message: result.fetch(:message))
diff --git a/app/services/ci/job_artifacts/destroy_associations_service.rb b/app/services/ci/job_artifacts/destroy_associations_service.rb
index 794d24eadf2..4604d35ec23 100644
--- a/app/services/ci/job_artifacts/destroy_associations_service.rb
+++ b/app/services/ci/job_artifacts/destroy_associations_service.rb
@@ -7,22 +7,22 @@ module Ci
def initialize(job_artifacts_relation)
@job_artifacts_relation = job_artifacts_relation
- @statistics = {}
+ @statistics_updates = {}
end
def destroy_records
@job_artifacts_relation.each_batch(of: BATCH_SIZE) do |relation|
service = Ci::JobArtifacts::DestroyBatchService.new(relation, pick_up_at: Time.current)
result = service.execute(update_stats: false)
- updates = result[:statistics_updates]
-
- @statistics.merge!(updates) { |_key, oldval, newval| newval + oldval }
+ @statistics_updates.merge!(result[:statistics_updates]) do |_project, existing_updates, new_updates|
+ existing_updates.concat(new_updates)
+ end
end
end
def update_statistics
- @statistics.each do |project, delta|
- project.increment_statistic_value(Ci::JobArtifact.project_statistics_name, delta)
+ @statistics_updates.each do |project, changes|
+ ProjectStatistics.bulk_increment_statistic(project, Ci::JobArtifact.project_statistics_name, changes)
end
end
end
diff --git a/app/services/ci/job_artifacts/destroy_batch_service.rb b/app/services/ci/job_artifacts/destroy_batch_service.rb
index e0307d9bd53..6cfe24069e5 100644
--- a/app/services/ci/job_artifacts/destroy_batch_service.rb
+++ b/app/services/ci/job_artifacts/destroy_batch_service.rb
@@ -46,14 +46,13 @@ module Ci
after_batch_destroy_hook(@job_artifacts)
- # This is executed outside of the transaction because it depends on Redis
update_project_statistics! if update_stats
+
increment_monitoring_statistics(artifacts_count, artifacts_bytes)
Gitlab::Ci::Artifacts::Logger.log_deleted(@job_artifacts, 'Ci::JobArtifacts::DestroyBatchService#execute')
- success(destroyed_artifacts_count: artifacts_count,
- statistics_updates: affected_project_statistics)
+ success(destroyed_artifacts_count: artifacts_count, statistics_updates: statistics_updates_per_project)
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -74,17 +73,17 @@ module Ci
# using ! here since this can't be called inside a transaction
def update_project_statistics!
- affected_project_statistics.each do |project, delta|
- project.increment_statistic_value(Ci::JobArtifact.project_statistics_name, delta)
+ statistics_updates_per_project.each do |project, updates|
+ ProjectStatistics.bulk_increment_statistic(project, Ci::JobArtifact.project_statistics_name, updates)
end
end
- def affected_project_statistics
- strong_memoize(:affected_project_statistics) do
- artifacts_by_project = @job_artifacts.group_by(&:project)
- artifacts_by_project.each.with_object({}) do |(project, artifacts), accumulator|
- delta = -artifacts.sum { |artifact| artifact.size.to_i }
- accumulator[project] = delta
+ def statistics_updates_per_project
+ strong_memoize(:statistics_updates_per_project) do
+ result = Hash.new { |updates, project| updates[project] = [] }
+
+ @job_artifacts.each_with_object(result) do |job_artifact, result|
+ result[job_artifact.project] << -job_artifact.size.to_i
end
end
end
diff --git a/app/views/groups/_invite_groups_modal.html.haml b/app/views/groups/_invite_groups_modal.html.haml
index 2e11f6cee4f..b8e40460a92 100644
--- a/app/views/groups/_invite_groups_modal.html.haml
+++ b/app/views/groups/_invite_groups_modal.html.haml
@@ -1,3 +1,3 @@
- return unless can_admin_group_member?(group)
-.js-invite-groups-modal{ data: common_invite_group_modal_data(group, GroupMember, 'false') }
+.js-invite-groups-modal{ data: { reload_page_on_submit: local_assigns.fetch(:reload_page_on_submit, false).to_s }.merge(common_invite_group_modal_data(group, GroupMember, 'false')) }
diff --git a/app/views/groups/_invite_members_modal.html.haml b/app/views/groups/_invite_members_modal.html.haml
index 786034fd2e7..bf0e8b627fd 100644
--- a/app/views/groups/_invite_members_modal.html.haml
+++ b/app/views/groups/_invite_members_modal.html.haml
@@ -2,4 +2,5 @@
.js-invite-members-modal{ data: { is_project: 'false',
access_levels: GroupMember.access_level_roles.to_json,
+ reload_page_on_submit: local_assigns.fetch(:reload_page_on_submit, false).to_s,
help_link: help_page_url('user/permissions') }.merge(common_invite_modal_dataset(group)).merge(users_filter_data(group)) }
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index 4da70c8bf5d..298ed2c0806 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -15,8 +15,8 @@
classes: 'gl-md-w-auto gl-w-full gl-md-ml-3 gl-md-mt-0 gl-mt-3',
trigger_source: 'group-members-page',
display_text: _('Invite members') } }
- = render 'groups/invite_groups_modal', group: @group
- = render 'groups/invite_members_modal', group: @group
+ = render 'groups/invite_groups_modal', group: @group, reload_page_on_submit: true
+ = render 'groups/invite_members_modal', group: @group, reload_page_on_submit: true
= render_if_exists 'groups/group_members/ldap_sync'
diff --git a/app/views/projects/_invite_groups_modal.html.haml b/app/views/projects/_invite_groups_modal.html.haml
index 40dc0009b24..101acd9149e 100644
--- a/app/views/projects/_invite_groups_modal.html.haml
+++ b/app/views/projects/_invite_groups_modal.html.haml
@@ -1,3 +1,3 @@
- return unless can_invite_members_for_project?(project)
-.js-invite-groups-modal{ data: common_invite_group_modal_data(project, ProjectMember, 'true') }
+.js-invite-groups-modal{ data: { reload_page_on_submit: local_assigns.fetch(:reload_page_on_submit, false).to_s }.merge(common_invite_group_modal_data(project, ProjectMember, 'true')) }
diff --git a/app/views/projects/_invite_members_modal.html.haml b/app/views/projects/_invite_members_modal.html.haml
index 16288f4357a..53f74a0f270 100644
--- a/app/views/projects/_invite_members_modal.html.haml
+++ b/app/views/projects/_invite_members_modal.html.haml
@@ -2,4 +2,5 @@
.js-invite-members-modal{ data: { is_project: 'true',
access_levels: ProjectMember.permissible_access_level_roles(current_user, project).to_json,
+ reload_page_on_submit: local_assigns.fetch(:reload_page_on_submit, false).to_s,
help_link: help_page_url('user/permissions') }.merge(common_invite_modal_dataset(project)).merge(users_filter_data(project.group)) }
diff --git a/app/views/projects/pages/new.html.haml b/app/views/projects/pages/new.html.haml
index f1f3510d0f8..760ecd4fb8a 100644
--- a/app/views/projects/pages/new.html.haml
+++ b/app/views/projects/pages/new.html.haml
@@ -1,8 +1,14 @@
-%section.js-search-settings-section
- - if Feature.enabled?(:use_pipeline_wizard_for_pages, @project.group)
- #js-pages{ data: @pipeline_wizard_data }
+- if Feature.enabled?(:show_pages_in_deployments_menu, @project, type: :experiment)
+ - @breadcrumb_link = project_pages_path(@project)
+ - breadcrumb_title s_('GitLabPages|Pages')
+ - page_title s_('GitLabPages|Pages')
+- else
+ %section.js-search-settings-section
- - else
- = render 'header'
+- if Feature.enabled?(:use_pipeline_wizard_for_pages, @project.group)
+ #js-pages{ data: @pipeline_wizard_data }
- = render 'use'
+- else
+ = render 'header'
+
+ = render 'use'
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index c7818602f52..4ac0e28d386 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -15,17 +15,17 @@
- invite_group_top_margin = ''
- if can_admin_project_member?(@project)
.js-import-project-members-trigger{ data: { classes: 'gl-md-w-auto gl-w-full' } }
- .js-import-project-members-modal{ data: { project_id: @project.id, project_name: @project.name } }
+ .js-import-project-members-modal{ data: { project_id: @project.id, project_name: @project.name, reload_page_on_submit: true.to_s } }
- invite_group_top_margin = 'gl-md-mt-0 gl-mt-3'
- if @project.allowed_to_share_with_group?
.js-invite-group-trigger{ data: { classes: "gl-md-w-auto gl-w-full gl-md-ml-3 #{invite_group_top_margin}", display_text: _('Invite a group') } }
- = render 'projects/invite_groups_modal', project: @project
+ = render 'projects/invite_groups_modal', project: @project, reload_page_on_submit: true
- if can_admin_project_member?(@project)
.js-invite-members-trigger{ data: { variant: 'confirm',
classes: 'gl-md-w-auto gl-w-full gl-md-ml-3 gl-md-mt-0 gl-mt-3',
trigger_source: 'project-members-page',
display_text: _('Invite members') } }
- = render 'projects/invite_members_modal', project: @project
+ = render 'projects/invite_members_modal', project: @project, reload_page_on_submit: true
- else
- if project_can_be_shared?
%h4
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 9540c33caba..652a0021b0f 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -3353,7 +3353,7 @@
:tags: []
- :name: update_highest_role
:worker_name: UpdateHighestRoleWorker
- :feature_category: :subscription_usage_reports
+ :feature_category: :subscription_cost_management
:has_external_dependencies: false
:urgency: :high
:resource_boundary: :unknown
diff --git a/app/workers/update_highest_role_worker.rb b/app/workers/update_highest_role_worker.rb
index a05c9c7a1e7..dccf88e1b1a 100644
--- a/app/workers/update_highest_role_worker.rb
+++ b/app/workers/update_highest_role_worker.rb
@@ -7,7 +7,7 @@ class UpdateHighestRoleWorker
sidekiq_options retry: 3
- feature_category :subscription_usage_reports
+ feature_category :subscription_cost_management
urgency :high
weight 2