Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-08-12 00:09:14 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-08-12 00:09:14 +0300
commit0f079aa28d93f40ad7fda398fb2280c3e358098d (patch)
treebc67abda12fd05a9712cd0bd97f82e33eb6c927f /app
parent603ee53dbdbd3adaced752e1a119eb40d64e9979 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue88
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue75
-rw-r--r--app/assets/javascripts/editor/schema/ci.json4
-rw-r--r--app/assets/javascripts/sidebar/components/severity/severity.vue8
-rw-r--r--app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue8
-rw-r--r--app/controllers/admin/topics_controller.rb20
-rw-r--r--app/models/concerns/cross_database_modification.rb36
-rw-r--r--app/services/topics/merge_service.rb64
-rw-r--r--app/views/layouts/header/_default.html.haml2
9 files changed, 254 insertions, 51 deletions
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue
index 4cc00eb01d9..29578c6f710 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_settings.vue
@@ -1,9 +1,93 @@
<script>
-export default {};
+import { ADD_VARIABLE_ACTION, EDIT_VARIABLE_ACTION, VARIABLE_ACTIONS } from '../constants';
+import { createJoinedEnvironments } from '../utils';
+import CiVariableTable from './ci_variable_table.vue';
+import CiVariableModal from './ci_variable_modal.vue';
+
+export default {
+ components: {
+ CiVariableTable,
+ CiVariableModal,
+ },
+ props: {
+ areScopedVariablesAvailable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ environments: {
+ type: Array,
+ required: true,
+ },
+ isLoading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ variables: {
+ type: Array,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ selectedVariable: {},
+ mode: null,
+ };
+ },
+ computed: {
+ joinedEnvironments() {
+ return createJoinedEnvironments(this.variables, this.environments);
+ },
+ showModal() {
+ return VARIABLE_ACTIONS.includes(this.mode);
+ },
+ },
+ methods: {
+ addVariable(variable) {
+ this.$emit('add-variable', variable);
+ },
+ deleteVariable(variable) {
+ this.$emit('delete-variable', variable);
+ },
+ updateVariable(variable) {
+ this.$emit('update-variable', variable);
+ },
+ hideModal() {
+ this.mode = null;
+ },
+ setSelectedVariable(variable = null) {
+ if (!variable) {
+ this.selectedVariable = {};
+ this.mode = ADD_VARIABLE_ACTION;
+ } else {
+ this.selectedVariable = variable;
+ this.mode = EDIT_VARIABLE_ACTION;
+ }
+ },
+ },
+};
</script>
<template>
<div class="row">
- <div class="col-lg-12"></div>
+ <div class="col-lg-12">
+ <ci-variable-table
+ :is-loading="isLoading"
+ :variables="variables"
+ @set-selected-variable="setSelectedVariable"
+ />
+ <ci-variable-modal
+ v-if="showModal"
+ :are-scoped-variables-available="areScopedVariablesAvailable"
+ :environments="joinedEnvironments"
+ :mode="mode"
+ :selected-variable="selectedVariable"
+ @add-variable="addVariable"
+ @delete-variable="deleteVariable"
+ @hideModal="hideModal"
+ @update-variable="updateVariable"
+ />
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue
index f078234829a..1bb94080694 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue
@@ -1,10 +1,17 @@
<script>
-import { GlTable, GlButton, GlModalDirective, GlIcon, GlTooltipDirective } from '@gitlab/ui';
-import { mapState, mapActions } from 'vuex';
+import {
+ GlButton,
+ GlIcon,
+ GlLoadingIcon,
+ GlModalDirective,
+ GlTable,
+ GlTooltipDirective,
+} from '@gitlab/ui';
import { s__, __ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
-import { ADD_CI_VARIABLE_MODAL_ID } from '../constants';
+import { ADD_CI_VARIABLE_MODAL_ID, variableText } from '../constants';
+import { convertEnvironmentScope } from '../utils';
import CiVariablePopover from './ci_variable_popover.vue';
export default {
@@ -14,7 +21,7 @@ export default {
iconSize: 16,
fields: [
{
- key: 'variable_type',
+ key: 'variableType',
label: s__('CiVariables|Type'),
customStyle: { width: '70px' },
},
@@ -41,7 +48,7 @@ export default {
customStyle: { width: '100px' },
},
{
- key: 'environment_scope',
+ key: 'environmentScope',
label: s__('CiVariables|Environments'),
customStyle: { width: '20%' },
},
@@ -56,6 +63,7 @@ export default {
CiVariablePopover,
GlButton,
GlIcon,
+ GlLoadingIcon,
GlTable,
TooltipOnTruncate,
},
@@ -64,10 +72,25 @@ export default {
GlTooltip: GlTooltipDirective,
},
mixins: [glFeatureFlagsMixin()],
+ props: {
+ isLoading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ variables: {
+ type: Array,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ areValuesHidden: true,
+ };
+ },
computed: {
- ...mapState(['variables', 'valuesHidden', 'isLoading', 'isDeleting']),
valuesButtonText() {
- return this.valuesHidden ? __('Reveal values') : __('Hide values');
+ return this.areValuesHidden ? __('Reveal values') : __('Hide values');
},
isTableEmpty() {
return !this.variables || this.variables.length === 0;
@@ -76,18 +99,28 @@ export default {
return this.$options.fields;
},
},
- mounted() {
- this.fetchVariables();
- },
methods: {
- ...mapActions(['fetchVariables', 'toggleValues', 'editVariable']),
+ convertEnvironmentScopeValue(env) {
+ return convertEnvironmentScope(env);
+ },
+ generateTypeText(item) {
+ return variableText[item.variableType];
+ },
+ toggleHiddenState() {
+ this.areValuesHidden = !this.areValuesHidden;
+ },
+ setSelectedVariable(variable = null) {
+ this.$emit('set-selected-variable', variable);
+ },
},
};
</script>
<template>
<div class="ci-variable-table" data-testid="ci-variable-table">
+ <gl-loading-icon v-if="isLoading" />
<gl-table
+ v-else
:fields="fields"
:items="variables"
tbody-tr-class="js-ci-variable-row"
@@ -104,6 +137,11 @@ export default {
<template #table-colgroup="scope">
<col v-for="field in scope.fields" :key="field.key" :style="field.customStyle" />
</template>
+ <template #cell(variableType)="{ item }">
+ <div class="gl-pt-2">
+ {{ generateTypeText(item) }}
+ </div>
+ </template>
<template #cell(key)="{ item }">
<div class="gl-display-flex gl-align-items-center">
<tooltip-on-truncate :title="item.key" truncate-target="child">
@@ -125,11 +163,12 @@ export default {
</template>
<template #cell(value)="{ item }">
<div class="gl-display-flex gl-align-items-center">
- <span v-if="valuesHidden">*********************</span>
+ <span v-if="areValuesHidden" data-testid="hiddenValue">*********************</span>
<span
v-else
:id="`ci-variable-value-${item.id}`"
class="gl-display-inline-block gl-max-w-full gl-text-truncate"
+ data-testid="revealedValue"
>{{ item.value }}</span
>
<gl-button
@@ -150,16 +189,16 @@ export default {
<gl-icon v-if="item.masked" :size="$options.iconSize" :name="$options.trueIcon" />
<gl-icon v-else :size="$options.iconSize" :name="$options.falseIcon" />
</template>
- <template #cell(environment_scope)="{ item }">
+ <template #cell(environmentScope)="{ item }">
<div class="gl-display-flex">
<span
:id="`ci-variable-env-${item.id}`"
class="gl-display-inline-block gl-max-w-full gl-text-truncate"
- >{{ item.environment_scope }}</span
+ >{{ convertEnvironmentScopeValue(item.environmentScope) }}</span
>
<ci-variable-popover
:target="`ci-variable-env-${item.id}`"
- :value="item.environment_scope"
+ :value="convertEnvironmentScopeValue(item.environmentScope)"
:tooltip-text="__('Copy environment')"
/>
</div>
@@ -170,7 +209,7 @@ export default {
icon="pencil"
:aria-label="__('Edit')"
data-qa-selector="edit_ci_variable_button"
- @click="editVariable(item)"
+ @click="setSelectedVariable(item)"
/>
</template>
<template #empty>
@@ -186,12 +225,14 @@ export default {
data-qa-selector="add_ci_variable_button"
variant="confirm"
category="primary"
+ :aria-label="__('Add')"
+ @click="setSelectedVariable()"
>{{ __('Add variable') }}</gl-button
>
<gl-button
v-if="!isTableEmpty"
data-qa-selector="reveal_ci_variable_value_button"
- @click="toggleValues(!valuesHidden)"
+ @click="toggleHiddenState"
>{{ valuesButtonText }}</gl-button
>
</div>
diff --git a/app/assets/javascripts/editor/schema/ci.json b/app/assets/javascripts/editor/schema/ci.json
index 6c805f7b4ea..848ba7dbeef 100644
--- a/app/assets/javascripts/editor/schema/ci.json
+++ b/app/assets/javascripts/editor/schema/ci.json
@@ -245,6 +245,10 @@
"terraform": {
"$ref": "#/definitions/string_file_list",
"description": "Path to file or list of files with terraform plan(s)."
+ },
+ "cyclonedx": {
+ "$ref": "#/definitions/string_file_list",
+ "markdownDescription": "Path to file or list of files with cyclonedx report(s). [Learn More](https://docs.gitlab.com/ee/ci/yaml/artifacts_reports.html#artifactsreportscyclonedx)."
}
}
}
diff --git a/app/assets/javascripts/sidebar/components/severity/severity.vue b/app/assets/javascripts/sidebar/components/severity/severity.vue
index 0db856543d0..776dab98f01 100644
--- a/app/assets/javascripts/sidebar/components/severity/severity.vue
+++ b/app/assets/javascripts/sidebar/components/severity/severity.vue
@@ -37,10 +37,10 @@ export default {
<gl-icon
:size="iconSize"
:name="`severity-${severity.icon}`"
- :class="[`icon-${severity.icon}`, { 'gl-mr-3': !iconOnly }]"
+ :class="[`icon-${severity.icon}`, { 'gl-mr-3 gl-flex-shrink-0': !iconOnly }]"
/>
- <tooltip-on-truncate v-if="!iconOnly" :title="severity.label" class="gl-text-truncate">{{
- severity.label
- }}</tooltip-on-truncate>
+ <tooltip-on-truncate v-if="!iconOnly" :title="severity.label" class="gl-text-truncate">
+ {{ severity.label }}
+ </tooltip-on-truncate>
</div>
</template>
diff --git a/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue b/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue
index 86e46016534..bf4ba715f85 100644
--- a/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue
+++ b/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue
@@ -149,23 +149,25 @@ export default {
</div>
<div class="hide-collapsed">
- <p
- class="gl-line-height-20 gl-mb-2 gl-text-gray-900 gl-display-flex gl-justify-content-space-between"
+ <div
+ class="gl-display-flex gl-align-items-center gl-line-height-20 gl-text-gray-900 gl-font-weight-bold"
>
{{ $options.i18n.SEVERITY }}
<gl-button
v-if="canUpdate"
category="tertiary"
size="small"
+ class="gl-ml-auto hide-collapsed gl-mr-n2"
data-testid="editButton"
@click="toggleFormDropdown"
@keydown.esc="hideDropdown"
>
{{ $options.i18n.EDIT }}
</gl-button>
- </p>
+ </div>
<gl-dropdown
+ class="gl-mt-3"
:class="dropdownClass"
block
:header-text="__('Assign severity')"
diff --git a/app/controllers/admin/topics_controller.rb b/app/controllers/admin/topics_controller.rb
index b451928e591..69bcfdf4791 100644
--- a/app/controllers/admin/topics_controller.rb
+++ b/app/controllers/admin/topics_controller.rb
@@ -45,6 +45,22 @@ class Admin::TopicsController < Admin::ApplicationController
notice: _('Topic %{topic_name} was successfully removed.') % { topic_name: @topic.title_or_name }
end
+ def merge
+ source_topic = Projects::Topic.find(merge_params[:source_topic_id])
+ target_topic = Projects::Topic.find(merge_params[:target_topic_id])
+
+ begin
+ ::Topics::MergeService.new(source_topic, target_topic).execute
+ rescue ArgumentError => e
+ return render status: :bad_request, json: { type: :alert, message: e.message }
+ end
+
+ message = _('Topic %{source_topic} was successfully merged into topic %{target_topic}.')
+ redirect_to admin_topics_path,
+ status: :found,
+ notice: message % { source_topic: source_topic.name, target_topic: target_topic.name }
+ end
+
private
def topic
@@ -63,4 +79,8 @@ class Admin::TopicsController < Admin::ApplicationController
:title
]
end
+
+ def merge_params
+ params.permit([:source_topic_id, :target_topic_id])
+ end
end
diff --git a/app/models/concerns/cross_database_modification.rb b/app/models/concerns/cross_database_modification.rb
index dea62f03f91..273d5f35e76 100644
--- a/app/models/concerns/cross_database_modification.rb
+++ b/app/models/concerns/cross_database_modification.rb
@@ -80,34 +80,22 @@ module CrossDatabaseModification
end
def transaction(**options, &block)
- if track_gitlab_schema_in_current_transaction?
- super(**options) do
- # Hook into current transaction to ensure that once
- # the `COMMIT` is executed the `gitlab_transactions_stack`
- # will be allowing to execute `after_commit_queue`
- record = TransactionStackTrackRecord.new(self, gitlab_schema)
-
- begin
- connection.current_transaction.add_record(record)
-
- yield
- ensure
- record.done!
- end
+ super(**options) do
+ # Hook into current transaction to ensure that once
+ # the `COMMIT` is executed the `gitlab_transactions_stack`
+ # will be allowing to execute `after_commit_queue`
+ record = TransactionStackTrackRecord.new(self, gitlab_schema)
+
+ begin
+ connection.current_transaction.add_record(record)
+
+ yield
+ ensure
+ record.done!
end
- else
- super(**options, &block)
end
end
- def track_gitlab_schema_in_current_transaction?
- return false unless Feature::FlipperFeature.table_exists?
-
- Feature.enabled?(:track_gitlab_schema_in_current_transaction)
- rescue ActiveRecord::NoDatabaseError, PG::ConnectionBad
- false
- end
-
def gitlab_schema
case self.name
when 'ActiveRecord::Base', 'ApplicationRecord'
diff --git a/app/services/topics/merge_service.rb b/app/services/topics/merge_service.rb
new file mode 100644
index 00000000000..0d256579fe0
--- /dev/null
+++ b/app/services/topics/merge_service.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+module Topics
+ class MergeService
+ attr_accessor :source_topic, :target_topic
+
+ def initialize(source_topic, target_topic)
+ @source_topic = source_topic
+ @target_topic = target_topic
+ end
+
+ def execute
+ validate_parameters!
+
+ ::Projects::ProjectTopic.transaction do
+ move_project_topics
+ refresh_target_topic_counters
+ delete_source_topic
+ end
+ end
+
+ private
+
+ def validate_parameters!
+ raise ArgumentError, 'The source topic is not a topic.' unless source_topic.is_a?(Projects::Topic)
+ raise ArgumentError, 'The target topic is not a topic.' unless target_topic.is_a?(Projects::Topic)
+ raise ArgumentError, 'The source topic and the target topic are identical.' if source_topic == target_topic
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def move_project_topics
+ project_ids_for_projects_currently_using_source_and_target = ::Projects::ProjectTopic
+ .where(topic_id: target_topic).select(:project_id)
+ # Only update for projects that exclusively use the source topic
+ ::Projects::ProjectTopic.where(topic_id: source_topic.id)
+ .where.not(project_id: project_ids_for_projects_currently_using_source_and_target)
+ .update_all(topic_id: target_topic.id)
+
+ # Delete source topic for projects that were using source and target
+ ::Projects::ProjectTopic.where(topic_id: source_topic.id).delete_all
+ end
+
+ def refresh_target_topic_counters
+ target_topic.update!(
+ total_projects_count: total_projects_count(target_topic.id),
+ non_private_projects_count: non_private_projects_count(target_topic.id)
+ )
+ end
+
+ def delete_source_topic
+ source_topic.destroy!
+ end
+
+ def total_projects_count(topic_id)
+ ::Projects::ProjectTopic.where(topic_id: topic_id).count
+ end
+
+ def non_private_projects_count(topic_id)
+ ::Projects::ProjectTopic.joins(:project).where(topic_id: topic_id).where('projects.visibility_level in (10, 20)')
+ .count
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+end
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 1d6a6c2aed5..7d7d20ffd6a 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -58,7 +58,7 @@
- if header_link?(:merge_requests)
= nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter dropdown" }) do
- top_level_link = assigned_mrs_dashboard_path
- = link_to top_level_link, class: 'dashboard-shortcuts-merge_requests', title: _('Merge requests'), aria: { label: _('Merge requests') },
+ = link_to top_level_link, class: 'dashboard-shortcuts-merge_requests has-tooltip', title: _('Merge requests'), aria: { label: _('Merge requests') },
data: { qa_selector: 'merge_requests_shortcut_button',
toggle: "dropdown",
placement: 'bottom',