Commit 9cfcc6e6 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents c2890c65 025f778f
......@@ -7,6 +7,7 @@ stages:
- post-test
- review-prepare
- review
- dast
- qa
- post-qa
- pages
......@@ -104,3 +105,4 @@ include:
- local: .gitlab/ci/yaml.gitlab-ci.yml
- local: .gitlab/ci/releases.gitlab-ci.yml
- local: .gitlab/ci/notify.gitlab-ci.yml
- local: .gitlab/ci/dast.gitlab-ci.yml
.dast_conf:
tags:
- prm
# For scheduling dast job
extends:
- .reports:schedule-dast
image:
name: "registry.gitlab.com/gitlab-org/security-products/dast:$DAST_VERSION"
resource_group: dast_scan
variables:
DAST_USERNAME_FIELD: "user[login]"
DAST_PASSWORD_FIELD: "user[password]"
DAST_FULL_SCAN_ENABLED: "true"
DAST_SPIDER_MINS: 0
# TBD pin to a version
DAST_VERSION: 1.22.1
# -Xmx is used to set the JVM memory to 6GB to prevent DAST OutOfMemoryError.
DAST_ZAP_CLI_OPTIONS: "-Xmx6144m"
DAST_RULES: "41,42,43,10027,10032,10041,10042,10045,10047,10052,10053,10057,10061,10096,10097,10104,10106,20012,20014,20015,20016,20017,20018,40019,40020,40021,40024,40025,40027,40029,40032,90001,90019,10109,10026,10028,10029,10030,10031,10033,10034,10035,10036,10038,10039,10043,10044,10048,10050,10051,10058,10062,10095,10107,10108,30003,40013,40022,40023,40028,90021,90023,90024,90025,90027,90028,10003,50003,0,2,3,6,7,10010,10011,10015,10017,10019,10020,10021,10023,10024,10025,10037,10040,10054,10055,10056,10098,10105,10202,20019,30001,30002,40003,40008,40009,40012,40014,40016,40017,40018,50000,50001,90011,90020,90022,90033"
before_script:
- 'export DAST_WEBSITE="${DAST_WEBSITE:-$(cat environment_url.txt)}"'
- 'export DAST_AUTH_URL="${DAST_WEBSITE}/users/sign_in"'
- 'export DAST_PASSWORD="${REVIEW_APPS_ROOT_PASSWORD}"'
# Below three lines can be removed once https://gitlab.com/gitlab-org/gitlab/-/issues/230687 is fixed
- mkdir -p /zap/xml
- 'sed -i "84 s/true/false/" /zap/xml/config.xml'
- cat /zap/xml/config.xml
# Help pages are excluded from scan as they are static pages.
# profile/two_factor_auth is excluded from scan to prevent 2FA from being turned on from user profile, which will reduce coverage.
- 'export DAST_AUTH_EXCLUDE_URLS="${DAST_WEBSITE}/help/.*,${DAST_WEBSITE}/profile/two_factor_auth,${DAST_WEBSITE}/users/sign_out"'
- enable_rule () { read all_rules; rule=$1; echo $all_rules | sed -r "s/(,)?$rule(,)?/\1-1\2/" ; }
# Sort ids in DAST_RULES ascendingly, which is required when using DAST_RULES as argument to enable_rule
- 'DAST_RULES=$(echo $DAST_RULES | tr "," "\n" | sort -n | paste -sd ",")'
needs: ["review-deploy"]
stage: dast
# Default job timeout set to 90m and dast rules needs 2h to so that it won't timeout.
timeout: 2h
artifacts:
paths:
- gl-dast-report.json # GitLab-specific
reports:
dast: gl-dast-report.json
expire_in: 1 week # GitLab-specific
# DAST scan with a subset of Release scan rules.
DAST-fullscan-ruleset1:
extends:
- .dast_conf
variables:
DAST_USERNAME: "user1"
script:
- export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 10019 | enable_rule 10020 | enable_rule 10021 | enable_rule 10023 | enable_rule 10024 | enable_rule 10025 | enable_rule 10037 | enable_rule 10040 | enable_rule 10054 | enable_rule 10055 | enable_rule 10056)
- echo $DAST_EXCLUDE_RULES
- /analyze -t $DAST_WEBSITE -d
# DAST scan with a subset of Release scan rules.
DAST-fullscan-ruleset2:
extends:
- .dast_conf
variables:
DAST_USERNAME: "user2"
script:
- export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 90011 | enable_rule 90020 | enable_rule 90022 | enable_rule 90033)
- echo $DAST_EXCLUDE_RULES
- /analyze -t $DAST_WEBSITE -d
# DAST scan with a subset of Release scan rules.
DAST-fullscan-ruleset3:
extends:
- .dast_conf
variables:
DAST_USERNAME: "user3"
script:
- export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 40016 | enable_rule 40017 | enable_rule 50000 | enable_rule 50001)
- echo $DAST_EXCLUDE_RULES
- /analyze -t $DAST_WEBSITE -d
# DAST scan with a subset of Release scan rules.
DAST-fullscan-ruleset4:
extends:
- .dast_conf
variables:
DAST_USERNAME: "user4"
script:
- export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 0 | enable_rule 2 | enable_rule 3 | enable_rule 7 )
- echo $DAST_EXCLUDE_RULES
- /analyze -t $DAST_WEBSITE -d
# DAST scan with a subset of Release scan rules.
DAST-fullscan-ruleset5:
extends:
- .dast_conf
variables:
DAST_USERNAME: "user5"
script:
- export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 10010 | enable_rule 10011 | enable_rule 10015 | enable_rule 10017 | enable_rule 10019)
- echo $DAST_EXCLUDE_RULES
- /analyze -t $DAST_WEBSITE -d
# DAST scan with a subset of Release scan rules.
DAST-fullscan-ruleset6:
extends:
- .dast_conf
variables:
DAST_USERNAME: "user6"
script:
- export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 30001 | enable_rule 40009)
- echo $DAST_EXCLUDE_RULES
- /analyze -t $DAST_WEBSITE -d
# Enable when https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39749 is fixed
# DAST scan with a subset of Beta scan rules.
# DAST-fullscan-ruleset7:
# extends:
# - .dast_conf
# variables:
# DAST_USERNAME: "user7"
# script:
# - export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 10098 | enable_rule 10105 | enable_rule 10202 | enable_rule 30002 | enable_rule 40003 | enable_rule 40008 | enable_rule 40009)
# - echo $DAST_EXCLUDE_RULES
# - /analyze -t $DAST_WEBSITE -d
# Enable when https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39749 is fixed
# Below jobs runs DAST scans with one time consuming scan rule. These scan rules are disabled in above jobs so that those jobs won't timeout.
# DAST scan with rule - 20019 External Redirect
# DAST-fullscan-rule-20019:
# extends:
# - .dast_conf
# variables:
# DAST_USERNAME: "user8"
# script:
# - export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 20019)
# - echo $DAST_EXCLUDE_RULES
# - /analyze -t $DAST_WEBSITE -d
# Enable when https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39749 is fixed
# DAST scan with rule - 10107 Httpoxy - Proxy Header Misuse - Active/beta
# DAST-fullscan-rule-10107:
# extends:
# - .dast_conf
# variables:
# DAST_USERNAME: "user9"
# script:
# - export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 10107)
# - echo $DAST_EXCLUDE_RULES
# - /analyze -t $DAST_WEBSITE -d
# DAST scan with rule - 90020 Remote OS Command Injection
DAST-fullscan-rule-90020:
extends:
- .dast_conf
variables:
DAST_USERNAME: "user10"
script:
- export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 90020)
- echo $DAST_EXCLUDE_RULES
- /analyze -t $DAST_WEBSITE -d
# DAST scan with rule - 40018 SQL Injection - Active/release
DAST-fullscan-rule-40018:
extends:
- .dast_conf
variables:
DAST_USERNAME: "user11"
script:
- export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 40018)
- echo $DAST_EXCLUDE_RULES
- /analyze -t $DAST_WEBSITE -d
# DAST scan with rule - 40014 Cross Site Scripting (Persistent) - Active/release
DAST-fullscan-rule-40014:
extends:
- .dast_conf
variables:
DAST_USERNAME: "user12"
script:
- export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 40014)
- echo $DAST_EXCLUDE_RULES
- /analyze -t $DAST_WEBSITE -d
# DAST scan with rule - 6 Path travesal
DAST-fullscan-rule-6:
extends:
- .dast_conf
variables:
DAST_USERNAME: "user13"
script:
- export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 6)
- echo $DAST_EXCLUDE_RULES
- /analyze -t $DAST_WEBSITE -d
# DAST scan with rule - 40012 Cross Site Scripting (Reflected)
DAST-fullscan-rule-40012:
extends:
- .dast_conf
variables:
DAST_USERNAME: "user14"
script:
- export DAST_EXCLUDE_RULES=$(echo $DAST_RULES | enable_rule 40012)
- echo $DAST_EXCLUDE_RULES
- /analyze -t $DAST_WEBSITE -d
......@@ -145,45 +145,3 @@ dependency_scanning:
reports:
dependency_scanning: gl-dependency-scanning-report.json
expire_in: 1 week # GitLab-specific
# Temporarily disabling review apps
## We need to duplicate this job's definition because it seems it's impossible to
## override an included `only.refs`.
## See https://gitlab.com/gitlab-org/gitlab/issues/31371.
# dast:
# extends:
# - .default-retry
# - .reports:rules:dast
# # This is needed so that manual jobs with needs don't block the pipeline.
# # See https://gitlab.com/gitlab-org/gitlab/-/issues/199979.
# dependencies: ["review-deploy"]
# stage: qa # GitLab-specific
# image:
# name: "registry.gitlab.com/gitlab-org/security-products/dast:$DAST_VERSION"
# variables:
# # To be done in a later iteration
# # DAST_USERNAME: "root"
# # DAST_USERNAME_FIELD: "user[login]"
# # DAST_PASSWORD_FIELD: "user[passowrd]"
# DAST_VERSION: 1
# script:
# - 'export DAST_WEBSITE="${DAST_WEBSITE:-$(cat environment_url.txt)}"'
# # To be done in a later iteration
# # - 'export DAST_AUTH_URL="${DAST_WEBSITE}/users/sign_in"'
# # - 'export DAST_PASSWORD="${REVIEW_APPS_ROOT_PASSWORD}"'
# - /analyze -t $DAST_WEBSITE
# timeout: 4h
# artifacts:
# paths:
# - gl-dast-report.json # GitLab-specific
# reports:
# dast: gl-dast-report.json
# expire_in: 1 week # GitLab-specific
# To be done in a later iteration: https://gitlab.com/gitlab-org/gitlab/issues/31160#note_278188255
# schedule:dast:
# extends:
# - dast
# - .reports:schedule-dast
# variables:
# DAST_FULL_SCAN_ENABLED: "true"
......@@ -77,6 +77,11 @@ review-deploy:
# to have to manually start the jobs in sequence, so we do it for them.
- '[ -z $CI_JOB_MANUAL ] || play_job "review-qa-smoke"'
- '[ -z $CI_JOB_MANUAL ] || play_job "review-performance"'
after_script:
# Run seed-dast-test-data.sh only when DAST_RUN is set to true. This is to pupulate review app with data for DAST scan.
# Set DAST_RUN to true when jobs are manually scheduled.
- if [ "$DAST_RUN" == "true" ]; then source scripts/review_apps/seed-dast-test-data.sh; TRACE=1 trigger_proj_user_creation; fi
artifacts:
paths: [environment_url.txt]
expire_in: 2 days
......
......@@ -645,6 +645,7 @@
- if: '$DAST_DISABLED || $GITLAB_FEATURES !~ /\bdast\b/'
when: never
- <<: *if-dot-com-gitlab-org-schedule
allow_failure: true
################
# Review rules #
......@@ -665,6 +666,8 @@
.review:rules:mr-and-schedule-auto-if-frontend-manual-otherwise:
rules:
- if: '$DAST_RUN == "true"' # Skip this job when DAST is run
when: never
- <<: *if-not-ee
when: never
- <<: *if-dot-com-gitlab-org-merge-request
......
......@@ -1033,8 +1033,6 @@ Rails/SaveBang:
- 'ee/spec/support/shared_examples/models/member_shared_examples.rb'
- 'ee/spec/support/shared_examples/models/mentionable_shared_examples.rb'
- 'ee/spec/support/shared_examples/policies/protected_environments_shared_examples.rb'
- 'ee/spec/support/shared_examples/requests/api/graphql/geo/registries_shared_examples.rb'
- 'ee/spec/support/shared_examples/requests/api/project_approval_rules_api_shared_examples.rb'
- 'ee/spec/support/shared_examples/services/build_execute_shared_examples.rb'
- 'ee/spec/support/shared_examples/services/issue_epic_shared_examples.rb'
- 'ee/spec/workers/adjourned_project_deletion_worker_spec.rb'
......@@ -1489,11 +1487,6 @@ Rails/SaveBang:
- 'spec/support/shared_examples/policies/project_policy_shared_examples.rb'
- 'spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb'
- 'spec/support/shared_examples/quick_actions/merge_request/merge_quick_action_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/award_emoji_todo_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/boards_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb'
- 'spec/support/shared_examples/serializers/note_entity_shared_examples.rb'
- 'spec/support/shared_examples/services/common_system_notes_shared_examples.rb'
- 'spec/support/shared_examples/services/issuable_shared_examples.rb'
......
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
import { GlLoadingIcon, GlButton, GlIcon } from '@gitlab/ui';
import { sprintf, n__ } from '~/locale';
import DraftsCount from './drafts_count.vue';
import PublishButton from './publish_button.vue';
......@@ -9,6 +9,7 @@ import PreviewItem from './preview_item.vue';
export default {
components: {
GlLoadingIcon,
GlButton,
GlIcon,
DraftsCount,
PublishButton,
......@@ -28,7 +29,7 @@ export default {
watch: {
showPreviewDropdown() {
if (this.showPreviewDropdown && this.$refs.dropdown) {
this.$nextTick(() => this.$refs.dropdown.focus());
this.$nextTick(() => this.$refs.dropdown.$el.focus());
}
},
},
......@@ -62,16 +63,18 @@ export default {
show: showPreviewDropdown,
}"
>
<button
<gl-button
ref="dropdown"
type="button"
class="btn btn-success review-preview-dropdown-toggle qa-review-preview-toggle"
category="primary"
variant="success"
class="review-preview-dropdown-toggle qa-review-preview-toggle"
@click="toggleReviewDropdown"
>
{{ __('Finish review') }}
<drafts-count />
<gl-icon name="angle-up" />
</button>
</gl-button>
<div
class="dropdown-menu dropdown-menu-large dropdown-menu-right dropdown-open-top"
:class="{
......
/* eslint-disable class-methods-use-this */
import $ from 'jquery';
import '~/gl_dropdown';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default class TemplateSelector {
constructor({ dropdown, data, pattern, wrapper, editor, $input } = {}) {
......@@ -19,7 +19,7 @@ export default class TemplateSelector {
}
initDropdown(dropdown, data) {
return $(dropdown).glDropdown({
return initDeprecatedJQueryDropdown($(dropdown), {
data,
filterable: true,
selectable: true,
......
import FileTemplateSelector from '../file_template_selector';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default class BlobCiYamlSelector extends FileTemplateSelector {
constructor({ mediator }) {
......@@ -15,7 +16,7 @@ export default class BlobCiYamlSelector extends FileTemplateSelector {
initDropdown() {
// maybe move to super class as well
this.$dropdown.glDropdown({
initDeprecatedJQueryDropdown(this.$dropdown, {
data: this.$dropdown.data('data'),
filterable: true,
selectable: true,
......
import FileTemplateSelector from '../file_template_selector';
import { __ } from '~/locale';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default class DockerfileSelector extends FileTemplateSelector {
constructor({ mediator }) {
......@@ -16,7 +17,7 @@ export default class DockerfileSelector extends FileTemplateSelector {
initDropdown() {
// maybe move to super class as well
this.$dropdown.glDropdown({
initDeprecatedJQueryDropdown(this.$dropdown, {
data: this.$dropdown.data('data'),
filterable: true,
selectable: true,
......
import FileTemplateSelector from '../file_template_selector';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default class BlobGitignoreSelector extends FileTemplateSelector {
constructor({ mediator }) {
......@@ -14,7 +15,7 @@ export default class BlobGitignoreSelector extends FileTemplateSelector {
}
initDropdown() {
this.$dropdown.glDropdown({
initDeprecatedJQueryDropdown(this.$dropdown, {
data: this.$dropdown.data('data'),
filterable: true,
selectable: true,
......
import FileTemplateSelector from '../file_template_selector';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default class BlobLicenseSelector extends FileTemplateSelector {
constructor({ mediator }) {
......@@ -14,7 +15,7 @@ export default class BlobLicenseSelector extends FileTemplateSelector {
}
initDropdown() {
this.$dropdown.glDropdown({
initDeprecatedJQueryDropdown(this.$dropdown, {
data: this.$dropdown.data('data'),
filterable: true,
selectable: true,
......
import FileTemplateSelector from '../file_template_selector';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default class MetricsDashboardSelector extends FileTemplateSelector {
constructor({ mediator }) {
......@@ -14,7 +15,7 @@ export default class MetricsDashboardSelector extends FileTemplateSelector {
}
initDropdown() {
this.$dropdown.glDropdown({
initDeprecatedJQueryDropdown(this.$dropdown, {
data: this.$dropdown.data('data'),
filterable: true,
selectable: true,
......
import FileTemplateSelector from '../file_template_selector';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default class FileTemplateTypeSelector extends FileTemplateSelector {
constructor({ mediator, dropdownData }) {
......@@ -12,7 +13,7 @@ export default class FileTemplateTypeSelector extends FileTemplateSelector {
}
initDropdown() {
this.$dropdown.glDropdown({
initDeprecatedJQueryDropdown(this.$dropdown, {
data: this.config.dropdownData,
filterable: false,
selectable: true,
......
<script>
/* eslint-disable vue/require-default-prop */
import IssueCardInner from './issue_card_inner.vue';
import BoardCardLayout from './board_card_layout.vue';
import eventHub from '../eventhub';
import sidebarEventHub from '~/sidebar/event_hub';
import boardsStore from '../stores/boards_store';
......@@ -8,7 +7,7 @@ import boardsStore from '../stores/boards_store';
export default {
name: 'BoardsIssueCard',
components: {
IssueCardInner,
BoardCardLayout,
},
props: {
list: {
......@@ -21,80 +20,29 @@ export default {
default: () => ({}),
required: false,
},
issueLinkBase: {
type: String,
default: '',
required: false,
},
disabled: {
type: Boolean,
default: false,
required: false,
},
index: {
type: Number,
default: 0,
required: false,
},
rootPath: {
type: String,
default: '',
required: false,
},
groupId: {
type: Number,
required: false,
},
},
data() {
return {
showDetail: false,
detailIssue: boardsStore.detail,
multiSelect: boardsStore.multiSelect,
};
},
computed: {
issueDetailVisible() {
return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id;
},
multiSelectVisible() {
return this.multiSelect.list.findIndex(issue => issue.id === this.issue.id) > -1;
},
canMultiSelect() {
return gon.features && gon.features.multiSelectBoard;
},
},
methods: {
mouseDown() {
this.showDetail = true;
// These are methods instead of computed's, because boardsStore is not reactive.
isActive() {
return this.getActiveId() === this.issue.id;
},
mouseMove() {
this.showDetail = false;
getActiveId() {
return boardsStore.detail?.issue?.id;
},
showIssue(e) {
if (e.target.classList.contains('js-no-trigger')) return;
showIssue({ isMultiSelect }) {
// If no issues are opened, close all sidebars first
if (!boardsStore.detail?.issue?.id) {
if (!this.getActiveId()) {
sidebarEventHub.$emit('sidebar.closeAll');
}
if (this.isActive()) {
eventHub.$emit('clearDetailIssue', isMultiSelect);
// If CMD or CTRL is clicked
const isMultiSelect = this.canMultiSelect && (e.ctrlKey || e.metaKey);
if (this.showDetail || isMultiSelect) {
this.showDetail = false;
if (boardsStore.detail.issue && boardsStore.detail.issue.id === this.issue.id) {
eventHub.$emit('clearDetailIssue', isMultiSelect);
if (isMultiSelect) {
eventHub.$emit('newDetailIssue', this.issue, isMultiSelect);
}
} else {
if (isMultiSelect) {
eventHub.$emit('newDetailIssue', this.issue, isMultiSelect);
boardsStore.setListDetail(this.list);
}
} else {
eventHub.$emit('newDetailIssue', this.issue, isMultiSelect);
boardsStore.setListDetail(this.list);
}
},
},
......@@ -102,28 +50,12 @@ export default {
</script>
<template>
<li
:class="{
'multi-select': multiSelectVisible,
'user-can-drag': !disabled && issue.id,
'is-disabled': disabled || !issue.id,
'is-active': issueDetailVisible,
}"
:index="index"
:data-issue-id="issue.id"
<board-card-layout
data-qa-selector="board_card"
class="board-card p-3 rounded"
@mousedown="mouseDown"
@mousemove="mouseMove"
@mouseup="showIssue($event)"
>
<issue-card-inner
:list="list"
:issue="issue"
:issue-link-base="issueLinkBase"
:group-id="groupId"
:root-path="rootPath"
:update-filters="true"
/>
</li>
:issue="issue"
:list="list"
:is-active="isActive()"
v-bind="$attrs"
@show="showIssue"
/>
</template>
<script>
/* eslint-disable vue/require-default-prop */
import IssueCardInner from './issue_card_inner.vue';
import boardsStore from '../stores/boards_store';
export default {
name: 'BoardsIssueCard',
components: {
IssueCardInner,
},
props: {
list: {
type: Object,
default: () => ({}),
required: false,
},
issue: {
type: Object,
default: () => ({}),
required: false,
},
issueLinkBase: {
type: String,
default: '',
required: false,
},
disabled: {
type: Boolean,
default: false,
required: false,
},
index: {
type: Number,
default: 0,
required: false,
},
rootPath: {
type: String,
default: '',
required: false,
},
groupId: {
type: Number,
required: false,
},
isActive: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
showDetail: false,
multiSelect: boardsStore.multiSelect,
};
},
computed: {
multiSelectVisible() {
return this.multiSelect.list.findIndex(issue => issue.id === this.issue.id) > -1;
},
canMultiSelect() {
return gon.features && gon.features.multiSelectBoard;
},
},
methods: {
mouseDown() {
this.showDetail = true;
},
mouseMove() {
this.showDetail = false;
},
showIssue(e) {
// Don't do anything if this happened on a no trigger element
if (e.target.classList.contains('js-no-trigger')) return;
const isMultiSelect = this.canMultiSelect && (e.ctrlKey || e.metaKey);
if (this.showDetail || isMultiSelect) {
this.showDetail = false;
this.$emit('show', { event: e, isMultiSelect });
}
},
},
};
</script>
<template>
<li
:class="{
'multi-select': multiSelectVisible,
'user-can-drag': !disabled && issue.id,
'is-disabled': disabled || !issue.id,
'is-active': isActive,
}"
:index="index"
:data-issue-id="issue.id"
data-testid="board_card"
class="board-card p-3 rounded"
@mousedown="mouseDown"
@mousemove="mouseMove"
@mouseup="showIssue($event)"
>
<issue-card-inner
:list="list"
:issue="issue"
:issue-link-base="issueLinkBase"
:group-id="groupId"
:root-path="rootPath"
:update-filters="true"
/>
</li>
</template>
<script>
import { mapActions, mapState } from 'vuex';
import { GlAlert } from '@gitlab/ui';
import { mapState, mapGetters, mapActions } from 'vuex';
import BoardColumn from 'ee_else_ce/boards/components/board_column.vue';
import { GlAlert } from '@gitlab/ui';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
components: {
BoardColumn,
BoardContentSidebar: () => import('ee_component/boards/components/board_content_sidebar.vue'),
EpicsSwimlanes: () => import('ee_component/boards/components/epics_swimlanes.vue'),
GlAlert,
},
......@@ -43,10 +44,8 @@ export default {
},
},
computed: {
...mapState(['isShowingEpicsSwimlanes', 'boardLists', 'error']),
isSwimlanesOn() {
return this.glFeatures.boardsWithSwimlanes && this.isShowingEpicsSwimlanes;
},
...mapState(['boardLists', 'error']),
...mapGetters(['isSwimlanesOn']),
boardListsToUse() {
return this.glFeatures.graphqlBoardLists ? this.boardLists : this.lists;
},
......@@ -86,15 +85,18 @@ export default {
:board-id="boardId"
/>
</div>
<epics-swimlanes
v-else
ref="swimlanes"
:lists="boardListsToUse"
:can-admin-list="canAdminList"
:disabled="disabled"
:board-id="boardId"
:group-id="groupId"
:root-path="rootPath"
/>
<template v-else>
<epics-swimlanes
ref="swimlanes"
:lists="boardLists"
:can-admin-list="canAdminList"
:disabled="disabled"
:board-id="boardId"
:group-id="groupId"
:root-path="rootPath"
/>
<board-content-sidebar />
</template>
</div>
</template>
<script>
import { GlDrawer, GlLabel } from '@gitlab/ui';
import { mapActions, mapState } from 'vuex';
import { mapActions, mapState, mapGetters } from 'vuex';
import { __ } from '~/locale';
import boardsStore from '~/boards/stores/boards_store';
import eventHub from '~/sidebar/event_hub';
import { isScopedLabel } from '~/lib/utils/common_utils';
import { inactiveId } from '~/boards/constants';
import { LIST } from '~/boards/constants';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
// NOTE: need to revisit how we handle headerHeight, because we have so many different header and footer options.
......@@ -26,7 +26,8 @@ export default {
},
mixins: [glFeatureFlagMixin()],
computed: {
...mapState(['activeId', 'boardLists']),
...mapGetters(['isSidebarOpen']),
...mapState(['activeId', 'sidebarType', 'boardLists']),
activeList() {
/*
Warning: Though a computed property it is not reactive because we are
......@@ -37,9 +38,6 @@ export default {
}
return boardsStore.state.lists.find(({ id }) => id === this.activeId);
},
isSidebarOpen() {
return this.activeId !== inactiveId;
},
activeListLabel() {
return this.activeList.label;
},
......@@ -49,18 +47,18 @@ export default {
listTypeTitle() {
return this.$options.labelListText;
},
showSidebar() {
return this.sidebarType === LIST;
},
},
created() {
eventHub.$on('sidebar.closeAll', this.closeSidebar);
eventHub.$on('sidebar.closeAll', this.unsetActiveId);
},
beforeDestroy() {
eventHub.$off('sidebar.closeAll', this.closeSidebar);
eventHub.$off('sidebar.closeAll', this.unsetActiveId);
},
methods: {
...mapActions(['setActiveId']),
closeSidebar() {
this.setActiveId(inactiveId);
},
...mapActions(['unsetActiveId']),
showScopedLabels(label) {
return boardsStore.scopedLabels.enabled && isScopedLabel(label);
},
......@@ -70,10 +68,11 @@ export default {
<template>
<gl-drawer
v-if="showSidebar"
class="js-board-settings-sidebar"
:open="isSidebarOpen"
:header-height="$options.headerHeight"
@close="closeSidebar"
@close="unsetActiveId"
>
<template #header>{{ $options.listSettingsText }}</template>
<template v-if="isSidebarOpen">
......
......@@ -83,7 +83,7 @@ export default Vue.extend({
$('.js-issue-board-sidebar', this.$el).each((i, el) => {
$(el)
.data('glDropdown')
.data('deprecatedJQueryDropdown')
.clearMenu();
});
}
......@@ -95,7 +95,7 @@ export default Vue.extend({
},
},
created() {
// Get events from glDropdown
// Get events from deprecatedJQueryDropdown
eventHub.$on('sidebar.removeAssignee', this.removeAssignee);
eventHub.$on('sidebar.addAssignee', this.addAssignee);
eventHub.$on('sidebar.removeAllAssignees', this.removeAllAssignees);
......
......@@ -6,6 +6,7 @@ import axios from '~/lib/utils/axios_utils';
import { deprecatedCreateFlash as flash } from '~/flash';
import CreateLabelDropdown from '../../create_label';
import boardsStore from '../stores/boards_store';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
$(document)
.off('created.label')
......@@ -36,7 +37,7 @@ export default function initNewListDropdown() {
$dropdownToggle.data('projectPath'),
);
$dropdownToggle.glDropdown({
initDeprecatedJQueryDropdown($dropdownToggle, {
data(term, callback) {
axios
.get($dropdownToggle.attr('data-list-labels-path'))
......
......@@ -6,6 +6,7 @@ import { __ } from '~/locale';
import eventHub from '../eventhub';
import Api from '../../api';
import { featureAccessLevel } from '~/pages/projects/shared/permissions/constants';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default {
name: 'BoardProjectSelect',
......@@ -36,7 +37,7 @@ export default {
},
},
mounted() {
$(this.$refs.projectsDropdown).glDropdown({
initDeprecatedJQueryDropdown($(this.$refs.projectsDropdown), {
filterable: true,
filterRemote: true,
search: {
......
......@@ -15,6 +15,9 @@ export const ListType = {
export const inactiveId = 0;
export const ISSUABLE = 'issuable';
export const LIST = 'list';
export default {
BoardType,
ListType,
......
......@@ -5,7 +5,7 @@ import { __ } from '~/locale';
import { parseBoolean } from '~/lib/utils/common_utils';
import createDefaultClient from '~/lib/graphql';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { BoardType, ListType } from '~/boards/constants';
import { BoardType, ListType, inactiveId } from '~/boards/constants';
import * as types from './mutation_types';
import { formatListIssues, fullBoardId } from '../boards_util';
import boardStore from '~/boards/stores/boards_store';
......@@ -28,8 +28,12 @@ export default {
commit(types.SET_INITIAL_BOARD_DATA, data);
},
setActiveId({ commit }, id) {
commit(types.SET_ACTIVE_ID, id);
setActiveId({ commit }, { id, sidebarType }) {
commit(types.SET_ACTIVE_ID, { id, sidebarType });
},
unsetActiveId({ dispatch }) {
dispatch('setActiveId', { id: inactiveId, sidebarType: '' });
},
setFilters: ({ commit }, filters) => {
......
import { inactiveId } from '../constants';
export default {
getLabelToggleState: state => (state.isShowingLabels ? 'on' : 'off'),
isSidebarOpen: state => state.activeId !== inactiveId,
isSwimlanesOn: state => {
if (!gon?.features?.boardsWithSwimlanes) {
return false;
}
return state.isShowingEpicsSwimlanes;
},
};
......@@ -19,8 +19,9 @@ export default {
state.boardLists = lists;
},
[mutationTypes.SET_ACTIVE_ID](state, id) {
[mutationTypes.SET_ACTIVE_ID](state, { id, sidebarType }) {
state.activeId = id;
state.sidebarType = sidebarType;
},
[mutationTypes.SET_FILTERS](state, filterParams) {
......
......@@ -7,6 +7,7 @@ export default () => ({
showPromotion: false,
isShowingLabels: true,
activeId: inactiveId,
sidebarType: '',
boardLists: [],
issuesByListId: {},
isLoadingIssues: false,
......
......@@ -53,7 +53,7 @@ export default class VariableList {
},
environment_scope: {
// We can't use a `.js-` class here because
// gl_dropdown replaces the <input> and doesn't copy over the class
// deprecated_jquery_dropdown replaces the <input> and doesn't copy over the class
// See https://gitlab.com/gitlab-org/gitlab-foss/issues/42458
selector: `input[name="${this.formField}[variables_attributes][][environment_scope]"]`,
default: '*',
......
......@@ -5,6 +5,7 @@ import { __ } from './locale';
import axios from './lib/utils/axios_utils';
import { deprecatedCreateFlash as flash } from './flash';
import { capitalizeFirstCharacter } from './lib/utils/text_utility';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default function initCompareAutocomplete(limitTo = null, clickHandler = () => {}) {
$('.js-compare-dropdown').each(function() {
......@@ -13,7 +14,7 @@ export default function initCompareAutocomplete(limitTo = null, clickHandler = (
const $dropdownContainer = $dropdown.closest('.dropdown');
const $fieldInput = $(`input[name="${$dropdown.data('fieldName')}"]`, $dropdownContainer);
const $filterInput = $('input[type="search"]', $dropdownContainer);
$dropdown.glDropdown({
initDeprecatedJQueryDropdown($dropdown, {
data(term, callback) {
const params = {
ref: $dropdown.data('ref'),
......
import { escape } from 'lodash';
import '~/gl_dropdown';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default class CreateItemDropdown {
/**
......@@ -28,7 +28,7 @@ export default class CreateItemDropdown {
}
buildDropdown() {
this.$dropdown.glDropdown({
initDeprecatedJQueryDropdown(this.$dropdown, {
data: this.getData.bind(this),
filterable: true,
filterRemote: this.getDataRemote,
......@@ -67,12 +67,12 @@ export default class CreateItemDropdown {
e.preventDefault();
this.refreshData();
this.$dropdown.data('glDropdown').selectRowAtIndex();
this.$dropdown.data('deprecatedJQueryDropdown').selectRowAtIndex();
}
refreshData() {
// Refresh the dropdown's data, which ends up calling `getData`
this.$dropdown.data('glDropdown').remote.execute();
this.$dropdown.data('deprecatedJQueryDropdown').remote.execute();
}
getData(term, callback) {
......
......@@ -3,10 +3,10 @@
import $ from 'jquery';
import { escape } from 'lodash';
import fuzzaldrinPlus from 'fuzzaldrin-plus';
import axios from './lib/utils/axios_utils';
import axios from '../lib/utils/axios_utils';
import { visitUrl } from '~/lib/utils/url_utility';
import { isObject } from './lib/utils/type_utility';
import renderItem from './gl_dropdown/render';
import { isObject } from '~/lib/utils/type_utility';
import renderItem from './render';
const BLUR_KEYCODES = [27, 40];
......@@ -890,12 +890,11 @@ class GitLabDropdown {
}
}
// eslint-disable-next-line func-names
$.fn.glDropdown = function(opts) {
export default function initDeprecatedJQueryDropdown($el, opts) {
// eslint-disable-next-line func-names
return this.each(function() {
if (!$.data(this, 'glDropdown')) {
return $.data(this, 'glDropdown', new GitLabDropdown(this, opts));
return $el.each(function() {
if (!$.data(this, 'deprecatedJQueryDropdown')) {
$.data(this, 'deprecatedJQueryDropdown', new GitLabDropdown(this, opts));
}
});
};
}
......@@ -98,6 +98,7 @@ export default {
:loading="loading"
:icon="buttonIcon"
:disabled="isDeleting || !hasSelectedDesigns"
/>
><slot></slot
></gl-button>
</div>
</template>
......@@ -6,6 +6,7 @@ import { __ } from '~/locale';
import axios from './lib/utils/axios_utils';
import { timeFor, parsePikadayDate, pikadayToString } from './lib/utils/datetime_utility';
import boardsStore from './boards/stores/boards_store';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
class DueDateSelect {
constructor({ $dropdown, $loading } = {}) {
......@@ -35,7 +36,7 @@ class DueDateSelect {
}
initGlDropdown() {
this.$dropdown.glDropdown({
initDeprecatedJQueryDropdown(this.$dropdown, {
opened: () => {
const calendar = this.$datePicker.data('pikaday');
calendar.show();
......
import $ from 'jquery';
import { __ } from '~/locale';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default class TransferDropdown {
constructor() {
......@@ -16,7 +17,7 @@ export default class TransferDropdown {
buildDropdown() {
const extraOptions = [{ id: '-1', text: __('No parent group') }, { type: 'divider' }];
this.groupDropdown.glDropdown({
initDeprecatedJQueryDropdown(this.groupDropdown, {
selectable: true,
filterable: true,
toggleLabel: item => item.text,
......
import $ from 'jquery';
import { stickyMonitor } from './lib/utils/sticky';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default stickyTop => {
stickyMonitor(document.querySelector('.js-diff-files-changed'), stickyTop);
$('.js-diff-stats-dropdown').glDropdown({
initDeprecatedJQueryDropdown($('.js-diff-stats-dropdown'), {
filterable: true,
remoteFilter: false,
});
......
......@@ -42,6 +42,7 @@ export default {
v-if="adminState !== null"
:inherit-from-id="adminState.id"
:override="override"
:learn-more-path="propsSource.learnMorePath"
@change="setOverride"
/>
<active-checkbox v-if="propsSource.showActive" :key="`${currentKey}-active-checkbox`" />
......
<script>
import { GlNewDropdown, GlNewDropdownItem } from '@gitlab/ui';
import { GlNewDropdown, GlNewDropdownItem, GlLink } from '@gitlab/ui';
import { s__ } from '~/locale';
const dropdownOptions = [
......@@ -19,12 +19,18 @@ export default {
components: {
GlNewDropdown,
GlNewDropdownItem,
GlLink,
},
props: {
inheritFromId: {
type: Number,
required: true,
},
learnMorePath: {
type: String,
required: false,
default: null,
},
override: {
type: Boolean,
required: true,
......@@ -48,7 +54,12 @@ export default {
<div
class="gl-display-flex gl-justify-content-space-between gl-align-items-baseline gl-py-4 gl-mt-5 gl-mb-6 gl-border-t-1 gl-border-t-solid gl-border-b-1 gl-border-b-solid gl-border-gray-100"
>
<span>{{ s__('Integrations|Default settings are inherited from the instance level.') }}</span>
<span
>{{ s__('Integrations|Default settings are inherited from the instance level.') }}
<gl-link v-if="learnMorePath" :href="learnMorePath" target="_blank">{{
__('Learn more')
}}</gl-link>
</span>
<input name="service[inherit_from_id]" :value="override ? '' : inheritFromId" type="hidden" />
<gl-new-dropdown :text="selected.text">
<gl-new-dropdown-item
......
......@@ -19,6 +19,7 @@ function parseDatasetToProps(data) {
projectKey,
upgradePlanPath,
editProjectPath,
learnMorePath,
triggerEvents,
fields,
inheritFromId,
......@@ -51,6 +52,7 @@ function parseDatasetToProps(data) {
upgradePlanPath,
editProjectPath,
},
learnMorePath,
triggerEvents: JSON.parse(triggerEvents),
fields: JSON.parse(fields),
inheritFromId: parseInt(inheritFromId, 10),
......
import $ from 'jquery';
import { __ } from './locale';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default function issueStatusSelect() {
$('.js-issue-status').each((i, el) => {
const fieldName = $(el).data('fieldName');
return $(el).glDropdown({
initDeprecatedJQueryDropdown($(el), {
selectable: true,
fieldName,
toggleLabel(selected, element, instance) {
......
<script>
import { uniqueId } from 'lodash';
import { mapActions } from 'vuex';
import { GlDeprecatedButton, GlIcon } from '@gitlab/ui';
import { GlButton } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
export default {
name: 'ManualVariablesForm',
components: {
GlDeprecatedButton,
GlIcon,
GlButton,
},
props: {
action: {
......@@ -136,12 +135,12 @@ export default {
<div class="table-section section-10">
<div class="table-mobile-header" role="rowheader"></div>
<div class="table-mobile-content justify-content-end">
<gl-deprecated-button
class="btn-transparent btn-blank w-25"
<gl-button
category="tertiary"
icon="clear"
:aria-label="__('Delete variable')"
@click="deleteVariable(variable.id)"
>
<gl-icon name="clear" />
</gl-deprecated-button>
/>
</div>
</div>
</div>
......@@ -175,9 +174,14 @@ export default {
<p class="text-muted" v-html="helpText"></p>
</div>
<div class="d-flex justify-content-center">
<gl-deprecated-button variant="primary" @click="triggerManualJob(variables)">
<gl-button
variant="info"
category="primary"
:aria-label="__('Trigger manual job')"
@click="triggerManualJob(variables)"
>
{{ action.button_title }}
</gl-deprecated-button>
</gl-button>
</div>
</div>
</template>
......@@ -12,6 +12,7 @@ import { deprecatedCreateFlash as flash } from './flash';
import ModalStore from './boards/stores/modal_store';
import boardsStore from './boards/stores/boards_store';
import { isScopedLabel } from '~/lib/utils/common_utils';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default class LabelsSelect {
constructor(els, options = {}) {
......@@ -173,7 +174,7 @@ export default class LabelsSelect {
})
.catch(() => flash(__('Error saving label update.')));
};
$dropdown.glDropdown({
initDeprecatedJQueryDropdown($dropdown, {
showMenuAbove,
data(term, callback) {
const labelUrl = $dropdown.attr('data-labels');
......@@ -203,7 +204,7 @@ export default class LabelsSelect {
callback(data);
if (showMenuAbove) {
$dropdown.data('glDropdown').positionMenuAbove();
$dropdown.data('deprecatedJQueryDropdown').positionMenuAbove();
}
})
.catch(() => flash(__('Error fetching labels.')));
......@@ -348,7 +349,7 @@ export default class LabelsSelect {
} else {
if (!$dropdown.hasClass('js-filter-bulk-update')) {
saveLabelData();
$dropdown.data('glDropdown').clearMenu();
$dropdown.data('deprecatedJQueryDropdown').clearMenu();
}
}
}
......@@ -455,7 +456,7 @@ export default class LabelsSelect {
if ($dropdown.hasClass('js-issue-board-sidebar')) {
const previousSelection = $dropdown.attr('data-selected');
this.selected = previousSelection ? previousSelection.split(',') : [];
$dropdown.data('glDropdown').updateLabel();
$dropdown.data('deprecatedJQueryDropdown').updateLabel();
}
},
preserveContext: true,
......
......@@ -22,7 +22,6 @@ import { getLocationHash, visitUrl } from './lib/utils/url_utility';
// everything else
import loadAwardsHandler from './awards_handler';
import { deprecatedCreateFlash as Flash, removeFlashClickListener } from './flash';
import './gl_dropdown';
import initTodoToggle from './header';
import initImporterStatus from './importer_status';
import initLayoutNav from './layout_nav';
......
import $ from 'jquery';
import { disableButtonIfEmptyField } from '~/lib/utils/common_utils';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default class Members {
constructor() {
......@@ -37,7 +38,7 @@ export default class Members {
$('.js-member-permissions-dropdown').each((i, btn) => {
const $btn = $(btn);
$btn.glDropdown({
initDeprecatedJQueryDropdown($btn, {
selectable: true,
isSelectable: (selected, $el) => this.dropdownIsSelectable(selected, $el),
fieldName: $btn.data('fieldName'),
......
......@@ -5,7 +5,7 @@
import $ from 'jquery';
import { template, escape } from 'lodash';
import { __, sprintf } from '~/locale';
import '~/gl_dropdown';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
import Api from '~/api';
import axios from './lib/utils/axios_utils';
import { timeFor, parsePikadayDate, dateInWords } from './lib/utils/datetime_utility';
......@@ -69,7 +69,7 @@ export default class MilestoneSelect {
);
milestoneLinkNoneTemplate = `<span class="no-value">${__('None')}</span>`;
}
return $dropdown.glDropdown({
return initDeprecatedJQueryDropdown($dropdown, {
showMenuAbove,
data: (term, callback) => {
let contextId = $dropdown.get(0).dataset.projectId;
......@@ -138,7 +138,7 @@ export default class MilestoneSelect {
callback(extraOptions.concat(data));
if (showMenuAbove) {
$dropdown.data('glDropdown').positionMenuAbove();
$dropdown.data('deprecatedJQueryDropdown').positionMenuAbove();
}
$(`[data-milestone-id="${selectedMilestone}"] > a`).addClass('is-active');
});
......
import $ from 'jquery';
import '~/gl_dropdown';
import Api from './api';
import { mergeUrlParams } from './lib/utils/url_utility';
import { parseBoolean } from '~/lib/utils/common_utils';
import { __ } from './locale';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default class NamespaceSelect {
constructor(opts) {
const isFilter = parseBoolean(opts.dropdown.dataset.isFilter);
const fieldName = opts.dropdown.dataset.fieldName || 'namespace_id';
$(opts.dropdown).glDropdown({
initDeprecatedJQueryDropdown($(opts.dropdown), {
filterable: true,
selectable: true,
filterRemote: true,
......
/* eslint-disable class-methods-use-this, no-unneeded-ternary */
import $ from 'jquery';
import '~/gl_dropdown';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
import { visitUrl } from '~/lib/utils/url_utility';
import UsersSelect from '~/users_select';
import { isMetaClick } from '~/lib/utils/common_utils';
......@@ -50,7 +50,7 @@ export default class Todos {
}
initFilterDropdown($dropdown, fieldName, searchFields) {
$dropdown.glDropdown({
initDeprecatedJQueryDropdown($dropdown, {
fieldName,
selectable: true,
filterable: searchFields ? true : false,
......
import initSidebarBundle from '~/sidebar/sidebar_bundle';
import initRelatedIssues from '~/related_issues';
import initShow from '../show';
document.addEventListener('DOMContentLoaded', () => {
......@@ -6,4 +7,5 @@ document.addEventListener('DOMContentLoaded', () => {
if (gon.features && !gon.features.vueIssuableSidebar) {
initSidebarBundle();
}
initRelatedIssues();
});
import $ from 'jquery';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default () => {
const $targetProjectDropdown = $('.js-target-project');
$targetProjectDropdown.glDropdown({
initDeprecatedJQueryDropdown($targetProjectDropdown, {
selectable: true,
fieldName: $targetProjectDropdown.data('fieldName'),
filterable: true,
......@@ -16,7 +17,7 @@ export default () => {
$('.mr_target_commit').empty();
const $targetBranchDropdown = $('.js-target-branch');
$targetBranchDropdown.data('refsUrl', $el.data('refsUrl'));
$targetBranchDropdown.data('glDropdown').clearMenu();
$targetBranchDropdown.data('deprecatedJQueryDropdown').clearMenu();
},
});
};
import $ from 'jquery';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default class TargetBranchDropdown {
constructor() {
......@@ -10,7 +11,7 @@ export default class TargetBranchDropdown {
}
initDropdown() {
this.$dropdown.glDropdown({
initDeprecatedJQueryDropdown(this.$dropdown, {
data: this.formatBranchesList(),
filterable: true,
selectable: true,
......
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
const defaultTimezone = { name: 'UTC', offset: 0 };
const defaults = {
$inputEl: null,
......@@ -42,7 +44,7 @@ export default class TimezoneDropdown {
}
initDropdown() {
this.$dropdown.glDropdown({
initDeprecatedJQueryDropdown(this.$dropdown, {
data: this.timezoneData,
filterable: true,
selectable: true,
......
......@@ -8,6 +8,7 @@ import { serializeForm } from '~/lib/utils/forms';
import axios from '~/lib/utils/axios_utils';
import { deprecatedCreateFlash as flash } from '~/flash';
import projectSelect from '../../project_select';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default class Project {
constructor() {
......@@ -104,7 +105,7 @@ export default class Project {
const action = $form.attr('action');
const linkTarget = mergeUrlParams(serializeForm($form[0]), action);
return $dropdown.glDropdown({
return initDeprecatedJQueryDropdown($dropdown, {
data(term, callback) {
axios
.get($dropdown.data('refsUrl'), {
......
import $ from 'jquery';
import '~/gl_dropdown';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
import { deprecatedCreateFlash as Flash } from '~/flash';
import Api from '~/api';
import { __ } from '~/locale';
......@@ -20,7 +20,7 @@ export default class Search {
this.eventListeners();
refreshCounts();
$groupDropdown.glDropdown({
initDeprecatedJQueryDropdown($groupDropdown, {
selectable: true,
filterable: true,
filterRemote: true,
......@@ -46,7 +46,7 @@ export default class Search {
clicked: () => Search.submitSearch(),
});
$projectDropdown.glDropdown({
initDeprecatedJQueryDropdown($projectDropdown, {
selectable: true,
filterable: true,
filterRemote: true,
......
......@@ -4,6 +4,7 @@ import axios from '~/lib/utils/axios_utils';
import { deprecatedCreateFlash as Flash } from '~/flash';
import { n__, s__, __ } from '~/locale';
import { LEVEL_TYPES, LEVEL_ID_PROP, ACCESS_LEVEL_NONE } from './constants';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default class AccessDropdown {
constructor(options) {
......@@ -29,7 +30,7 @@ export default class AccessDropdown {
initDropdown() {
const { onSelect, onHide } = this.options;
this.$dropdown.glDropdown({
initDeprecatedJQueryDropdown(this.$dropdown, {
data: this.getData.bind(this),
selectable: true,
filterable: true,
......
import { __ } from '~/locale';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default class ProtectedTagAccessDropdown {
constructor(options) {
......@@ -8,7 +9,7 @@ export default class ProtectedTagAccessDropdown {
initDropdown() {
const { onSelect } = this.options;
this.options.$dropdown.glDropdown({
initDeprecatedJQueryDropdown(this.options.$dropdown, {
data: this.options.data,
selectable: true,
inputId: this.options.$dropdown.data('inputId'),
......
......@@ -23,7 +23,7 @@ export default class ProtectedTagCreate {
});
// Select default
$allowedToCreateDropdown.data('glDropdown').selectRowAtIndex(0);
$allowedToCreateDropdown.data('deprecatedJQueryDropdown').selectRowAtIndex(0);
// Protected tag dropdown
this.createItemDropdown = new CreateItemDropdown({
......
import $ from 'jquery';
import '~/gl_dropdown';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
class RefSelectDropdown {
constructor($dropdownButton, availableRefs) {
const availableRefsValue =
availableRefs || JSON.parse(document.getElementById('availableRefs').innerHTML);
$dropdownButton.glDropdown({
initDeprecatedJQueryDropdown($dropdownButton, {
data: availableRefsValue,
filterable: true,
filterByText: true,
......
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import Sortable from 'sortablejs';
import sortableConfig from 'ee/sortable/sortable_config';
import sortableConfig from 'ee_else_ce/sortable/sortable_config';
import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue';
import tooltip from '~/vue_shared/directives/tooltip';
......
......@@ -17,6 +17,9 @@ export default function initRelatedIssues() {
endpoint: relatedIssuesRootElement.dataset.endpoint,
canAdmin: parseBoolean(relatedIssuesRootElement.dataset.canAddRelatedIssues),
helpPath: relatedIssuesRootElement.dataset.helpPath,
showCategorizedIssues: parseBoolean(
relatedIssuesRootElement.dataset.showCategorizedIssues,
),
},
}),
});
......
......@@ -139,7 +139,7 @@ export default {
class="form-control"
/>
</gl-form-group>
<gl-form-group class="w-50" @keydown.enter.prevent.capture>
<gl-form-group class="w-50" data-testid="milestones-field" @keydown.enter.prevent.capture>
<label>{{ __('Milestones') }}</label>
<div class="d-flex flex-column col-md-6 col-sm-10 pl-0">
<milestone-combobox
......
......@@ -13,6 +13,7 @@ import {
spriteIcon,
} from './lib/utils/common_utils';
import Tracking from '~/tracking';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
/**
* Search input in top navigation bar.
......@@ -119,7 +120,7 @@ export class SearchAutocomplete {
}
createAutocomplete() {
return this.searchInput.glDropdown({
return initDeprecatedJQueryDropdown(this.searchInput, {
filterInputBlur: false,
filterable: true,
filterRemote: true,
......@@ -145,10 +146,10 @@ export class SearchAutocomplete {
if (!term) {
const contents = this.getCategoryContents();
if (contents) {
const glDropdownInstance = this.searchInput.data('glDropdown');
const deprecatedJQueryDropdownInstance = this.searchInput.data('deprecatedJQueryDropdown');
if (glDropdownInstance) {
glDropdownInstance.filter.options.callback(contents);
if (deprecatedJQueryDropdownInstance) {
deprecatedJQueryDropdownInstance.filter.options.callback(contents);
}
this.enableAutocomplete();
}
......@@ -463,7 +464,7 @@ export class SearchAutocomplete {
}
highlightFirstRow() {
this.searchInput.data('glDropdown').highlightRowAtIndex(null, 0);
this.searchInput.data('deprecatedJQueryDropdown').highlightRowAtIndex(null, 0);
}
getAvatar(item) {
......
......@@ -62,7 +62,7 @@ export default {
this.addAssignee = this.store.addAssignee.bind(this.store);
this.removeAllAssignees = this.store.removeAllAssignees.bind(this.store);
// Get events from glDropdown
// Get events from deprecatedJQueryDropdown
eventHub.$on('sidebar.removeAssignee', this.removeAssignee);
eventHub.$on('sidebar.addAssignee', this.addAssignee);
eventHub.$on('sidebar.removeAllAssignees', this.removeAllAssignees);
......
import $ from 'jquery';
import '~/gl_dropdown';
import { escape } from 'lodash';
import { __ } from '~/locale';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
function isValidProjectId(id) {
return id > 0;
......@@ -27,7 +27,7 @@ class SidebarMoveIssue {
}
initDropdown() {
this.$dropdownToggle.glDropdown({
initDeprecatedJQueryDropdown(this.$dropdownToggle, {
search: {
fields: ['name_with_namespace'],
},
......
import $ from 'jquery';
import { __ } from './locale';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default function subscriptionSelect() {
$('.js-subscription-event').each((i, element) => {
const fieldName = $(element).data('fieldName');
return $(element).glDropdown({
return initDeprecatedJQueryDropdown($(element), {
selectable: true,
fieldName,
toggleLabel(selected, el, instance) {
......
......@@ -33,7 +33,7 @@ export default class IssuableTemplateSelector extends TemplateSelector {
this.templateWarningEl.find('.js-close-btn').on('click', () => {
// Explicitly check against 0 value
if (this.previousSelectedIndex !== undefined) {
this.dropdown.data('glDropdown').selectRowAtIndex(this.previousSelectedIndex);
this.dropdown.data('deprecatedJQueryDropdown').selectRowAtIndex(this.previousSelectedIndex);
} else {
this.reset();
}
......@@ -61,7 +61,7 @@ export default class IssuableTemplateSelector extends TemplateSelector {
}
setSelectedIndex() {
this.previousSelectedIndex = this.dropdown.data('glDropdown').selectedIndex;
this.previousSelectedIndex = this.dropdown.data('deprecatedJQueryDropdown').selectedIndex;
}
onDropdownClicked(query) {
......
import $ from 'jquery';
import Api from './api';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
export default () => {
$('#js-project-dropdown').glDropdown({
initDeprecatedJQueryDropdown($('#js-project-dropdown'), {
data: (term, callback) => {
Api.projects(
term,
......
......@@ -13,6 +13,7 @@ import { s__, __, sprintf } from '../locale';
import ModalStore from '../boards/stores/modal_store';
import { parseBoolean } from '../lib/utils/common_utils';
import { getAjaxUsersSelectOptions, getAjaxUsersSelectParams } from './utils';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
// TODO: remove eventHub hack after code splitting refactor
window.emitSidebarEvent = window.emitSidebarEvent || $.noop;
......@@ -233,14 +234,14 @@ function UsersSelect(currentUser, els, options = {}) {
closingTag: '</a>',
})}</span> <% } %>`,
);
return $dropdown.glDropdown({
return initDeprecatedJQueryDropdown($dropdown, {
showMenuAbove,
data(term, callback) {
return userSelect.users(term, options, users => {
// GitLabDropdownFilter returns this.instance
// GitLabDropdownRemote returns this.options.instance
const glDropdown = this.instance || this.options.instance;
glDropdown.options.processData(term, users, callback);
const deprecatedJQueryDropdown = this.instance || this.options.instance;
deprecatedJQueryDropdown.options.processData(term, users, callback);
});
},
processData(term, data, callback) {
......@@ -349,7 +350,7 @@ function UsersSelect(currentUser, els, options = {}) {
callback(users);
if (showMenuAbove) {
$dropdown.data('glDropdown').positionMenuAbove();
$dropdown.data('deprecatedJQueryDropdown').positionMenuAbove();
}
},
filterable: true,
......@@ -359,13 +360,13 @@ function UsersSelect(currentUser, els, options = {}) {
},
selectable: true,
fieldName: $dropdown.data('fieldName'),
toggleLabel(selected, el, glDropdown) {
const inputValue = glDropdown.filterInput.val();
toggleLabel(selected, el, deprecatedJQueryDropdown) {
const inputValue = deprecatedJQueryDropdown.filterInput.val();
if (this.multiSelect && inputValue === '') {
// Remove non-users from the fullData array
const users = glDropdown.filteredFullData();
const callback = glDropdown.parseData.bind(glDropdown);
const users = deprecatedJQueryDropdown.filteredFullData();
const callback = deprecatedJQueryDropdown.parseData.bind(deprecatedJQueryDropdown);
// Update the data model
this.processData(inputValue, users, callback);
......
......@@ -873,7 +873,6 @@ $mr-widget-min-height: 69px;
.merge-request-tabs-container,
.epic-tabs-container {
flex-direction: column-reverse;
padding-top: $gl-padding-8;
}
}
......@@ -883,7 +882,6 @@ $mr-widget-min-height: 69px;
.epic-tabs-container {
flex-direction: column-reverse;
align-items: flex-start;
padding-top: $gl-padding-8;
}
}
}
......
......@@ -105,7 +105,7 @@ input[type='checkbox']:hover {
}
.dropdown-header {
// Necessary because glDropdown doesn't support a second style of headers
// Necessary because deprecatedJQueryDropdown doesn't support a second style of headers
font-weight: $gl-font-weight-bold;
color: $gl-text-color;
font-size: $gl-font-size;
......
......@@ -3,7 +3,7 @@
module Types
module Ci
class PipelineConfigSourceEnum < BaseEnum
::Ci::PipelineEnums.config_sources.keys.each do |state_symbol|
::Enums::Ci::Pipeline.config_sources.keys.each do |state_symbol|
value state_symbol.to_s.upcase, value: state_symbol.to_s
end
end
......
......@@ -26,7 +26,7 @@ module Types
description: 'Detailed status of the pipeline',
resolve: -> (obj, _args, ctx) { obj.detailed_status(ctx[:current_user]) }
field :config_source, PipelineConfigSourceEnum, null: true,
description: "Config source of the pipeline (#{::Ci::PipelineEnums.config_sources.keys.join(', ').upcase})"
description: "Config source of the pipeline (#{::Enums::Ci::Pipeline.config_sources.keys.join(', ').upcase})"
field :duration, GraphQL::INT_TYPE, null: true,
description: 'Duration of the pipeline in seconds'
field :coverage, GraphQL::FLOAT_TYPE, null: true,
......
......@@ -250,15 +250,16 @@ module SearchHelper
# Sanitize a HTML field for search display. Most tags are stripped out and the
# maximum length is set to 200 characters.
def search_md_sanitize(object, field)
html = markdown_field(object, field)
html = Truncato.truncate(
html,
def search_md_sanitize(source)
source = Truncato.truncate(
source,
count_tags: false,
count_tail: false,
max_length: 200
)
html = markdown(source)
# Truncato's filtered_tags and filtered_attributes are not quite the same
sanitize(html, tags: %w(a p ol ul li pre code))
end
......
......@@ -92,6 +92,7 @@ module ServicesHelper
commit_events: integration.commit_events.to_s,
enable_comments: integration.comment_on_event_enabled.to_s,
comment_detail: integration.comment_detail,
learn_more_path: integrations_help_page_path,
trigger_events: trigger_events_for_service(integration),
fields: fields_for_service(integration),
inherit_from_id: integration.inherit_from_id
......@@ -106,10 +107,18 @@ module ServicesHelper
ServiceFieldSerializer.new(service: integration).represent(integration.global_fields).to_json
end
def integrations_help_page_path
help_page_path('user/admin_area/settings/project_integration_management')
end
def project_jira_issues_integration?
false
end
def group_level_integrations?
@group.present? && Feature.enabled?(:group_level_integrations, @group)
end
extend self
end
......
......@@ -5,7 +5,7 @@ class AuditEvent < ApplicationRecord
include IgnorableColumns
include BulkInsertSafe
PARALLEL_PERSISTENCE_COLUMNS = [:author_name, :entity_path, :target_details].freeze
PARALLEL_PERSISTENCE_COLUMNS = [:author_name, :entity_path, :target_details, :target_type].freeze
ignore_column :type, remove_with: '13.6', remove_after: '2020-11-22'
ignore_column :updated_at, remove_with: '13.4', remove_after: '2020-09-22'
......
......@@ -104,15 +104,15 @@ module Ci
after_create :keep_around_commits, unless: :importing?
# We use `Ci::PipelineEnums.sources` here so that EE can more easily extend
# We use `Enums::Ci::Pipeline.sources` here so that EE can more easily extend
# this `Hash` with new values.
enum_with_nil source: ::Ci::PipelineEnums.sources
enum_with_nil source: Enums::Ci::Pipeline.sources
enum_with_nil config_source: ::Ci::PipelineEnums.config_sources
enum_with_nil config_source: Enums::Ci::Pipeline.config_sources
# We use `Ci::PipelineEnums.failure_reasons` here so that EE can more easily
# We use `Enums::Ci::Pipeline.failure_reasons` here so that EE can more easily
# extend this `Hash` with new values.
enum failure_reason: ::Ci::PipelineEnums.failure_reasons
enum failure_reason: Enums::Ci::Pipeline.failure_reasons
enum locked: { unlocked: 0, artifacts_locked: 1 }
......@@ -260,7 +260,7 @@ module Ci
scope :internal, -> { where(source: internal_sources) }
scope :no_child, -> { where.not(source: :parent_pipeline) }
scope :ci_sources, -> { where(config_source: ::Ci::PipelineEnums.ci_config_sources_values) }
scope :ci_sources, -> { where(config_source: Enums::Ci::Pipeline.ci_config_sources_values) }
scope :for_user, -> (user) { where(user: user) }
scope :for_sha, -> (sha) { where(sha: sha) }
scope :for_source_sha, -> (source_sha) { where(source_sha: source_sha) }
......@@ -1012,7 +1012,7 @@ module Ci
end
def cacheable?
Ci::PipelineEnums.ci_config_sources.key?(config_source.to_sym)
Enums::Ci::Pipeline.ci_config_sources.key?(config_source.to_sym)
end
def source_ref_path
......
# frozen_string_literal: true
module Ci
module PipelineEnums
# Returns the `Hash` to use for creating the `failure_reason` enum for
# `Ci::Pipeline`.
def self.failure_reasons
{
unknown_failure: 0,
config_error: 1,
external_validation_failure: 2
}
end
# Returns the `Hash` to use for creating the `sources` enum for
# `Ci::Pipeline`.
def self.sources
{
unknown: nil,
push: 1,
web: 2,
trigger: 3,
schedule: 4,
api: 5,
external: 6,
# TODO: Rename `pipeline` to `cross_project_pipeline` in 13.0
# https://gitlab.com/gitlab-org/gitlab/issues/195991
pipeline: 7,
chat: 8,
webide: 9,
merge_request_event: 10,
external_pull_request_event: 11,
parent_pipeline: 12,
ondemand_dast_scan: 13
}
end
# Returns the `Hash` to use for creating the `config_sources` enum for
# `Ci::Pipeline`.
def self.config_sources
{
unknown_source: nil,
repository_source: 1,
auto_devops_source: 2,
webide_source: 3,
remote_source: 4,
external_project_source: 5,
bridge_source: 6,
parameter_source: 7
}
end
def self.ci_config_sources
config_sources.slice(
:unknown_source,
:repository_source,
:auto_devops_source,
:remote_source,
:external_project_source
)
end
def self.ci_config_sources_values
ci_config_sources.values
end
def self.non_ci_config_source_values
config_sources.values - ci_config_sources.values
end
end
end
Ci::PipelineEnums.prepend_if_ee('EE::Ci::PipelineEnums')
......@@ -106,7 +106,9 @@ module Clusters
proxy_url = kube_client.proxy_url('service', service_name, service_port, Gitlab::Kubernetes::Helm::NAMESPACE)
# ensures headers containing auth data are appended to original k8s client options
options = kube_client.rest_client.options.merge(headers: kube_client.headers)
options = kube_client.rest_client.options
.merge(prometheus_client_default_options)
.merge(headers: kube_client.headers)
Gitlab::PrometheusClient.new(proxy_url, options)
rescue Kubeclient::HttpError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::ENETUNREACH
# If users have mistakenly set parameters or removed the depended clusters,
......
......@@ -3,7 +3,7 @@
module Clusters
module Applications
class Runner < ApplicationRecord
VERSION = '0.19.2'
VERSION = '0.20.0'
self.table_name = 'clusters_applications_runners'
......
......@@ -77,9 +77,9 @@ class CommitStatus < ApplicationRecord
merge(or_conditions)
end
# We use `CommitStatusEnums.failure_reasons` here so that EE can more easily
# We use `Enums::CommitStatus.failure_reasons` here so that EE can more easily
# extend this `Hash` with new values.
enum_with_nil failure_reason: ::CommitStatusEnums.failure_reasons
enum_with_nil failure_reason: Enums::CommitStatus.failure_reasons
##
# We still create some CommitStatuses outside of CreatePipelineService.
......
# frozen_string_literal: true
module CommitStatusEnums
# Returns the Hash to use for creating the `failure_reason` enum for
# `CommitStatus`.
def self.failure_reasons
{
unknown_failure: nil,
script_failure: 1,
api_failure: 2,
stuck_or_timeout_failure: 3,
runner_system_failure: 4,
missing_dependency_failure: 5,
runner_unsupported: 6,
stale_schedule: 7,
job_execution_timeout: 8,
archived_failure: 9,
unmet_prerequisites: 10,
scheduler_failure: 11,
data_integrity_failure: 12,
forward_deployment_failure: 13,
insufficient_bridge_permissions: 1_001,
downstream_bridge_project_not_found: 1_002,
invalid_bridge_trigger: 1_003,
bridge_pipeline_is_child_pipeline: 1_006,
downstream_pipeline_creation_failed: 1_007,
secrets_provider_not_found: 1_008
}
end
end
CommitStatusEnums.prepend_if_ee('EE::CommitStatusEnums')
# frozen_string_literal: true
module Enums
module Ci
module Pipeline
# Returns the `Hash` to use for creating the `failure_reason` enum for
# `Ci::Pipeline`.
def self.failure_reasons
{
unknown_failure: 0,
config_error: 1,
external_validation_failure: 2
}
end
# Returns the `Hash` to use for creating the `sources` enum for
# `Ci::Pipeline`.
def self.sources
{
unknown: nil,
push: 1,
web: 2,
trigger: 3,
schedule: 4,
api: 5,
external: 6,
# TODO: Rename `pipeline` to `cross_project_pipeline` in 13.0
# https://gitlab.com/gitlab-org/gitlab/issues/195991
pipeline: 7,
chat: 8,
webide: 9,
merge_request_event: 10,
external_pull_request_event: 11,
parent_pipeline: 12,
ondemand_dast_scan: 13
}
end
# Returns the `Hash` to use for creating the `config_sources` enum for
# `Ci::Pipeline`.
def self.config_sources
{
unknown_source: nil,
repository_source: 1,
auto_devops_source: 2,
webide_source: 3,
remote_source: 4,
external_project_source: 5,
bridge_source: 6,
parameter_source: 7
}
end
def self.ci_config_sources
config_sources.slice(
:unknown_source,
:repository_source,
:auto_devops_source,
:remote_source,
:external_project_source
)
end
def self.ci_config_sources_values
ci_config_sources.values
end
def self.non_ci_config_source_values
config_sources.values - ci_config_sources.values
end
end
end
end
Enums::Ci::Pipeline.prepend_if_ee('EE::Enums::Ci::Pipeline')
# frozen_string_literal: true
module Enums
module CommitStatus
# Returns the Hash to use for creating the `failure_reason` enum for
# `CommitStatus`.
def self.failure_reasons
{
unknown_failure: nil,
script_failure: 1,
api_failure: 2,
stuck_or_timeout_failure: 3,
runner_system_failure: 4,
missing_dependency_failure: 5,
runner_unsupported: 6,
stale_schedule: 7,
job_execution_timeout: 8,
archived_failure: 9,
unmet_prerequisites: 10,
scheduler_failure: 11,
data_integrity_failure: 12,
forward_deployment_failure: 13,
insufficient_bridge_permissions: 1_001,
downstream_bridge_project_not_found: 1_002,
invalid_bridge_trigger: 1_003,
bridge_pipeline_is_child_pipeline: 1_006,
downstream_pipeline_creation_failed: 1_007,
secrets_provider_not_found: 1_008
}
end
end
end
Enums::CommitStatus.prepend_if_ee('EE::Enums::CommitStatus')
# frozen_string_literal: true
module InternalIdEnums
def self.usage_resources
# when adding new resource, make sure it doesn't conflict with EE usage_resources
{
module Enums
module InternalId
def self.usage_resources
# when adding new resource, make sure it doesn't conflict with EE usage_resources
{
issues: 0,
merge_requests: 1,
deployments: 2,
......@@ -14,8 +15,9 @@ module InternalIdEnums
operations_user_lists: 7,
alert_management_alerts: 8,
sprints: 9 # iterations
}
}
end
end
end
InternalIdEnums.prepend_if_ee('EE::InternalIdEnums')
Enums::InternalId.prepend_if_ee('EE::Enums::InternalId')
# frozen_string_literal: true
module Enums
module PrometheusMetric
def self.groups
{
# built-in groups
nginx_ingress_vts: -1,
ha_proxy: -2,
aws_elb: -3,
nginx: -4,
kubernetes: -5,
nginx_ingress: -6,
cluster_health: -100
}.merge(custom_groups).freeze
end
# custom/user groups
def self.custom_groups
{
business: 0,
response: 1,
system: 2
}.freeze
end
def self.group_details
{
# built-in groups
nginx_ingress_vts: {
group_title: _('Response metrics (NGINX Ingress VTS)'),
required_metrics: %w(nginx_upstream_responses_total nginx_upstream_response_msecs_avg),
priority: 10
}.freeze,
nginx_ingress: {
group_title: _('Response metrics (NGINX Ingress)'),
required_metrics: %w(nginx_ingress_controller_requests nginx_ingress_controller_ingress_upstream_latency_seconds_sum),
priority: 10
}.freeze,
ha_proxy: {
group_title: _('Response metrics (HA Proxy)'),
required_metrics: %w(haproxy_frontend_http_requests_total haproxy_frontend_http_responses_total),
priority: 10
}.freeze,
aws_elb: {
group_title: _('Response metrics (AWS ELB)'),
required_metrics: %w(aws_elb_request_count_sum aws_elb_latency_average aws_elb_httpcode_backend_5_xx_sum),
priority: 10
}.freeze,
nginx: {
group_title: _('Response metrics (NGINX)'),
required_metrics: %w(nginx_server_requests nginx_server_requestMsec),
priority: 10
}.freeze,
kubernetes: {
group_title: _('System metrics (Kubernetes)'),
required_metrics: %w(container_memory_usage_bytes container_cpu_usage_seconds_total),
priority: 5
}.freeze,
cluster_health: {
group_title: _('Cluster Health'),
required_metrics: %w(container_memory_usage_bytes container_cpu_usage_seconds_total),
priority: 10
}.freeze
}.merge(custom_group_details).freeze
end
# custom/user groups
def self.custom_group_details
{
business: {
group_title: _('Business metrics (Custom)'),
priority: 0
}.freeze,
response: {
group_title: _('Response metrics (Custom)'),
priority: -5
}.freeze,
system: {
group_title: _('System metrics (Custom)'),
priority: -10
}.freeze
}.freeze
end
end
end
# frozen_string_literal: true
module Enums
module UserCallout
# Returns the `Hash` to use for the `feature_name` enum in the `UserCallout`
# model.
#
# This method is separate from the `UserCallout` model so that it can be
# extended by EE.
#
# If you are going to add new items to this hash, check that you're not going
# to conflict with EE-only values: https://gitlab.com/gitlab-org/gitlab/blob/master/ee/app/models/concerns/ee/enums/user_callout.rb
def self.feature_names
{
gke_cluster_integration: 1,
gcp_signup_offer: 2,
cluster_security_warning: 3,
suggest_popover_dismissed: 9,
tabs_position_highlight: 10,
webhooks_moved: 13,
service_templates_deprecated: 14,
admin_integrations_moved: 15,
personal_access_token_expiry: 21, # EE-only
suggest_pipeline: 22,
customize_homepage: 23
}
end
end
end
Enums::UserCallout.prepend_if_ee('EE::Enums::UserCallout')
......@@ -3,6 +3,11 @@
module PrometheusAdapter
extend ActiveSupport::Concern
# We should choose more conservative timeouts, but some queries we run are now busting our
# default timeouts, which are stricter. We should make those queries faster instead.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/232786
DEFAULT_PROMETHEUS_REQUEST_TIMEOUT_SEC = 60.seconds
included do
include ReactiveCaching
......@@ -15,6 +20,12 @@ module PrometheusAdapter
raise NotImplementedError
end
def prometheus_client_default_options
{
timeout: DEFAULT_PROMETHEUS_REQUEST_TIMEOUT_SEC
}
end
# This is a light-weight check if a prometheus client is properly configured.
def configured?
raise NotImplemented
......
......@@ -21,7 +21,7 @@ class InternalId < ApplicationRecord
belongs_to :project
belongs_to :namespace
enum usage: ::InternalIdEnums.usage_resources
enum usage: Enums::InternalId.usage_resources
validates :usage, presence: true
......
......@@ -37,6 +37,7 @@ class Packages::Package < ApplicationRecord
validates :name, format: { with: Gitlab::Regex.conan_recipe_component_regex }, if: :conan?
validates :version, format: { with: Gitlab::Regex.conan_recipe_component_regex }, if: :conan?
validates :version, format: { with: Gitlab::Regex.maven_version_regex }, if: -> { version? && maven? }
validates :version, format: { with: Gitlab::Regex.pypi_version_regex }, if: :pypi?
enum package_type: { maven: 1, npm: 2, conan: 3, nuget: 4, pypi: 5, composer: 6 }
......
......@@ -97,13 +97,9 @@ class PrometheusService < MonitoringService
def prometheus_client
return unless should_return_client?
options = {
allow_local_requests: allow_local_api_url?,
# We should choose more conservative timeouts, but some queries we run are now busting our
# default timeouts, which are stricter. We should make those queries faster instead.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/233109
timeout: 60
}
options = prometheus_client_default_options.merge(
allow_local_requests: allow_local_api_url?
)
if behind_iap?
# Adds the Authorization header
......
......@@ -80,11 +80,15 @@ class ProjectStatistics < ApplicationRecord
end
def update_storage_size
storage_size = repository_size + wiki_size + lfs_objects_size + build_artifacts_size + packages_size + pipeline_artifacts_size
storage_size = repository_size + wiki_size + lfs_objects_size + build_artifacts_size + packages_size
# The `snippets_size` column was added on 20200622095419 but db/post_migrate/20190527194900_schedule_calculate_wiki_sizes.rb
# might try to update project statistics before the `snippets_size` column has been created.
storage_size += snippets_size if self.class.column_names.include?('snippets_size')
# The `pipeline_artifacts_size` column was added on 20200817142800 but db/post_migrate/20190527194900_schedule_calculate_wiki_sizes.rb
# might try to update project statistics before the `pipeline_artifacts_size` column has been created.
storage_size += pipeline_artifacts_size if self.class.column_names.include?('pipeline_artifacts_size')
self.storage_size = storage_size
end
......
......@@ -4,7 +4,7 @@ class PrometheusMetric < ApplicationRecord
belongs_to :project, validate: true, inverse_of: :prometheus_metrics
has_many :prometheus_alerts, inverse_of: :prometheus_metric
enum group: PrometheusMetricEnums.groups
enum group: Enums::PrometheusMetric.groups
validates :title, presence: true
validates :query, presence: true
......@@ -72,6 +72,6 @@ class PrometheusMetric < ApplicationRecord
private
def group_details(group)
PrometheusMetricEnums.group_details.fetch(group.to_sym)
Enums::PrometheusMetric.group_details.fetch(group.to_sym)
end
end
# frozen_string_literal: true
module PrometheusMetricEnums
def self.groups
{
# built-in groups
nginx_ingress_vts: -1,
ha_proxy: -2,
aws_elb: -3,
nginx: -4,
kubernetes: -5,
nginx_ingress: -6,
cluster_health: -100
}.merge(custom_groups).freeze
end
# custom/user groups
def self.custom_groups
{
business: 0,
response: 1,
system: 2
}.freeze
end
def self.group_details
{
# built-in groups
nginx_ingress_vts: {
group_title: _('Response metrics (NGINX Ingress VTS)'),
required_metrics: %w(nginx_upstream_responses_total nginx_upstream_response_msecs_avg),
priority: 10
}.freeze,
nginx_ingress: {
group_title: _('Response metrics (NGINX Ingress)'),
required_metrics: %w(nginx_ingress_controller_requests nginx_ingress_controller_ingress_upstream_latency_seconds_sum),
priority: 10
}.freeze,
ha_proxy: {
group_title: _('Response metrics (HA Proxy)'),
required_metrics: %w(haproxy_frontend_http_requests_total haproxy_frontend_http_responses_total),
priority: 10
}.freeze,
aws_elb: {
group_title: _('Response metrics (AWS ELB)'),
required_metrics: %w(aws_elb_request_count_sum aws_elb_latency_average aws_elb_httpcode_backend_5_xx_sum),
priority: 10
}.freeze,
nginx: {
group_title: _('Response metrics (NGINX)'),
required_metrics: %w(nginx_server_requests nginx_server_requestMsec),
priority: 10
}.freeze,
kubernetes: {
group_title: _('System metrics (Kubernetes)'),
required_metrics: %w(container_memory_usage_bytes container_cpu_usage_seconds_total),
priority: 5
}.freeze,
cluster_health: {
group_title: _('Cluster Health'),
required_metrics: %w(container_memory_usage_bytes container_cpu_usage_seconds_total),
priority: 10
}.freeze
}.merge(custom_group_details).freeze
end
# custom/user groups
def self.custom_group_details
{
business: {
group_title: _('Business metrics (Custom)'),
priority: 0
}.freeze,
response: {
group_title: _('Response metrics (Custom)'),
priority: -5
}.freeze,
system: {
group_title: _('System metrics (Custom)'),
priority: -10
}.freeze
}.freeze
end
end
......@@ -9,7 +9,6 @@ class Service < ApplicationRecord
include DataFields
include IgnorableColumns
ignore_columns %i[title description], remove_with: '13.4', remove_after: '2020-09-22'
ignore_columns %i[default], remove_with: '13.5', remove_after: '2020-10-22'
SERVICE_NAMES = %w[
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment