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>2020-07-08 12:09:17 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-07-08 12:09:17 +0300
commit21341457a8c422d890a9ec30838b597dea565d62 (patch)
treeaa8aca2a9bce4e16936cc8d7b40aa1c79ca82e35 /app
parent0a319374e7784aa5c2d1c30dd832d2a0509edbab (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue199
-rw-r--r--app/assets/javascripts/alerts_settings/constants.js13
-rw-r--r--app/assets/javascripts/alerts_settings/services/index.js9
-rw-r--r--app/assets/javascripts/ide/components/ide.vue9
-rw-r--r--app/assets/javascripts/issuables_list/components/issuable.vue39
-rw-r--r--app/assets/javascripts/issuables_list/components/issuables_list_app.vue30
-rw-r--r--app/assets/javascripts/pages/projects/integrations/jira/issues/index/index.js5
-rw-r--r--app/assets/stylesheets/pages/issues/issues_list.scss5
-rw-r--r--app/assets/stylesheets/utilities.scss5
-rw-r--r--app/finders/ci/variables_finder.rb31
-rw-r--r--app/helpers/operations_helper.rb2
-rw-r--r--app/models/ci/instance_variable.rb8
-rw-r--r--app/models/ci/variable.rb2
-rw-r--r--app/models/concerns/ci/contextable.rb2
-rw-r--r--app/models/concerns/update_project_statistics.rb12
-rw-r--r--app/models/project_statistics.rb2
-rw-r--r--app/models/snippet.rb4
-rw-r--r--app/models/snippet_statistics.rb36
-rw-r--r--app/services/snippets/destroy_service.rb5
-rw-r--r--app/services/snippets/update_statistics_service.rb5
-rw-r--r--app/views/projects/commits/show.html.haml2
-rw-r--r--app/views/projects/triggers/_trigger.html.haml2
22 files changed, 344 insertions, 83 deletions
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
index 7dde9edbd27..63f3b3bc1f0 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
@@ -5,18 +5,21 @@ import {
GlForm,
GlFormGroup,
GlFormInput,
+ GlFormInputGroup,
+ GlFormTextarea,
GlLink,
GlModal,
GlModalDirective,
GlSprintf,
GlFormSelect,
} from '@gitlab/ui';
+import { debounce } from 'lodash';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import ToggleButton from '~/vue_shared/components/toggle_button.vue';
import csrf from '~/lib/utils/csrf';
import service from '../services';
-import { i18n, serviceOptions } from '../constants';
+import { i18n, serviceOptions, JSON_VALIDATE_DELAY } from '../constants';
export default {
i18n,
@@ -27,7 +30,9 @@ export default {
GlForm,
GlFormGroup,
GlFormInput,
+ GlFormInputGroup,
GlFormSelect,
+ GlFormTextarea,
GlLink,
GlModal,
GlSprintf,
@@ -73,6 +78,11 @@ export default {
feedbackMessage: null,
isFeedbackDismissed: false,
},
+ testAlert: {
+ json: null,
+ error: null,
+ },
+ canSaveForm: false,
};
},
computed: {
@@ -109,12 +119,32 @@ export default {
showFeedbackMsg() {
return this.feedback.feedbackMessage && !this.isFeedbackDismissed;
},
+ showAlertSave() {
+ return (
+ this.feedback.feedbackMessage === this.$options.i18n.testAlertFailed &&
+ !this.isFeedbackDismissed
+ );
+ },
prometheusInfo() {
return !this.isGeneric ? this.$options.i18n.prometheusInfo : '';
},
prometheusFeatureEnabled() {
return !this.isGeneric && this.glFeatures.alertIntegrationsDropdown;
},
+ jsonIsValid() {
+ return this.testAlert.error === null;
+ },
+ canTestAlert() {
+ return this.selectedService.active && this.testAlert.json !== null;
+ },
+ canSaveConfig() {
+ return !this.loading && this.canSaveForm;
+ },
+ },
+ watch: {
+ 'testAlert.json': debounce(function debouncedJsonValidate() {
+ this.validateJson();
+ }, JSON_VALIDATE_DELAY),
},
created() {
if (this.glFeatures.alertIntegrationsDropdown) {
@@ -126,6 +156,9 @@ export default {
}
},
methods: {
+ clearJson() {
+ this.testAlert.json = null;
+ },
dismissFeedback() {
this.feedback = { ...this.feedback, feedbackMessage: null };
this.isFeedbackDismissed = false;
@@ -135,6 +168,7 @@ export default {
.updateGenericKey({ endpoint: this.generic.formPath, params: { service: { token: '' } } })
.then(({ data: { token } }) => {
this.authorizationKey.generic = token;
+ this.setFeedback({ feedbackMessage: this.$options.i18n.authKeyRest, variant: 'success' });
})
.catch(() => {
this.setFeedback({ feedbackMessage: this.$options.i18n.errorKeyMsg, variant: 'danger' });
@@ -145,11 +179,24 @@ export default {
.updatePrometheusKey({ endpoint: this.prometheus.prometheusResetKeyPath })
.then(({ data: { token } }) => {
this.authorizationKey.prometheus = token;
+ this.setFeedback({ feedbackMessage: this.$options.i18n.authKeyRest, variant: 'success' });
})
.catch(() => {
this.setFeedback({ feedbackMessage: this.$options.i18n.errorKeyMsg, variant: 'danger' });
});
},
+ toggleService(value) {
+ this.canSaveForm = true;
+ if (!this.glFeatures.alertIntegrationsDropdown) {
+ this.toggleActivated(value);
+ }
+
+ if (this.isGeneric) {
+ this.activated.generic = value;
+ } else {
+ this.activated.prometheus = value;
+ }
+ },
toggleActivated(value) {
return this.isGeneric
? this.toggleGenericActivated(value)
@@ -164,15 +211,14 @@ export default {
})
.then(() => {
this.activated.generic = value;
-
- if (value) {
- this.setFeedback({
- feedbackMessage: this.$options.i18n.endPointActivated,
- variant: 'success',
- });
- }
+ this.toggleSuccess(value);
+ })
+ .catch(() => {
+ this.setFeedback({
+ feedbackMessage: this.$options.i18n.errorMsg,
+ variant: 'danger',
+ });
})
- .catch(() => {})
.finally(() => {
this.loading = false;
});
@@ -191,12 +237,7 @@ export default {
})
.then(() => {
this.activated.prometheus = value;
- if (value) {
- this.setFeedback({
- feedbackMessage: this.$options.i18n.endPointActivated,
- variant: 'success',
- });
- }
+ this.toggleSuccess(value);
})
.catch(() => {
this.setFeedback({
@@ -208,16 +249,61 @@ export default {
this.loading = false;
});
},
+ toggleSuccess(value) {
+ if (value) {
+ this.setFeedback({
+ feedbackMessage: this.$options.i18n.endPointActivated,
+ variant: 'info',
+ });
+ } else {
+ this.setFeedback({
+ feedbackMessage: this.$options.i18n.changesSaved,
+ variant: 'info',
+ });
+ }
+ },
setFeedback({ feedbackMessage, variant }) {
this.feedback = { feedbackMessage, variant };
},
- onSubmit(evt) {
- // TODO: Add form submit as part of https://gitlab.com/gitlab-org/gitlab/-/issues/215356
- evt.preventDefault();
+ validateJson() {
+ this.testAlert.error = null;
+ try {
+ JSON.parse(this.testAlert.json);
+ } catch (e) {
+ this.testAlert.error = JSON.stringify(e.message);
+ }
},
- onReset(evt) {
- // TODO: Add form reset as part of https://gitlab.com/gitlab-org/gitlab/-/issues/215356
- evt.preventDefault();
+ validateTestAlert() {
+ this.loading = true;
+ this.validateJson();
+ return service
+ .updateTestAlert({
+ endpoint: this.selectedService.url,
+ data: this.testAlert.json,
+ authKey: this.selectedService.authKey,
+ })
+ .then(() => {
+ this.setFeedback({
+ feedbackMessage: this.$options.i18n.testAlertSuccess,
+ variant: 'success',
+ });
+ })
+ .catch(() => {
+ this.setFeedback({
+ feedbackMessage: this.$options.i18n.testAlertFailed,
+ variant: 'danger',
+ });
+ })
+ .finally(() => {
+ this.loading = false;
+ });
+ },
+ onSubmit() {
+ this.toggleActivated(this.selectedService.active);
+ },
+ onReset() {
+ this.testAlert.json = null;
+ this.dismissFeedback();
},
},
};
@@ -227,6 +313,15 @@ export default {
<div>
<gl-alert v-if="showFeedbackMsg" :variant="feedback.variant" @dismiss="dismissFeedback">
{{ feedback.feedbackMessage }}
+ <gl-button
+ v-if="showAlertSave"
+ variant="danger"
+ category="primary"
+ class="gl-display-block gl-mt-3"
+ @click="toggleActivated(selectedService.active)"
+ >
+ {{ __('Save anyway') }}
+ </gl-button>
</gl-alert>
<div data-testid="alert-settings-description" class="gl-mt-5">
<p v-for="section in sections" :key="section.text">
@@ -237,7 +332,7 @@ export default {
</gl-sprintf>
</p>
</div>
- <gl-form @submit="onSubmit" @reset="onReset">
+ <gl-form @submit.prevent="onSubmit" @reset.prevent="onReset">
<gl-form-group
v-if="glFeatures.alertIntegrationsDropdown"
:label="$options.i18n.integrationsLabel"
@@ -248,6 +343,7 @@ export default {
v-model="selectedEndpoint"
:options="options"
data-testid="alert-settings-select"
+ @change="clearJson"
/>
<span class="gl-text-gray-400">
<gl-sprintf :message="$options.i18n.integrationsInfo">
@@ -272,7 +368,7 @@ export default {
:disabled-input="loading"
:is-loading="loading"
:value="selectedService.active"
- @change="toggleActivated"
+ @change="toggleService"
/>
</gl-form-group>
<gl-form-group
@@ -293,12 +389,15 @@ export default {
</span>
</gl-form-group>
<gl-form-group :label="$options.i18n.urlLabel" label-for="url" label-class="label-bold">
- <div class="input-group">
- <gl-form-input id="url" :readonly="true" :value="selectedService.url" />
- <span class="input-group-append">
- <clipboard-button :text="selectedService.url" :title="$options.i18n.copyToClipboard" />
- </span>
- </div>
+ <gl-form-input-group id="url" :readonly="true" :value="selectedService.url">
+ <template #append>
+ <clipboard-button
+ :text="selectedService.url"
+ :title="$options.i18n.copyToClipboard"
+ class="gl-m-0!"
+ />
+ </template>
+ </gl-form-input-group>
<span class="gl-text-gray-400">
{{ prometheusInfo }}
</span>
@@ -308,15 +407,20 @@ export default {
label-for="authorization-key"
label-class="label-bold"
>
- <div class="input-group">
- <gl-form-input id="authorization-key" :readonly="true" :value="selectedService.authKey" />
- <span class="input-group-append">
+ <gl-form-input-group
+ id="authorization-key"
+ class="gl-mb-2"
+ :readonly="true"
+ :value="selectedService.authKey"
+ >
+ <template #append>
<clipboard-button
:text="selectedService.authKey"
:title="$options.i18n.copyToClipboard"
+ class="gl-m-0!"
/>
- </span>
- </div>
+ </template>
+ </gl-form-input-group>
<gl-button v-gl-modal.authKeyModal class="gl-mt-3">{{ $options.i18n.resetKey }}</gl-button>
<gl-modal
modal-id="authKeyModal"
@@ -328,11 +432,32 @@ export default {
{{ $options.i18n.restKeyInfo }}
</gl-modal>
</gl-form-group>
+ <gl-form-group
+ v-if="glFeatures.alertIntegrationsDropdown"
+ :label="$options.i18n.alertJson"
+ label-for="alert-json"
+ label-class="label-bold"
+ :invalid-feedback="testAlert.error"
+ >
+ <gl-form-textarea
+ id="alert-json"
+ v-model.trim="testAlert.json"
+ :disabled="!selectedService.active"
+ :state="jsonIsValid"
+ :placeholder="$options.i18n.alertJsonPlaceholder"
+ rows="6"
+ max-rows="10"
+ />
+ </gl-form-group>
+ <gl-button :disabled="!canTestAlert" @click="validateTestAlert">{{
+ $options.i18n.testAlertInfo
+ }}</gl-button>
<div
- class="footer-block row-content-block gl-display-flex gl-justify-content-space-between d-none"
+ v-if="glFeatures.alertIntegrationsDropdown"
+ class="footer-block row-content-block gl-display-flex gl-justify-content-space-between"
>
- <gl-button type="submit" variant="success" category="primary">
- {{ __('Save and test changes') }}
+ <gl-button type="submit" variant="success" category="primary" :disabled="!canSaveConfig">
+ {{ __('Save changes') }}
</gl-button>
<gl-button type="reset" variant="default" category="primary">
{{ __('Cancel') }}
diff --git a/app/assets/javascripts/alerts_settings/constants.js b/app/assets/javascripts/alerts_settings/constants.js
index 15618978145..0a022a07352 100644
--- a/app/assets/javascripts/alerts_settings/constants.js
+++ b/app/assets/javascripts/alerts_settings/constants.js
@@ -21,6 +21,7 @@ export const i18n = {
'AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.',
),
endPointActivated: s__('AlertSettings|Alerts endpoint successfully activated.'),
+ changesSaved: s__('AlertSettings|Your changes were successfully updated.'),
prometheusInfo: s__('AlertSettings|Add URL and auth key to your Prometheus config file'),
integrationsInfo: s__(
'AlertSettings|Learn more about our %{linkStart}upcoming integrations%{linkEnd}',
@@ -32,10 +33,20 @@ export const i18n = {
authKeyLabel: s__('AlertSettings|Authorization key'),
urlLabel: s__('AlertSettings|Webhook URL'),
activeLabel: s__('AlertSettings|Active'),
- apiBaseUrlHelpText: s__(' AlertSettings|URL cannot be blank and must start with http or https'),
+ apiBaseUrlHelpText: s__('AlertSettings|URL cannot be blank and must start with http or https'),
+ testAlertInfo: s__('AlertSettings|Test alert payload'),
+ alertJson: s__('AlertSettings|Alert test payload'),
+ alertJsonPlaceholder: s__('AlertSettings|Enter test alert JSON....'),
+ testAlertFailed: s__('AlertSettings|Test failed. Do you still want to save your changes anyway?'),
+ testAlertSuccess: s__(
+ 'AlertSettings|Test alert sent successfully. If you have made other changes, please save them now.',
+ ),
+ authKeyRest: s__('AlertSettings|Authorization key has been successfully reset'),
};
export const serviceOptions = [
{ value: 'generic', text: s__('AlertSettings|Generic') },
{ value: 'prometheus', text: s__('AlertSettings|External Prometheus') },
];
+
+export const JSON_VALIDATE_DELAY = 250;
diff --git a/app/assets/javascripts/alerts_settings/services/index.js b/app/assets/javascripts/alerts_settings/services/index.js
index 669c40bc86b..c49992d4f57 100644
--- a/app/assets/javascripts/alerts_settings/services/index.js
+++ b/app/assets/javascripts/alerts_settings/services/index.js
@@ -1,3 +1,4 @@
+/* eslint-disable @gitlab/require-i18n-strings */
import axios from '~/lib/utils/axios_utils';
export default {
@@ -24,4 +25,12 @@ export default {
},
});
},
+ updateTestAlert({ endpoint, data, authKey }) {
+ return axios.post(endpoint, data, {
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${authKey}`,
+ },
+ });
+ },
};
diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue
index e9f84eb8648..55b3eaf9737 100644
--- a/app/assets/javascripts/ide/components/ide.vue
+++ b/app/assets/javascripts/ide/components/ide.vue
@@ -1,6 +1,6 @@
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
-import { GlDeprecatedButton, GlLoadingIcon } from '@gitlab/ui';
+import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import { __ } from '~/locale';
import { modalTypes } from '../constants';
import FindFile from '~/vue_shared/components/file_finder/index.vue';
@@ -24,7 +24,7 @@ export default {
FindFile,
ErrorMessage,
CommitEditorHeader,
- GlDeprecatedButton,
+ GlButton,
GlLoadingIcon,
RightPane,
},
@@ -121,15 +121,16 @@ export default {
)
}}
</p>
- <gl-deprecated-button
+ <gl-button
variant="success"
+ category="primary"
:title="__('New file')"
:aria-label="__('New file')"
data-qa-selector="first_file_button"
@click="createNewFile()"
>
{{ __('New file') }}
- </gl-deprecated-button>
+ </gl-button>
</template>
<gl-loading-icon v-else-if="!currentTree || currentTree.loading" size="md" />
<p v-else>
diff --git a/app/assets/javascripts/issuables_list/components/issuable.vue b/app/assets/javascripts/issuables_list/components/issuable.vue
index b36a83c4974..9af1887ef12 100644
--- a/app/assets/javascripts/issuables_list/components/issuable.vue
+++ b/app/assets/javascripts/issuables_list/components/issuable.vue
@@ -7,6 +7,7 @@
// TODO: need to move this component to graphql - https://gitlab.com/gitlab-org/gitlab/-/issues/221246
import { escape, isNumber } from 'lodash';
import { GlLink, GlTooltipDirective as GlTooltip, GlSprintf, GlLabel, GlIcon } from '@gitlab/ui';
+import jiraLogo from '@gitlab/svgs/dist/illustrations/logos/jira.svg';
import {
dateInWords,
formatDate,
@@ -25,6 +26,7 @@ import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
i18n: {
openedAgo: __('opened %{timeAgoString} by %{user}'),
+ openedAgoJira: __('opened %{timeAgoString} by %{user} in Jira'),
},
components: {
IssueAssignees,
@@ -60,6 +62,11 @@ export default {
},
},
},
+ data() {
+ return {
+ jiraLogo,
+ };
+ },
computed: {
milestoneLink() {
const { title } = this.issuable.milestone;
@@ -87,6 +94,9 @@ export default {
isClosed() {
return this.issuable.state === 'closed';
},
+ isJiraIssue() {
+ return this.issuable.external_tracker === 'jira';
+ },
issueCreatedToday() {
return getDayDifference(new Date(this.issuable.created_at), new Date()) < 1;
},
@@ -223,7 +233,18 @@ export default {
:title="$options.confidentialTooltipText"
:aria-label="$options.confidentialTooltipText"
></i>
- <gl-link :href="issuable.web_url">{{ issuable.title }}</gl-link>
+ <gl-link
+ :href="issuable.web_url"
+ :target="isJiraIssue ? '_blank' : null"
+ data-testid="issuable-title"
+ >
+ {{ issuable.title }}
+ <gl-icon
+ v-if="isJiraIssue"
+ name="external-link"
+ class="gl-vertical-align-text-bottom"
+ />
+ </gl-link>
</span>
<span v-if="issuable.has_tasks" class="ml-1 task-status d-none d-sm-inline-block">
{{ issuable.task_status }}
@@ -231,11 +252,21 @@ export default {
</div>
<div class="issuable-info">
- <span class="js-ref-path">{{ referencePath }}</span>
+ <span class="js-ref-path">
+ <span
+ v-if="isJiraIssue"
+ class="svg-container jira-logo-container"
+ data-testid="jira-logo"
+ v-html="jiraLogo"
+ ></span>
+ {{ referencePath }}
+ </span>
<span data-testid="openedByMessage" class="d-none d-sm-inline-block mr-1">
&middot;
- <gl-sprintf :message="$options.i18n.openedAgo">
+ <gl-sprintf
+ :message="isJiraIssue ? $options.i18n.openedAgoJira : $options.i18n.openedAgo"
+ >
<template #timeAgoString>
<span>{{ issuableCreatedAt }}</span>
</template>
@@ -302,6 +333,7 @@ export default {
<!-- Issuable meta -->
<div class="flex-shrink-0 d-flex flex-column align-items-end justify-content-center">
<div class="controls d-flex">
+ <span v-if="isJiraIssue">&nbsp;</span>
<span v-if="isClosed" class="issuable-status">{{ __('CLOSED') }}</span>
<issue-assignees
@@ -326,6 +358,7 @@ export default {
</template>
<gl-link
+ v-if="!isJiraIssue"
v-gl-tooltip
class="ml-2 js-notes"
:href="`${issuable.web_url}#notes`"
diff --git a/app/assets/javascripts/issuables_list/components/issuables_list_app.vue b/app/assets/javascripts/issuables_list/components/issuables_list_app.vue
index 1c395fd9795..db18bcbce09 100644
--- a/app/assets/javascripts/issuables_list/components/issuables_list_app.vue
+++ b/app/assets/javascripts/issuables_list/components/issuables_list_app.vue
@@ -118,6 +118,29 @@ export default {
baseUrl() {
return window.location.href.replace(/(\?.*)?(#.*)?$/, '');
},
+ paginationNext() {
+ return this.page + 1;
+ },
+ paginationPrev() {
+ return this.page - 1;
+ },
+ paginationProps() {
+ const paginationProps = { value: this.page };
+
+ if (this.totalItems) {
+ return {
+ ...paginationProps,
+ perPage: this.itemsPerPage,
+ totalItems: this.totalItems,
+ };
+ }
+
+ return {
+ ...paginationProps,
+ prevPage: this.paginationPrev,
+ nextPage: this.paginationNext,
+ };
+ },
},
watch: {
selection() {
@@ -272,11 +295,8 @@ export default {
</ul>
<div class="mt-3">
<gl-pagination
- v-if="totalItems"
- :value="page"
- :per-page="itemsPerPage"
- :total-items="totalItems"
- class="justify-content-center"
+ v-bind="paginationProps"
+ class="gl-justify-content-center"
@input="onPaginate"
/>
</div>
diff --git a/app/assets/javascripts/pages/projects/integrations/jira/issues/index/index.js b/app/assets/javascripts/pages/projects/integrations/jira/issues/index/index.js
new file mode 100644
index 00000000000..260ba69b4bc
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/integrations/jira/issues/index/index.js
@@ -0,0 +1,5 @@
+import initIssuablesList from '~/issuables_list';
+
+document.addEventListener('DOMContentLoaded', () => {
+ initIssuablesList();
+});
diff --git a/app/assets/stylesheets/pages/issues/issues_list.scss b/app/assets/stylesheets/pages/issues/issues_list.scss
new file mode 100644
index 00000000000..c0af7a6af6d
--- /dev/null
+++ b/app/assets/stylesheets/pages/issues/issues_list.scss
@@ -0,0 +1,5 @@
+.svg-container.jira-logo-container {
+ svg {
+ vertical-align: text-bottom;
+ }
+}
diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss
index e02b4eff9f0..94af1df2ccb 100644
--- a/app/assets/stylesheets/utilities.scss
+++ b/app/assets/stylesheets/utilities.scss
@@ -117,8 +117,3 @@
.gl-border-b-2 {
border-bottom-width: $gl-border-size-2;
}
-
-// Remove once this MR has been merged in GitLab UI > https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/1539
-.gl-min-w-full {
- min-width: 100%;
-}
diff --git a/app/finders/ci/variables_finder.rb b/app/finders/ci/variables_finder.rb
new file mode 100644
index 00000000000..d933643ffb2
--- /dev/null
+++ b/app/finders/ci/variables_finder.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Ci
+ class VariablesFinder
+ attr_reader :project, :params
+
+ def initialize(project, params)
+ @project, @params = project, params
+
+ raise ArgumentError, 'Please provide params[:key]' if params[:key].blank?
+ end
+
+ def execute
+ variables = project.variables
+ variables = by_key(variables)
+ variables = by_environment_scope(variables)
+ variables
+ end
+
+ private
+
+ def by_key(variables)
+ variables.by_key(params[:key])
+ end
+
+ def by_environment_scope(variables)
+ environment_scope = params.dig(:filter, :environment_scope)
+ environment_scope.present? ? variables.by_environment_scope(environment_scope) : variables
+ end
+ end
+end
diff --git a/app/helpers/operations_helper.rb b/app/helpers/operations_helper.rb
index 7500f7b1ff4..419fdd521c2 100644
--- a/app/helpers/operations_helper.rb
+++ b/app/helpers/operations_helper.rb
@@ -17,7 +17,7 @@ module OperationsHelper
def alerts_settings_data
{
- 'prometheus_activated' => prometheus_service.activated?.to_s,
+ 'prometheus_activated' => prometheus_service.manual_configuration?.to_s,
'activated' => alerts_service.activated?.to_s,
'prometheus_form_path' => scoped_integration_path(prometheus_service),
'form_path' => scoped_integration_path(alerts_service),
diff --git a/app/models/ci/instance_variable.rb b/app/models/ci/instance_variable.rb
index 8245729a884..628749b32cb 100644
--- a/app/models/ci/instance_variable.rb
+++ b/app/models/ci/instance_variable.rb
@@ -45,13 +45,5 @@ module Ci
end
end
end
-
- private
-
- def validate_plan_limit_not_exceeded
- if Gitlab::Ci::Features.instance_level_variables_limit_enabled?
- super
- end
- end
end
end
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index 08d39595c61..13358b95a47 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -18,5 +18,7 @@ module Ci
}
scope :unprotected, -> { where(protected: false) }
+ scope :by_key, -> (key) { where(key: key) }
+ scope :by_environment_scope, -> (environment_scope) { where(environment_scope: environment_scope) }
end
end
diff --git a/app/models/concerns/ci/contextable.rb b/app/models/concerns/ci/contextable.rb
index 7ea5382a4fa..10df5e1a8dc 100644
--- a/app/models/concerns/ci/contextable.rb
+++ b/app/models/concerns/ci/contextable.rb
@@ -84,8 +84,6 @@ module Ci
end
def secret_instance_variables
- return [] unless ::Feature.enabled?(:ci_instance_level_variables, project, default_enabled: true)
-
project.ci_instance_variables_for(ref: git_ref)
end
diff --git a/app/models/concerns/update_project_statistics.rb b/app/models/concerns/update_project_statistics.rb
index 6cf012680d8..c0fa14d3369 100644
--- a/app/models/concerns/update_project_statistics.rb
+++ b/app/models/concerns/update_project_statistics.rb
@@ -35,8 +35,8 @@ module UpdateProjectStatistics
@project_statistics_name = project_statistics_name
@statistic_attribute = statistic_attribute
- after_save(:update_project_statistics_after_save, if: :update_project_statistics_attribute_changed?)
- after_destroy(:update_project_statistics_after_destroy, unless: :project_destroyed?)
+ after_save(:update_project_statistics_after_save, if: :update_project_statistics_after_save?)
+ after_destroy(:update_project_statistics_after_destroy, if: :update_project_statistics_after_destroy?)
end
private :update_project_statistics
@@ -45,6 +45,14 @@ module UpdateProjectStatistics
included do
private
+ def update_project_statistics_after_save?
+ update_project_statistics_attribute_changed?
+ end
+
+ def update_project_statistics_after_destroy?
+ !project_destroyed?
+ end
+
def update_project_statistics_after_save
attr = self.class.statistic_attribute
delta = read_attribute(attr).to_i - attribute_before_last_save(attr).to_i
diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb
index 55c7c6ab682..f153bfe3f5b 100644
--- a/app/models/project_statistics.rb
+++ b/app/models/project_statistics.rb
@@ -12,7 +12,7 @@ class ProjectStatistics < ApplicationRecord
before_save :update_storage_size
COLUMNS_TO_REFRESH = [:repository_size, :wiki_size, :lfs_objects_size, :commit_count, :snippets_size].freeze
- INCREMENTABLE_COLUMNS = { build_artifacts_size: %i[storage_size], packages_size: %i[storage_size] }.freeze
+ INCREMENTABLE_COLUMNS = { build_artifacts_size: %i[storage_size], packages_size: %i[storage_size], snippets_size: %i[storage_size] }.freeze
NAMESPACE_RELATABLE_COLUMNS = [:repository_size, :wiki_size, :lfs_objects_size].freeze
scope :for_project_ids, ->(project_ids) { where(project_id: project_ids) }
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index 5f45407c05e..eb3960ff12b 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -44,7 +44,9 @@ class Snippet < ApplicationRecord
has_many :notes, as: :noteable, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :user_mentions, class_name: "SnippetUserMention", dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_one :snippet_repository, inverse_of: :snippet
- has_one :statistics, class_name: 'SnippetStatistics'
+
+ # We need to add the `dependent` in order to call the after_destroy callback
+ has_one :statistics, class_name: 'SnippetStatistics', dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
delegate :name, :email, to: :author, prefix: true, allow_nil: true
diff --git a/app/models/snippet_statistics.rb b/app/models/snippet_statistics.rb
index 8030328ebe4..7439f98d114 100644
--- a/app/models/snippet_statistics.rb
+++ b/app/models/snippet_statistics.rb
@@ -1,11 +1,19 @@
# frozen_string_literal: true
class SnippetStatistics < ApplicationRecord
+ include AfterCommitQueue
+ include UpdateProjectStatistics
+
belongs_to :snippet
validates :snippet, presence: true
- delegate :repository, to: :snippet
+ update_project_statistics project_statistics_name: :snippets_size, statistic_attribute: :repository_size
+
+ delegate :repository, :project, :project_id, to: :snippet
+
+ after_save :update_author_root_storage_statistics, if: :update_author_root_storage_statistics?
+ after_destroy :update_author_root_storage_statistics, unless: :project_snippet?
def update_commit_count
self.commit_count = repository.commit_count
@@ -32,4 +40,30 @@ class SnippetStatistics < ApplicationRecord
save!
end
+
+ private
+
+ alias_method :original_update_project_statistics_after_save?, :update_project_statistics_after_save?
+ def update_project_statistics_after_save?
+ project_snippet? && original_update_project_statistics_after_save?
+ end
+
+ alias_method :original_update_project_statistics_after_destroy?, :update_project_statistics_after_destroy?
+ def update_project_statistics_after_destroy?
+ project_snippet? && original_update_project_statistics_after_destroy?
+ end
+
+ def update_author_root_storage_statistics?
+ !project_snippet? && saved_change_to_repository_size?
+ end
+
+ def update_author_root_storage_statistics
+ run_after_commit do
+ Namespaces::ScheduleAggregationWorker.perform_async(snippet.author.namespace_id)
+ end
+ end
+
+ def project_snippet?
+ snippet.is_a?(ProjectSnippet)
+ end
end
diff --git a/app/services/snippets/destroy_service.rb b/app/services/snippets/destroy_service.rb
index 146a0b53fc1..977626fcf17 100644
--- a/app/services/snippets/destroy_service.rb
+++ b/app/services/snippets/destroy_service.rb
@@ -27,11 +27,6 @@ module Snippets
attempt_destroy!
- # Update project statistics if the snippet is a Project one
- if snippet.project_id
- ProjectCacheWorker.perform_async(snippet.project_id, [], [:snippets_size])
- end
-
ServiceResponse.success(message: 'Snippet was deleted.')
rescue DestroyError
service_response_error('Failed to remove snippet repository.', 400)
diff --git a/app/services/snippets/update_statistics_service.rb b/app/services/snippets/update_statistics_service.rb
index 61fa43e7755..295cb963ccc 100644
--- a/app/services/snippets/update_statistics_service.rb
+++ b/app/services/snippets/update_statistics_service.rb
@@ -16,11 +16,6 @@ module Snippets
snippet.repository.expire_statistics_caches
statistics.refresh!
- # Update project statistics if the snippet is a Project one
- if snippet.project_id
- ProjectCacheWorker.perform_async(snippet.project_id, [], [:snippets_size])
- end
-
ServiceResponse.success(message: 'Snippet statistics successfully updated.')
end
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index d65563a6eba..737e4f66dd2 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -24,7 +24,7 @@
.control
= form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form js-signature-container', data: { 'signatures-path' => namespace_project_signatures_path }) do
- = search_field_tag :search, params[:search], { placeholder: _('Search by message'), id: 'commits-search', class: 'form-control search-text-input input-short gl-mt-3 mt-sm-0 gl-min-w-full', spellcheck: false }
+ = search_field_tag :search, params[:search], { placeholder: _('Search by message'), id: 'commits-search', class: 'form-control search-text-input input-short gl-mt-3 gl-sm-mt-0 gl-min-w-full', spellcheck: false }
.control.d-none.d-md-block
= link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn' do
= icon("rss")
diff --git a/app/views/projects/triggers/_trigger.html.haml b/app/views/projects/triggers/_trigger.html.haml
index d80248f7e80..3036e918160 100644
--- a/app/views/projects/triggers/_trigger.html.haml
+++ b/app/views/projects/triggers/_trigger.html.haml
@@ -31,7 +31,7 @@
- revoke_trigger_confirmation = "By revoking a trigger you will break any processes making use of it. Are you sure?"
- if can?(current_user, :admin_trigger, trigger)
= link_to edit_project_trigger_path(@project, trigger), method: :get, title: "Edit", class: "btn btn-default btn-sm" do
- %i.fa.fa-pencil
+ = sprite_icon('pencil')
- if can?(current_user, :manage_trigger, trigger)
= link_to project_trigger_path(@project, trigger), data: { confirm: revoke_trigger_confirmation }, method: :delete, title: "Revoke", class: "btn btn-default btn-warning btn-sm btn-trigger-revoke" do
%i.fa.fa-trash