Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
34ad6d995b
commit
888264e6b7
|
@ -155,7 +155,7 @@ export default {
|
|||
<span v-if="item.active" data-testid="integration-activated-status">
|
||||
<gl-icon
|
||||
v-gl-tooltip
|
||||
name="check-circle-filled"
|
||||
name="check"
|
||||
:size="16"
|
||||
class="gl-text-green-500 gl-hover-cursor-pointer gl-mr-3"
|
||||
:title="$options.i18n.status.enabled.tooltip"
|
||||
|
|
|
@ -167,7 +167,7 @@ export default {
|
|||
if (testAfterSubmit) {
|
||||
this.viewIntegration(integration, tabIndices.sendTestAlert);
|
||||
} else {
|
||||
this.clearCurrentIntegration(type);
|
||||
this.clearCurrentIntegration({ type });
|
||||
}
|
||||
|
||||
createFlash({
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
|
||||
import produce from 'immer';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import introspectionQueryResultData from './graphql/fragmentTypes.json';
|
||||
import getCurrentIntegrationQuery from './graphql/queries/get_current_integration.query.graphql';
|
||||
|
||||
const fragmentMatcher = new IntrospectionFragmentMatcher({
|
||||
introspectionQueryResultData,
|
||||
});
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
const resolvers = {
|
||||
|
@ -50,7 +56,9 @@ const resolvers = {
|
|||
|
||||
export default new VueApollo({
|
||||
defaultClient: createDefaultClient(resolvers, {
|
||||
cacheConfig: {},
|
||||
cacheConfig: {
|
||||
fragmentMatcher,
|
||||
},
|
||||
assumeImmutableResults: true,
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"__schema":{"types":[{"kind":"UNION","name":"AlertManagementIntegration","possibleTypes":[{"name":"AlertManagementHttpIntegration"},{"name":"AlertManagementPrometheusIntegration"}]}]}}
|
|
@ -0,0 +1,74 @@
|
|||
<script>
|
||||
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
|
||||
|
||||
function getHeaderNumber(el) {
|
||||
return parseInt(el.tagName.match(/\d+/)[0], 10);
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isHidden: false,
|
||||
items: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.blobViewer = document.querySelector('.blob-viewer[data-type="rich"]');
|
||||
|
||||
this.observer = new MutationObserver(() => {
|
||||
if (this.blobViewer.classList.contains('hidden')) {
|
||||
this.isHidden = true;
|
||||
} else if (this.blobViewer.getAttribute('data-loaded') === 'true') {
|
||||
this.isHidden = false;
|
||||
this.generateHeaders();
|
||||
}
|
||||
});
|
||||
|
||||
if (this.blobViewer) {
|
||||
this.observer.observe(this.blobViewer, {
|
||||
attributes: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.observer) {
|
||||
this.observer.disconnect();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateHeaders() {
|
||||
const headers = [...this.blobViewer.querySelectorAll('h1,h2,h3,h4,h5,h6')];
|
||||
|
||||
if (headers.length) {
|
||||
const firstHeader = getHeaderNumber(headers[0]);
|
||||
|
||||
headers.forEach((el) => {
|
||||
this.items.push({
|
||||
text: el.textContent.trim(),
|
||||
anchor: el.querySelector('a').getAttribute('id'),
|
||||
spacing: Math.max((getHeaderNumber(el) - firstHeader) * 8, 0),
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-dropdown v-if="!isHidden && items.length" icon="list-bulleted" class="gl-mr-2">
|
||||
<gl-dropdown-item v-for="(item, index) in items" :key="index" :href="`#${item.anchor}`">
|
||||
<span
|
||||
:style="{ 'padding-left': `${item.spacing}px` }"
|
||||
class="gl-display-block"
|
||||
data-testid="tableContentsLink"
|
||||
>
|
||||
{{ item.text }}
|
||||
</span>
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
</template>
|
|
@ -1,5 +1,13 @@
|
|||
<script>
|
||||
import { GlButton, GlFormGroup, GlFormInput, GlFormCheckbox, GlIcon, GlLink } from '@gitlab/ui';
|
||||
import {
|
||||
GlButton,
|
||||
GlFormGroup,
|
||||
GlFormInput,
|
||||
GlFormCheckbox,
|
||||
GlIcon,
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
} from '@gitlab/ui';
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
|
||||
|
@ -11,6 +19,7 @@ export default {
|
|||
GlFormInput,
|
||||
GlIcon,
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -78,13 +87,11 @@ export default {
|
|||
</div>
|
||||
<div class="settings-content">
|
||||
<form>
|
||||
<gl-form-checkbox
|
||||
id="grafana-integration-enabled"
|
||||
v-model="integrationEnabled"
|
||||
class="mb-4"
|
||||
>
|
||||
{{ s__('GrafanaIntegration|Active') }}
|
||||
</gl-form-checkbox>
|
||||
<gl-form-group :label="__('Enable authentication')" label-for="grafana-integration-enabled">
|
||||
<gl-form-checkbox id="grafana-integration-enabled" v-model="integrationEnabled">
|
||||
{{ s__('GrafanaIntegration|Active') }}
|
||||
</gl-form-checkbox>
|
||||
</gl-form-group>
|
||||
<gl-form-group
|
||||
:label="s__('GrafanaIntegration|Grafana URL')"
|
||||
label-for="grafana-url"
|
||||
|
@ -95,18 +102,27 @@ export default {
|
|||
<gl-form-group :label="s__('GrafanaIntegration|API token')" label-for="grafana-token">
|
||||
<gl-form-input id="grafana-token" v-model="localGrafanaToken" />
|
||||
<p class="form-text text-muted">
|
||||
{{ s__('GrafanaIntegration|Enter the Grafana API token.') }}
|
||||
<a
|
||||
href="https://grafana.com/docs/http_api/auth/#create-api-token"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
<gl-sprintf
|
||||
:message="
|
||||
s__('GrafanaIntegration|Enter the %{docLinkStart}Grafana API token%{docLinkEnd}.')
|
||||
"
|
||||
>
|
||||
{{ __('More information.') }}
|
||||
<gl-icon name="external-link" class="vertical-align-middle" />
|
||||
</a>
|
||||
<template #docLink="{ content }">
|
||||
<gl-link
|
||||
href="https://grafana.com/docs/http_api/auth/#create-api-token"
|
||||
target="_blank"
|
||||
>{{ content }} <gl-icon name="external-link" class="gl-vertical-align-middle"
|
||||
/></gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
</gl-form-group>
|
||||
<gl-button variant="success" category="primary" @click="updateGrafanaIntegration">
|
||||
<gl-button
|
||||
variant="confirm"
|
||||
category="primary"
|
||||
data-testid="save-grafana-settings-button"
|
||||
@click="updateGrafanaIntegration"
|
||||
>
|
||||
{{ __('Save changes') }}
|
||||
</gl-button>
|
||||
</form>
|
||||
|
|
|
@ -134,7 +134,7 @@ export default {
|
|||
ref="submitBtn"
|
||||
data-qa-selector="save_changes_button"
|
||||
:disabled="loading"
|
||||
variant="success"
|
||||
variant="confirm"
|
||||
type="submit"
|
||||
class="js-no-auto-disable"
|
||||
>
|
||||
|
|
|
@ -11,7 +11,6 @@ import {
|
|||
GlModal,
|
||||
GlModalDirective,
|
||||
} from '@gitlab/ui';
|
||||
import { isEqual } from 'lodash';
|
||||
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
||||
import { I18N_PAGERDUTY_SETTINGS_FORM, CONFIGURE_PAGERDUTY_WEBHOOK_DOCS_LINK } from '../constants';
|
||||
|
||||
|
@ -50,14 +49,8 @@ export default {
|
|||
pagerduty_active: this.active,
|
||||
};
|
||||
},
|
||||
isFormUpdated() {
|
||||
return isEqual(this.pagerDutySettings, {
|
||||
active: this.active,
|
||||
webhookUrl: this.webhookUrl,
|
||||
});
|
||||
},
|
||||
isSaveDisabled() {
|
||||
return this.isFormUpdated || this.loading || this.resettingWebhook;
|
||||
return this.loading || this.resettingWebhook;
|
||||
},
|
||||
webhookUpdateAlertMsg() {
|
||||
return this.webhookUpdateFailed
|
||||
|
@ -123,13 +116,15 @@ export default {
|
|||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
<form ref="settingsForm" @submit.prevent="updatePagerDutyIntegrationSettings">
|
||||
<form ref="settingsForm">
|
||||
<gl-form-group class="col-8 col-md-9 gl-p-0">
|
||||
<gl-toggle
|
||||
id="active"
|
||||
v-model="active"
|
||||
:disabled="isSaveDisabled"
|
||||
:is-loading="loading"
|
||||
:label="$options.i18n.activeToggle.label"
|
||||
@change="updatePagerDutyIntegrationSettings"
|
||||
/>
|
||||
</gl-form-group>
|
||||
|
||||
|
@ -166,15 +161,6 @@ export default {
|
|||
{{ $options.i18n.webhookUrl.restKeyInfo }}
|
||||
</gl-modal>
|
||||
</gl-form-group>
|
||||
<gl-button
|
||||
ref="submitBtn"
|
||||
:disabled="isSaveDisabled"
|
||||
variant="success"
|
||||
type="submit"
|
||||
class="js-no-auto-disable"
|
||||
>
|
||||
{{ $options.i18n.saveBtnLabel }}
|
||||
</gl-button>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -23,7 +23,7 @@ export const I18N_INTEGRATION_TABS = {
|
|||
headerText: s__('IncidentSettings|Incidents'),
|
||||
expandBtnLabel: __('Expand'),
|
||||
subHeaderText: s__(
|
||||
'IncidentSettings|Set up integrations with external tools to help better manage incidents.',
|
||||
'IncidentSettings|Fine-tune incident settings and set up integrations with external tools to help better manage incidents.',
|
||||
),
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<script>
|
||||
import { GlButton, GlIcon } from '@gitlab/ui';
|
||||
import { kebabCase, mapKeys } from 'lodash';
|
||||
|
||||
const getDataKey = (key) => `data-${kebabCase(key)}`;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -12,6 +15,11 @@ export default {
|
|||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
dataAttrs() {
|
||||
return mapKeys(this.menuItem.data || {}, (value, key) => getDataKey(key));
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -20,6 +28,8 @@ export default {
|
|||
category="tertiary"
|
||||
:href="menuItem.href"
|
||||
class="top-nav-menu-item gl-display-block"
|
||||
:class="menuItem.css_class"
|
||||
v-bind="dataAttrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<span class="gl-display-flex">
|
||||
|
|
|
@ -34,19 +34,19 @@ export default {
|
|||
<h4
|
||||
class="js-section-header settings-title js-settings-toggle js-settings-toggle-trigger-only"
|
||||
>
|
||||
{{ s__('MetricsSettings|Metrics dashboard') }}
|
||||
{{ s__('MetricsSettings|Metrics') }}
|
||||
</h4>
|
||||
<gl-button class="js-settings-toggle">{{ __('Expand') }}</gl-button>
|
||||
<p class="js-section-sub-header">
|
||||
{{ s__('MetricsSettings|Manage Metrics Dashboard settings.') }}
|
||||
<gl-link :href="helpPage">{{ __('Learn more') }}</gl-link>
|
||||
{{ s__('MetricsSettings|Manage metrics dashboard settings.') }}
|
||||
<gl-link :href="helpPage">{{ __('Learn more.') }}</gl-link>
|
||||
</p>
|
||||
</div>
|
||||
<div class="settings-content">
|
||||
<form>
|
||||
<dashboard-timezone />
|
||||
<external-dashboard />
|
||||
<gl-button variant="success" category="primary" @click="saveChanges">
|
||||
<gl-button variant="confirm" category="primary" @click="saveChanges">
|
||||
{{ __('Save Changes') }}
|
||||
</gl-button>
|
||||
</form>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import TableOfContents from '~/blob/components/table_contents.vue';
|
||||
import PipelineTourSuccessModal from '~/blob/pipeline_tour_success_modal.vue';
|
||||
import BlobViewer from '~/blob/viewer/index';
|
||||
import GpgBadges from '~/gpg_badges';
|
||||
|
@ -92,3 +93,15 @@ if (successPipelineEl) {
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
const tableContentsEl = document.querySelector('.js-table-contents');
|
||||
|
||||
if (tableContentsEl) {
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el: tableContentsEl,
|
||||
render(h) {
|
||||
return h(TableOfContents);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ module Packages
|
|||
end
|
||||
|
||||
def projects_visible_to_reporters(user, within_group:)
|
||||
if user.is_a?(DeployToken) && Feature.enabled?(:packages_finder_helper_deploy_token, default_enabled: :yaml)
|
||||
if user.is_a?(DeployToken)
|
||||
user.accessible_projects
|
||||
else
|
||||
within_group.all_projects
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
module Mutations
|
||||
module ResolvesSubscription
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
argument :subscribed_state,
|
||||
GraphQL::BOOLEAN_TYPE,
|
||||
|
|
|
@ -2,10 +2,32 @@
|
|||
|
||||
module Mutations
|
||||
module Issues
|
||||
class SetSubscription < Base
|
||||
class SetSubscription < BaseMutation
|
||||
graphql_name 'IssueSetSubscription'
|
||||
|
||||
include ResolvesSubscription
|
||||
include Mutations::ResolvesIssuable
|
||||
|
||||
argument :project_path, GraphQL::ID_TYPE,
|
||||
required: true,
|
||||
description: "The project the issue to mutate is in."
|
||||
|
||||
argument :iid, GraphQL::STRING_TYPE,
|
||||
required: true,
|
||||
description: "The IID of the issue to mutate."
|
||||
|
||||
field :issue,
|
||||
Types::IssueType,
|
||||
null: true,
|
||||
description: "The issue after mutation."
|
||||
|
||||
authorize :update_subscription
|
||||
|
||||
private
|
||||
|
||||
def find_object(project_path:, iid:)
|
||||
resolve_issuable(type: :issue, parent_path: project_path, iid: iid)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,10 +2,32 @@
|
|||
|
||||
module Mutations
|
||||
module MergeRequests
|
||||
class SetSubscription < Base
|
||||
class SetSubscription < BaseMutation
|
||||
graphql_name 'MergeRequestSetSubscription'
|
||||
|
||||
include ResolvesSubscription
|
||||
include Mutations::ResolvesIssuable
|
||||
|
||||
argument :project_path, GraphQL::ID_TYPE,
|
||||
required: true,
|
||||
description: "The project the merge request to mutate is in."
|
||||
|
||||
argument :iid, GraphQL::STRING_TYPE,
|
||||
required: true,
|
||||
description: "The IID of the merge request to mutate."
|
||||
|
||||
field :merge_request,
|
||||
Types::MergeRequestType,
|
||||
null: true,
|
||||
description: "The merge request after mutation."
|
||||
|
||||
authorize :update_subscription
|
||||
|
||||
private
|
||||
|
||||
def find_object(project_path:, iid:)
|
||||
resolve_issuable(type: :merge_request, parent_path: project_path, iid: iid)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -132,7 +132,7 @@ module Nav
|
|||
active: active_nav_link?(controller: 'admin/sessions'),
|
||||
icon: 'lock-open',
|
||||
href: destroy_admin_session_path,
|
||||
method: :post
|
||||
data: { method: 'post' }
|
||||
)
|
||||
elsif current_user.admin?
|
||||
builder.add_secondary_menu_item(
|
||||
|
|
|
@ -336,7 +336,7 @@ class Member < ApplicationRecord
|
|||
|
||||
return User.find_by(id: user) if user.is_a?(Integer)
|
||||
|
||||
User.find_by(email: user) || user
|
||||
User.find_by_any_email(user) || user
|
||||
end
|
||||
|
||||
def retrieve_member(source, user, existing_members)
|
||||
|
|
|
@ -38,6 +38,7 @@ class IssuePolicy < IssuablePolicy
|
|||
|
||||
rule { ~anonymous & can?(:read_issue) }.policy do
|
||||
enable :create_todo
|
||||
enable :update_subscription
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ class MergeRequestPolicy < IssuablePolicy
|
|||
|
||||
rule { ~anonymous & can?(:read_merge_request) }.policy do
|
||||
enable :create_todo
|
||||
enable :update_subscription
|
||||
end
|
||||
|
||||
condition(:can_merge) { @subject.can_be_merged_by?(@user) }
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
.file-header-content
|
||||
- if Gitlab::MarkupHelper.gitlab_markdown?(blob.path)
|
||||
.js-table-contents
|
||||
= blob_icon blob.mode, blob.name
|
||||
|
||||
%strong.file-title-name.gl-word-break-all{ data: { qa_selector: 'file_name_content' } }
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
%section.settings.no-animate#js-alert-management-settings{ class: ('expanded' if expanded) }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _('Alert integrations')
|
||||
= _('Alerts')
|
||||
%button.gl-button.btn.btn-default.js-settings-toggle{ type: 'button' }
|
||||
= _('Expand')
|
||||
%p
|
||||
|
|
|
@ -1,23 +1,13 @@
|
|||
- setting = tracing_setting
|
||||
- has_jaeger_url = setting.external_url.present?
|
||||
|
||||
%section.settings.border-0.no-animate
|
||||
.settings-header{ :class => "border-top" }
|
||||
.settings-header{ :class => 'border-top' }
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _("Jaeger tracing")
|
||||
= _('Tracing')
|
||||
%button.btn.btn-default.gl-button.js-settings-toggle{ type: 'button' }
|
||||
= _('Expand')
|
||||
%p
|
||||
- if has_jaeger_url
|
||||
- tracing_link = link_to sanitize(setting.external_url, scrubber: Rails::Html::TextOnlyScrubber.new), target: "_blank", rel: 'noopener noreferrer' do
|
||||
%span
|
||||
= _('Tracing')
|
||||
= sprite_icon('external-link', css_class: 'ml-1 vertical-align-middle')
|
||||
- else
|
||||
- tracing_link = link_to project_tracing_path(@project) do
|
||||
%span
|
||||
= _('Tracing')
|
||||
= _("To open Jaeger from GitLab to view tracing from the %{link} page, add a URL to your Jaeger server.").html_safe % { link: tracing_link }
|
||||
= _('Embed an image of your existing Jaeger server in GitLab.')
|
||||
= link_to _('Learn more.'), help_page_path('operations/tracing'), target: '_blank', rel: 'noopener noreferrer'
|
||||
.settings-content
|
||||
= form_for @project, url: project_settings_operations_path(@project), method: :patch do |f|
|
||||
|
@ -27,8 +17,8 @@
|
|||
= form.label :external_url, _('Jaeger URL'), class: 'label-bold'
|
||||
= form.url_field :external_url, class: 'form-control gl-form-input', placeholder: 'https://jaeger.example.com'
|
||||
%p.form-text.text-muted
|
||||
- jaeger_help_url = "https://www.jaegertracing.io/docs/getting-started/"
|
||||
- jaeger_help_url = 'https://www.jaegertracing.io/docs/getting-started/'
|
||||
- link_start_tag = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: jaeger_help_url }
|
||||
- link_end_tag = "#{sprite_icon('external-link', css_class: 'ml-1 vertical-align-middle')}</a>".html_safe
|
||||
= _("Learn more about %{link_start_tag}Jaeger configuration%{link_end_tag}.").html_safe % { link_start_tag: link_start_tag, link_end_tag: link_end_tag }
|
||||
- link_end_tag = "#{sprite_icon('external-link', css_class: 'gl-ml-2 gl-vertical-align-middle')}</a>".html_safe
|
||||
= _('Learn more about %{link_start_tag}Jaeger configuration%{link_end_tag}.').html_safe % { link_start_tag: link_start_tag, link_end_tag: link_end_tag }
|
||||
= f.submit _('Save changes'), class: 'gl-button btn btn-confirm'
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
- page_title title
|
||||
- breadcrumb_title title
|
||||
|
||||
= render 'projects/settings/operations/metrics_dashboard'
|
||||
= render 'projects/settings/operations/tracing'
|
||||
= render 'projects/settings/operations/error_tracking'
|
||||
= render 'projects/settings/operations/alert_management'
|
||||
= render 'projects/settings/operations/incidents'
|
||||
= render 'projects/settings/operations/error_tracking'
|
||||
= render 'projects/settings/operations/prometheus', service: prometheus_service if Feature.enabled?(:settings_operations_prometheus_service)
|
||||
= render 'projects/settings/operations/metrics_dashboard'
|
||||
= render 'projects/settings/operations/grafana_integration'
|
||||
= render 'projects/settings/operations/tracing'
|
||||
= render_if_exists 'projects/settings/operations/status_page'
|
||||
= render 'projects/settings/operations/prometheus', service: prometheus_service if Feature.enabled?(:settings_operations_prometheus_service)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Removed packages_finder_helper_deploy_token feature flag
|
||||
merge_request: 62189
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Observe secondary email addresses when adding a member
|
||||
merge_request: 62024
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove support for /wip quick action
|
||||
merge_request: 61199
|
||||
author:
|
||||
type: removed
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Fix permission check when setting issue/merge request subscription in GraphQL
|
||||
API.
|
||||
merge_request: 61980
|
||||
author:
|
||||
type: fixed
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
name: packages_finder_helper_deploy_token
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58497
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/326808
|
||||
milestone: '13.11'
|
||||
name: dast_runner_site_validation
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61649
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/331082
|
||||
milestone: '14.0'
|
||||
type: development
|
||||
group: group::package
|
||||
default_enabled: true
|
||||
group: group::dynamic analysis
|
||||
default_enabled: false
|
|
@ -1,16 +1,18 @@
|
|||
---
|
||||
key_path: redis_hll_counters.pipeline_authoring.o_pipeline_authoring_unique_users_committing_ciconfigfile_monthly
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Monthly unique user count doing commits which contains the CI config file
|
||||
product_section: ops
|
||||
product_stage: verify
|
||||
product_group: group::pipeline authoring
|
||||
product_category: pipeline_authoring
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: 28d
|
||||
data_source: redis_hll
|
||||
distribution:
|
||||
- ee
|
||||
- ce
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
key_path: redis_hll_counters.pipeline_authoring.o_pipeline_authoring_unique_users_committing_ciconfigfile_weekly
|
||||
description: Weekly unique user count doing commits which contains the CI config file
|
||||
product_section: ops
|
||||
product_stage: verify
|
||||
product_group: group::pipeline authoring
|
||||
product_category: pipeline_authoring
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: 7d
|
||||
data_source: redis_hll
|
||||
distribution:
|
||||
- ee
|
||||
- ce
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
|
@ -15,6 +15,7 @@ GitLab features use it, including:
|
|||
- [CI Linter](../../ci/lint.md)
|
||||
- [Snippets](../../user/snippets.md)
|
||||
- [Web Editor](../../user/project/repository/web_editor.md)
|
||||
- [Security Policies](../../user/application_security/threat_monitoring/index.md)
|
||||
|
||||
## How to use Editor Lite
|
||||
|
||||
|
|
|
@ -12828,21 +12828,21 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `redis_hll_counters.pipeline_authoring.o_pipeline_authoring_unique_users_committing_ciconfigfile_monthly`
|
||||
|
||||
Missing description
|
||||
Monthly unique user count doing commits which contains the CI config file
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210216184303_o_pipeline_authoring_unique_users_committing_ciconfigfile_monthly.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::pipeline authoring`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `redis_hll_counters.pipeline_authoring.o_pipeline_authoring_unique_users_committing_ciconfigfile_weekly`
|
||||
|
||||
Monthly unique user count doing commits which contains the CI config file
|
||||
Weekly unique user count doing commits which contains the CI config file
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_7d/20210216184301_o_pipeline_authoring_unique_users_committing_ciconfigfile_weekly.yml)
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210216184301_o_pipeline_authoring_unique_users_committing_ciconfigfile_weekly.yml)
|
||||
|
||||
Group: `group::pipeline authoring`
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ to use this endpoint.
|
|||
|
||||
With Maintainer or higher [permissions](../../user/permissions.md),
|
||||
you can view the list of configured alerts integrations by navigating to **Settings > Operations**
|
||||
in your project's sidebar menu, and expanding the **Alert integrations** section. The list displays
|
||||
in your project's sidebar menu, and expanding the **Alerts** section. The list displays
|
||||
the integration name, type, and status (enabled or disabled):
|
||||
|
||||
![Current Integrations](img/integrations_list_v13_5.png)
|
||||
|
@ -39,7 +39,7 @@ receive alert payloads in JSON format. You can always
|
|||
1. Sign in to GitLab as a user with maintainer [permissions](../../user/permissions.md)
|
||||
for a project.
|
||||
1. Navigate to **Settings > Operations** in your project.
|
||||
1. Expand the **Alert integrations** section, and in the **Select integration type** dropdown menu,
|
||||
1. Expand the **Alerts** section, and in the **Select integration type** dropdown menu,
|
||||
select **HTTP Endpoint**.
|
||||
1. Toggle the **Active** alert setting. The URL and Authorization Key for the webhook configuration
|
||||
are available in the **View credentials** tab after you save the integration. You must also input
|
||||
|
@ -56,7 +56,7 @@ and you can [customize the payload](#customize-the-alert-payload-outside-of-gitl
|
|||
1. Sign in to GitLab as a user with maintainer [permissions](../../user/permissions.md)
|
||||
for a project.
|
||||
1. Navigate to **Settings > Operations** in your project.
|
||||
1. Expand the **Alert integrations** section.
|
||||
1. Expand the **Alerts** section.
|
||||
1. For each endpoint you want to create:
|
||||
|
||||
1. Click the **Add new integration** button.
|
||||
|
|
|
@ -111,7 +111,6 @@ threads. Some quick actions might not be available to all subscription tiers.
|
|||
| `/unlock` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Unlock the discussions. |
|
||||
| `/unsubscribe` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Unsubscribe from notifications. |
|
||||
| `/weight <value>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set weight. Valid options for `<value>` include `0`, `1`, `2`, and so on. |
|
||||
| `/wip` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Toggle the draft status. |
|
||||
| `/zoom <Zoom URL>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add Zoom meeting to this issue ([introduced in GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16609)). |
|
||||
|
||||
## Commit messages
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
module API
|
||||
class Jobs < ::API::Base
|
||||
include PaginationParams
|
||||
|
||||
before { authenticate! }
|
||||
|
||||
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
|
|
|
@ -21,10 +21,6 @@ module Gitlab
|
|||
nil
|
||||
end
|
||||
|
||||
def known_payload_keys
|
||||
super + STORAGES.flat_map(&:known_payload_keys)
|
||||
end
|
||||
|
||||
def payload
|
||||
super.merge(*STORAGES.flat_map(&:payload))
|
||||
end
|
||||
|
|
|
@ -5,12 +5,6 @@ module Gitlab
|
|||
module RedisPayload
|
||||
include ::Gitlab::Utils::StrongMemoize
|
||||
|
||||
# Fetches payload keys from the lazy payload (this avoids
|
||||
# unnecessary processing of the values).
|
||||
def known_payload_keys
|
||||
to_lazy_payload.keys
|
||||
end
|
||||
|
||||
def payload
|
||||
to_lazy_payload.transform_values do |value|
|
||||
result = value.call
|
||||
|
|
|
@ -51,10 +51,6 @@ module Gitlab
|
|||
payload
|
||||
end
|
||||
|
||||
def self.known_payload_keys
|
||||
DB_COUNTERS
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ignored_query?(payload)
|
||||
|
|
|
@ -14,8 +14,6 @@ module Gitlab
|
|||
COUNTER = :external_http_count
|
||||
DURATION = :external_http_duration_s
|
||||
|
||||
KNOWN_PAYLOAD_KEYS = [COUNTER, DURATION].freeze
|
||||
|
||||
def self.detail_store
|
||||
::Gitlab::SafeRequestStore[DETAIL_STORE] ||= []
|
||||
end
|
||||
|
|
|
@ -8,14 +8,13 @@ module Gitlab
|
|||
# this is already :/. We could also take a hash and manually check every
|
||||
# entry, but it's much more maintainable to do rely on native Ruby.
|
||||
# rubocop: disable Metrics/ParameterLists
|
||||
def self.build(id:, title:, active: false, icon: '', href: '', method: nil, view: '', css_class: '', data: {})
|
||||
def self.build(id:, title:, active: false, icon: '', href: '', view: '', css_class: '', data: {})
|
||||
{
|
||||
id: id,
|
||||
title: title,
|
||||
active: active,
|
||||
icon: icon,
|
||||
href: href,
|
||||
method: method,
|
||||
view: view.to_s,
|
||||
css_class: css_class,
|
||||
data: data
|
||||
|
|
|
@ -99,7 +99,7 @@ module Gitlab
|
|||
# Allow it to mark as WIP on MR creation page _or_ through MR notes.
|
||||
(quick_action_target.new_record? || current_user.can?(:"update_#{quick_action_target.to_ability_name}", quick_action_target))
|
||||
end
|
||||
command :draft, :wip do
|
||||
command :draft do
|
||||
@updates[:wip_event] = quick_action_target.work_in_progress? ? 'unwip' : 'wip'
|
||||
end
|
||||
|
||||
|
|
|
@ -3,24 +3,6 @@
|
|||
module Gitlab
|
||||
module SidekiqMiddleware
|
||||
class InstrumentationLogger
|
||||
def self.keys
|
||||
@keys ||= [
|
||||
:cpu_s,
|
||||
:gitaly_calls,
|
||||
:gitaly_duration_s,
|
||||
:rugged_calls,
|
||||
:rugged_duration_s,
|
||||
:elasticsearch_calls,
|
||||
:elasticsearch_duration_s,
|
||||
:elasticsearch_timed_out_count,
|
||||
*::Gitlab::Memory::Instrumentation::KEY_MAPPING.values,
|
||||
*::Gitlab::Instrumentation::Redis.known_payload_keys,
|
||||
*::Gitlab::Metrics::Subscribers::ActiveRecord.known_payload_keys,
|
||||
*::Gitlab::Metrics::Subscribers::ExternalHttp::KNOWN_PAYLOAD_KEYS,
|
||||
*::Gitlab::Metrics::Subscribers::RackAttack::PAYLOAD_KEYS
|
||||
]
|
||||
end
|
||||
|
||||
def call(worker, job, queue)
|
||||
::Gitlab::InstrumentationHelper.init_instrumentation_data
|
||||
|
||||
|
@ -37,7 +19,6 @@ module Gitlab
|
|||
# https://github.com/mperham/sidekiq/blob/53bd529a0c3f901879925b8390353129c465b1f2/lib/sidekiq/processor.rb#L115-L118
|
||||
job[:instrumentation] = {}.tap do |instrumentation_values|
|
||||
::Gitlab::InstrumentationHelper.add_instrumentation_data(instrumentation_values)
|
||||
instrumentation_values.slice!(*self.class.keys)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2859,9 +2859,6 @@ msgid_plural "Alerts"
|
|||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "Alert integrations"
|
||||
msgstr ""
|
||||
|
||||
msgid "AlertManagement|Acknowledged"
|
||||
msgstr ""
|
||||
|
||||
|
@ -8424,6 +8421,9 @@ msgstr ""
|
|||
msgid "Configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "Configuration help"
|
||||
msgstr ""
|
||||
|
||||
msgid "Configure GitLab runners to start using the Web Terminal. %{helpStart}Learn more.%{helpEnd}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -12130,6 +12130,9 @@ msgstr ""
|
|||
msgid "Embed"
|
||||
msgstr ""
|
||||
|
||||
msgid "Embed an image of your existing Jaeger server in GitLab."
|
||||
msgstr ""
|
||||
|
||||
msgid "Empty file"
|
||||
msgstr ""
|
||||
|
||||
|
@ -12214,6 +12217,9 @@ msgstr ""
|
|||
msgid "Enable authenticated API request rate limit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable authentication"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable automatic repository housekeeping (git repack, git gc)"
|
||||
msgstr ""
|
||||
|
||||
|
@ -15509,7 +15515,7 @@ msgstr ""
|
|||
msgid "GrafanaIntegration|Active"
|
||||
msgstr ""
|
||||
|
||||
msgid "GrafanaIntegration|Enter the Grafana API token."
|
||||
msgid "GrafanaIntegration|Enter the %{docLinkStart}Grafana API token%{docLinkEnd}."
|
||||
msgstr ""
|
||||
|
||||
msgid "GrafanaIntegration|Enter the base URL of the Grafana instance."
|
||||
|
@ -17382,6 +17388,9 @@ msgstr ""
|
|||
msgid "IncidentSettings|Alert integration"
|
||||
msgstr ""
|
||||
|
||||
msgid "IncidentSettings|Fine-tune incident settings and set up integrations with external tools to help better manage incidents."
|
||||
msgstr ""
|
||||
|
||||
msgid "IncidentSettings|Grafana integration"
|
||||
msgstr ""
|
||||
|
||||
|
@ -17397,9 +17406,6 @@ msgstr ""
|
|||
msgid "IncidentSettings|PagerDuty integration"
|
||||
msgstr ""
|
||||
|
||||
msgid "IncidentSettings|Set up integrations with external tools to help better manage incidents."
|
||||
msgstr ""
|
||||
|
||||
msgid "IncidentSettings|Time limit"
|
||||
msgstr ""
|
||||
|
||||
|
@ -18560,9 +18566,6 @@ msgstr ""
|
|||
msgid "Jaeger URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "Jaeger tracing"
|
||||
msgstr ""
|
||||
|
||||
msgid "Jan"
|
||||
msgstr ""
|
||||
|
||||
|
@ -20907,10 +20910,10 @@ msgstr ""
|
|||
msgid "MetricsSettings|External dashboard URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "MetricsSettings|Manage Metrics Dashboard settings."
|
||||
msgid "MetricsSettings|Manage metrics dashboard settings."
|
||||
msgstr ""
|
||||
|
||||
msgid "MetricsSettings|Metrics dashboard"
|
||||
msgid "MetricsSettings|Metrics"
|
||||
msgstr ""
|
||||
|
||||
msgid "MetricsSettings|UTC (Coordinated Universal Time)"
|
||||
|
@ -31126,9 +31129,6 @@ msgstr ""
|
|||
msgid "StatusPage|Status page URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "StatusPage|Status page frontend documentation"
|
||||
msgstr ""
|
||||
|
||||
msgid "StatusPage|To publish incidents to an external status page, GitLab stores a JSON file in your Amazon S3 account at a location that your external status page service can access. Make sure to also set up %{docsLink}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -34049,9 +34049,6 @@ msgstr ""
|
|||
msgid "To only use CI/CD features for an external repository, choose %{strong_open}CI/CD for external repo%{strong_close}."
|
||||
msgstr ""
|
||||
|
||||
msgid "To open Jaeger from GitLab to view tracing from the %{link} page, add a URL to your Jaeger server."
|
||||
msgstr ""
|
||||
|
||||
msgid "To pass variables to the triggered pipeline, add %{code_start}variables[VARIABLE]=VALUE%{code_end} to the API request."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ module QA
|
|||
DOCKER_TLS_CERTDIR: "/certs"
|
||||
DOCKER_TLS_VERIFY: 1
|
||||
DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
|
||||
before_script:
|
||||
- until docker info; do sleep 1; done
|
||||
script:
|
||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||
- docker build -t $IMAGE_TAG .
|
||||
|
|
|
@ -25,7 +25,7 @@ RSpec.describe 'Alert integrations settings form', :js do
|
|||
|
||||
it 'shows the alerts setting form title' do
|
||||
page.within('#js-alert-management-settings') do
|
||||
expect(find('h4')).to have_content('Alert integrations')
|
||||
expect(find('h4')).to have_content('Alerts')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ RSpec.describe 'User searches project settings', :js do
|
|||
visit project_settings_operations_path(project)
|
||||
end
|
||||
|
||||
it_behaves_like 'can search settings', 'Alert integrations', 'Error tracking'
|
||||
it_behaves_like 'can search settings', 'Alerts', 'Error tracking'
|
||||
end
|
||||
|
||||
context 'in Pages page' do
|
||||
|
|
|
@ -113,41 +113,22 @@ RSpec.describe ::Packages::FinderHelper do
|
|||
let_it_be(:user) { create(:deploy_token, :group, read_package_registry: true) }
|
||||
let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) }
|
||||
|
||||
shared_examples 'handling all conditions' do
|
||||
where(:group_visibility, :subgroup_visibility, :project2_visibility, :shared_example_name) do
|
||||
'PUBLIC' | 'PUBLIC' | 'PUBLIC' | 'returning both packages'
|
||||
'PUBLIC' | 'PUBLIC' | 'PRIVATE' | 'returning both packages'
|
||||
'PUBLIC' | 'PRIVATE' | 'PRIVATE' | 'returning both packages'
|
||||
'PRIVATE' | 'PRIVATE' | 'PRIVATE' | 'returning both packages'
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
project2.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project2_visibility, false))
|
||||
subgroup.update!(visibility_level: Gitlab::VisibilityLevel.const_get(subgroup_visibility, false))
|
||||
project1.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false))
|
||||
group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false))
|
||||
end
|
||||
|
||||
it_behaves_like params[:shared_example_name]
|
||||
end
|
||||
where(:group_visibility, :subgroup_visibility, :project2_visibility, :shared_example_name) do
|
||||
'PUBLIC' | 'PUBLIC' | 'PUBLIC' | 'returning both packages'
|
||||
'PUBLIC' | 'PUBLIC' | 'PRIVATE' | 'returning both packages'
|
||||
'PUBLIC' | 'PRIVATE' | 'PRIVATE' | 'returning both packages'
|
||||
'PRIVATE' | 'PRIVATE' | 'PRIVATE' | 'returning both packages'
|
||||
end
|
||||
|
||||
context 'with packages_finder_helper_deploy_token enabled' do
|
||||
with_them do
|
||||
before do
|
||||
expect(group).not_to receive(:all_projects)
|
||||
project2.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project2_visibility, false))
|
||||
subgroup.update!(visibility_level: Gitlab::VisibilityLevel.const_get(subgroup_visibility, false))
|
||||
project1.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false))
|
||||
group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false))
|
||||
end
|
||||
|
||||
it_behaves_like 'handling all conditions'
|
||||
end
|
||||
|
||||
context 'with packages_finder_helper_deploy_token disabled' do
|
||||
before do
|
||||
stub_feature_flags(packages_finder_helper_deploy_token: false)
|
||||
expect(group).to receive(:all_projects).and_call_original
|
||||
end
|
||||
|
||||
it_behaves_like 'handling all conditions'
|
||||
it_behaves_like params[:shared_example_name]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -236,41 +217,22 @@ RSpec.describe ::Packages::FinderHelper do
|
|||
let_it_be(:user) { create(:deploy_token, :group, read_package_registry: true) }
|
||||
let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) }
|
||||
|
||||
shared_examples 'handling all conditions' do
|
||||
where(:group_visibility, :subgroup_visibility, :project2_visibility, :shared_example_name) do
|
||||
'PUBLIC' | 'PUBLIC' | 'PUBLIC' | 'returning both projects'
|
||||
'PUBLIC' | 'PUBLIC' | 'PRIVATE' | 'returning both projects'
|
||||
'PUBLIC' | 'PRIVATE' | 'PRIVATE' | 'returning both projects'
|
||||
'PRIVATE' | 'PRIVATE' | 'PRIVATE' | 'returning both projects'
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
project2.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project2_visibility, false))
|
||||
subgroup.update!(visibility_level: Gitlab::VisibilityLevel.const_get(subgroup_visibility, false))
|
||||
project1.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false))
|
||||
group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false))
|
||||
end
|
||||
|
||||
it_behaves_like params[:shared_example_name]
|
||||
end
|
||||
where(:group_visibility, :subgroup_visibility, :project2_visibility, :shared_example_name) do
|
||||
'PUBLIC' | 'PUBLIC' | 'PUBLIC' | 'returning both projects'
|
||||
'PUBLIC' | 'PUBLIC' | 'PRIVATE' | 'returning both projects'
|
||||
'PUBLIC' | 'PRIVATE' | 'PRIVATE' | 'returning both projects'
|
||||
'PRIVATE' | 'PRIVATE' | 'PRIVATE' | 'returning both projects'
|
||||
end
|
||||
|
||||
context 'with packages_finder_helper_deploy_token enabled' do
|
||||
with_them do
|
||||
before do
|
||||
expect(group).not_to receive(:all_projects)
|
||||
project2.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project2_visibility, false))
|
||||
subgroup.update!(visibility_level: Gitlab::VisibilityLevel.const_get(subgroup_visibility, false))
|
||||
project1.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false))
|
||||
group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false))
|
||||
end
|
||||
|
||||
it_behaves_like 'handling all conditions'
|
||||
end
|
||||
|
||||
context 'with packages_finder_helper_deploy_token disabled' do
|
||||
before do
|
||||
stub_feature_flags(packages_finder_helper_deploy_token: false)
|
||||
expect(group).to receive(:all_projects).and_call_original
|
||||
end
|
||||
|
||||
it_behaves_like 'handling all conditions'
|
||||
it_behaves_like params[:shared_example_name]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -80,7 +80,7 @@ describe('AlertIntegrationsList', () => {
|
|||
const cell = finsStatusCell().at(0);
|
||||
const activatedIcon = cell.find(GlIcon);
|
||||
expect(cell.text()).toBe(i18n.status.enabled.name);
|
||||
expect(activatedIcon.attributes('name')).toBe('check-circle-filled');
|
||||
expect(activatedIcon.attributes('name')).toBe('check');
|
||||
expect(activatedIcon.attributes('title')).toBe(i18n.status.enabled.tooltip);
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
import { GlDropdownItem } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { nextTick } from 'vue';
|
||||
import TableContents from '~/blob/components/table_contents.vue';
|
||||
|
||||
let wrapper;
|
||||
|
||||
function createComponent() {
|
||||
wrapper = shallowMount(TableContents);
|
||||
}
|
||||
|
||||
async function setLoaded(loaded) {
|
||||
document.querySelector('.blob-viewer').setAttribute('data-loaded', loaded);
|
||||
|
||||
await nextTick();
|
||||
}
|
||||
|
||||
describe('Markdown table of contents component', () => {
|
||||
beforeEach(() => {
|
||||
setFixtures(`
|
||||
<div class="blob-viewer" data-type="rich" data-loaded="false">
|
||||
<h1><a href="#1"></a>Hello</h1>
|
||||
<h2><a href="#2"></a>World</h2>
|
||||
<h3><a href="#3"></a>Testing</h3>
|
||||
<h2><a href="#4"></a>GitLab</h2>
|
||||
</div>
|
||||
`);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('not loaded', () => {
|
||||
it('does not populate dropdown', () => {
|
||||
createComponent();
|
||||
|
||||
expect(wrapper.findComponent(GlDropdownItem).exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loaded', () => {
|
||||
it('populates dropdown', async () => {
|
||||
createComponent();
|
||||
|
||||
await setLoaded(true);
|
||||
|
||||
const dropdownItems = wrapper.findAllComponents(GlDropdownItem);
|
||||
|
||||
expect(dropdownItems.exists()).toBe(true);
|
||||
expect(dropdownItems.length).toBe(4);
|
||||
});
|
||||
|
||||
it('sets padding for dropdown items', async () => {
|
||||
createComponent();
|
||||
|
||||
await setLoaded(true);
|
||||
|
||||
const dropdownLinks = wrapper.findAll('[data-testid="tableContentsLink"]');
|
||||
|
||||
expect(dropdownLinks.at(0).element.style.paddingLeft).toBe('0px');
|
||||
expect(dropdownLinks.at(1).element.style.paddingLeft).toBe('8px');
|
||||
expect(dropdownLinks.at(2).element.style.paddingLeft).toBe('16px');
|
||||
expect(dropdownLinks.at(3).element.style.paddingLeft).toBe('8px');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -43,14 +43,18 @@ exports[`grafana integration component default state to match the default snapsh
|
|||
class="settings-content"
|
||||
>
|
||||
<form>
|
||||
<gl-form-checkbox-stub
|
||||
class="mb-4"
|
||||
id="grafana-integration-enabled"
|
||||
<gl-form-group-stub
|
||||
label="Enable authentication"
|
||||
label-for="grafana-integration-enabled"
|
||||
>
|
||||
<gl-form-checkbox-stub
|
||||
id="grafana-integration-enabled"
|
||||
>
|
||||
|
||||
Active
|
||||
|
||||
Active
|
||||
|
||||
</gl-form-checkbox-stub>
|
||||
</gl-form-checkbox-stub>
|
||||
</gl-form-group-stub>
|
||||
|
||||
<gl-form-group-stub
|
||||
description="Enter the base URL of the Grafana instance."
|
||||
|
@ -76,32 +80,19 @@ exports[`grafana integration component default state to match the default snapsh
|
|||
<p
|
||||
class="form-text text-muted"
|
||||
>
|
||||
|
||||
Enter the Grafana API token.
|
||||
|
||||
<a
|
||||
href="https://grafana.com/docs/http_api/auth/#create-api-token"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
More information.
|
||||
|
||||
<gl-icon-stub
|
||||
class="vertical-align-middle"
|
||||
name="external-link"
|
||||
size="16"
|
||||
/>
|
||||
</a>
|
||||
<gl-sprintf-stub
|
||||
message="Enter the %{docLinkStart}Grafana API token%{docLinkEnd}."
|
||||
/>
|
||||
</p>
|
||||
</gl-form-group-stub>
|
||||
|
||||
<gl-button-stub
|
||||
buttontextclasses=""
|
||||
category="primary"
|
||||
data-testid="save-grafana-settings-button"
|
||||
icon=""
|
||||
size="medium"
|
||||
variant="success"
|
||||
variant="confirm"
|
||||
>
|
||||
|
||||
Save changes
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { GlButton } from '@gitlab/ui';
|
||||
import { mount, shallowMount } from '@vue/test-utils';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import GrafanaIntegration from '~/grafana_integration/components/grafana_integration.vue';
|
||||
import { createStore } from '~/grafana_integration/store';
|
||||
|
@ -51,8 +52,7 @@ describe('grafana integration component', () => {
|
|||
it('renders as an expand button by default', () => {
|
||||
wrapper = shallowMount(GrafanaIntegration, { store });
|
||||
|
||||
const button = wrapper.find(GlButton);
|
||||
|
||||
const button = wrapper.findComponent(GlButton);
|
||||
expect(button.text()).toBe('Expand');
|
||||
});
|
||||
});
|
||||
|
@ -70,6 +70,7 @@ describe('grafana integration component', () => {
|
|||
describe('form', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(axios, 'patch').mockImplementation();
|
||||
wrapper = mountExtended(GrafanaIntegration, { store });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -77,7 +78,7 @@ describe('grafana integration component', () => {
|
|||
});
|
||||
|
||||
describe('submit button', () => {
|
||||
const findSubmitButton = () => wrapper.find('.settings-content form').find(GlButton);
|
||||
const findSubmitButton = () => wrapper.findByTestId('save-grafana-settings-button');
|
||||
|
||||
const endpointRequest = [
|
||||
operationsSettingsEndpoint,
|
||||
|
@ -93,9 +94,7 @@ describe('grafana integration component', () => {
|
|||
];
|
||||
|
||||
it('submits form on click', () => {
|
||||
wrapper = mount(GrafanaIntegration, { store });
|
||||
axios.patch.mockResolvedValue();
|
||||
|
||||
findSubmitButton(wrapper).trigger('click');
|
||||
|
||||
expect(axios.patch).toHaveBeenCalledWith(...endpointRequest);
|
||||
|
@ -104,7 +103,6 @@ describe('grafana integration component', () => {
|
|||
|
||||
it('creates flash banner on error', () => {
|
||||
const message = 'mockErrorMessage';
|
||||
wrapper = mount(GrafanaIntegration, { store });
|
||||
axios.patch.mockRejectedValue({ response: { data: { message } } });
|
||||
|
||||
findSubmitButton().trigger('click');
|
||||
|
|
|
@ -103,7 +103,7 @@ exports[`Alert integration settings form default state should match the default
|
|||
icon=""
|
||||
size="medium"
|
||||
type="submit"
|
||||
variant="success"
|
||||
variant="confirm"
|
||||
>
|
||||
|
||||
Save changes
|
||||
|
|
|
@ -30,7 +30,7 @@ exports[`IncidentsSettingTabs should render the component 1`] = `
|
|||
|
||||
<p>
|
||||
|
||||
Set up integrations with external tools to help better manage incidents.
|
||||
Fine-tune incident settings and set up integrations with external tools to help better manage incidents.
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -66,20 +66,6 @@ exports[`Alert integration settings form should match the default snapshot 1`] =
|
|||
|
||||
</gl-modal-stub>
|
||||
</gl-form-group-stub>
|
||||
|
||||
<gl-button-stub
|
||||
buttontextclasses=""
|
||||
category="primary"
|
||||
class="js-no-auto-disable"
|
||||
icon=""
|
||||
size="medium"
|
||||
type="submit"
|
||||
variant="success"
|
||||
>
|
||||
|
||||
Save changes
|
||||
|
||||
</gl-button-stub>
|
||||
</form>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { GlAlert, GlModal } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlAlert, GlModal, GlToggle } from '@gitlab/ui';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import PagerDutySettingsForm from '~/incidents_settings/components/pagerduty_form.vue';
|
||||
|
||||
|
@ -8,13 +8,13 @@ describe('Alert integration settings form', () => {
|
|||
const resetWebhookUrl = jest.fn();
|
||||
const service = { updateSettings: jest.fn().mockResolvedValue(), resetWebhookUrl };
|
||||
|
||||
const findForm = () => wrapper.find({ ref: 'settingsForm' });
|
||||
const findWebhookInput = () => wrapper.find('[data-testid="webhook-url"]');
|
||||
const findModal = () => wrapper.find(GlModal);
|
||||
const findAlert = () => wrapper.find(GlAlert);
|
||||
const findWebhookInput = () => wrapper.findByTestId('webhook-url');
|
||||
const findFormToggle = () => wrapper.findComponent(GlToggle);
|
||||
const findModal = () => wrapper.findComponent(GlModal);
|
||||
const findAlert = () => wrapper.findComponent(GlAlert);
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallowMount(PagerDutySettingsForm, {
|
||||
wrapper = shallowMountExtended(PagerDutySettingsForm, {
|
||||
provide: {
|
||||
service,
|
||||
pagerDutySettings: {
|
||||
|
@ -27,18 +27,15 @@ describe('Alert integration settings form', () => {
|
|||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (wrapper) {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
}
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('should match the default snapshot', () => {
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should call service `updateSettings` on form submit', () => {
|
||||
findForm().trigger('submit');
|
||||
it('should call service `updateSettings` on toggle change', () => {
|
||||
findFormToggle().vm.$emit('change', true);
|
||||
expect(service.updateSettings).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ pagerduty_active: wrapper.vm.active }),
|
||||
);
|
||||
|
|
|
@ -7,6 +7,8 @@ const TEST_MENU_ITEM = {
|
|||
icon: 'search',
|
||||
href: '/pretty/good/burger',
|
||||
view: 'burger-view',
|
||||
css_class: 'test-super-crazy test-class',
|
||||
data: { qa_selector: 'not-a-real-selector', method: 'post', testFoo: 'test' },
|
||||
};
|
||||
|
||||
describe('~/nav/components/top_nav_menu_item.vue', () => {
|
||||
|
@ -47,6 +49,22 @@ describe('~/nav/components/top_nav_menu_item.vue', () => {
|
|||
expect(button.text()).toBe(TEST_MENU_ITEM.title);
|
||||
});
|
||||
|
||||
it('renders button classes', () => {
|
||||
const button = findButton();
|
||||
|
||||
expect(button.classes()).toEqual(expect.arrayContaining(TEST_MENU_ITEM.css_class.split(' ')));
|
||||
});
|
||||
|
||||
it('renders button data attributes', () => {
|
||||
const button = findButton();
|
||||
|
||||
expect(button.attributes()).toMatchObject({
|
||||
'data-qa-selector': TEST_MENU_ITEM.data.qa_selector,
|
||||
'data-method': TEST_MENU_ITEM.data.method,
|
||||
'data-test-foo': TEST_MENU_ITEM.data.testFoo,
|
||||
});
|
||||
});
|
||||
|
||||
it('passes listeners to button', () => {
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ describe('operation settings external dashboard component', () => {
|
|||
|
||||
it('renders header text', () => {
|
||||
mountComponent();
|
||||
expect(wrapper.find('.js-section-header').text()).toBe('Metrics dashboard');
|
||||
expect(wrapper.find('.js-section-header').text()).toBe('Metrics');
|
||||
});
|
||||
|
||||
describe('expand/collapse button', () => {
|
||||
|
@ -77,13 +77,13 @@ describe('operation settings external dashboard component', () => {
|
|||
});
|
||||
|
||||
it('renders descriptive text', () => {
|
||||
expect(subHeader.text()).toContain('Manage Metrics Dashboard settings.');
|
||||
expect(subHeader.text()).toContain('Manage metrics dashboard settings.');
|
||||
});
|
||||
|
||||
it('renders help page link', () => {
|
||||
const link = subHeader.find(GlLink);
|
||||
|
||||
expect(link.text()).toBe('Learn more');
|
||||
expect(link.text()).toBe('Learn more.');
|
||||
expect(link.attributes().href).toBe(helpPage);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,8 +3,38 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Mutations::Issues::SetSubscription do
|
||||
it_behaves_like 'a subscribeable graphql resource' do
|
||||
let_it_be(:resource) { create(:issue) }
|
||||
let(:permission_name) { :update_issue }
|
||||
let_it_be_with_reload(:project) { create(:project) }
|
||||
let_it_be_with_reload(:resource) { create(:issue, project: project) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
specify { expect(described_class).to require_graphql_authorizations(:update_subscription) }
|
||||
|
||||
context 'when user does not have access to the project' do
|
||||
it_behaves_like 'a subscribeable not accessible graphql resource'
|
||||
end
|
||||
|
||||
context 'when user is developer member of the project' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it_behaves_like 'a subscribeable graphql resource'
|
||||
end
|
||||
|
||||
context 'when the project is public' do
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
|
||||
end
|
||||
|
||||
it_behaves_like 'a subscribeable graphql resource'
|
||||
end
|
||||
|
||||
context 'when the project is public but the issue is confidential' do
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
|
||||
resource.update!(confidential: true)
|
||||
end
|
||||
|
||||
it_behaves_like 'a subscribeable not accessible graphql resource'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,8 +3,30 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Mutations::MergeRequests::SetSubscription do
|
||||
it_behaves_like 'a subscribeable graphql resource' do
|
||||
let_it_be(:resource) { create(:merge_request) }
|
||||
let(:permission_name) { :update_merge_request }
|
||||
let_it_be_with_reload(:project) { create(:project) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
let(:resource) { create(:merge_request, source_project: project, target_project: project) }
|
||||
|
||||
specify { expect(described_class).to require_graphql_authorizations(:update_subscription) }
|
||||
|
||||
context 'when user does not have access to the project' do
|
||||
it_behaves_like 'a subscribeable not accessible graphql resource'
|
||||
end
|
||||
|
||||
context 'when user is developer member of the project' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it_behaves_like 'a subscribeable graphql resource'
|
||||
end
|
||||
|
||||
context 'when the project is public' do
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
|
||||
end
|
||||
|
||||
it_behaves_like 'a subscribeable graphql resource'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -424,7 +424,7 @@ RSpec.describe Nav::TopNavHelper do
|
|||
title: 'Leave Admin Mode',
|
||||
icon: 'lock-open',
|
||||
href: '/admin/session/destroy',
|
||||
method: :post
|
||||
data: { method: 'post' }
|
||||
)
|
||||
expect(subject[:secondary].last).to eq(expected_leave_admin_mode_item)
|
||||
end
|
||||
|
|
|
@ -18,24 +18,6 @@ RSpec.describe Gitlab::Instrumentation::RedisBase, :request_store do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.known_payload_keys' do
|
||||
it 'returns generated payload keys' do
|
||||
expect(instrumentation_class_a.known_payload_keys).to eq([:redis_instance_a_calls,
|
||||
:redis_instance_a_duration_s,
|
||||
:redis_instance_a_read_bytes,
|
||||
:redis_instance_a_write_bytes])
|
||||
end
|
||||
|
||||
it 'does not call calculation methods' do
|
||||
expect(instrumentation_class_a).not_to receive(:get_request_count)
|
||||
expect(instrumentation_class_a).not_to receive(:query_time)
|
||||
expect(instrumentation_class_a).not_to receive(:read_bytes)
|
||||
expect(instrumentation_class_a).not_to receive(:write_bytes)
|
||||
|
||||
instrumentation_class_a.known_payload_keys
|
||||
end
|
||||
end
|
||||
|
||||
describe '.payload' do
|
||||
it 'returns values that are higher than 0' do
|
||||
allow(instrumentation_class_a).to receive(:get_request_count) { 1 }
|
||||
|
|
|
@ -26,46 +26,6 @@ RSpec.describe Gitlab::Instrumentation::Redis do
|
|||
it_behaves_like 'aggregation of redis storage data', :read_bytes
|
||||
it_behaves_like 'aggregation of redis storage data', :write_bytes
|
||||
|
||||
describe '.known_payload_keys' do
|
||||
it 'returns all known payload keys' do
|
||||
expected_keys = [
|
||||
:redis_calls,
|
||||
:redis_duration_s,
|
||||
:redis_read_bytes,
|
||||
:redis_write_bytes,
|
||||
:redis_action_cable_calls,
|
||||
:redis_action_cable_duration_s,
|
||||
:redis_action_cable_read_bytes,
|
||||
:redis_action_cable_write_bytes,
|
||||
:redis_cache_calls,
|
||||
:redis_cache_duration_s,
|
||||
:redis_cache_read_bytes,
|
||||
:redis_cache_write_bytes,
|
||||
:redis_queues_calls,
|
||||
:redis_queues_duration_s,
|
||||
:redis_queues_read_bytes,
|
||||
:redis_queues_write_bytes,
|
||||
:redis_shared_state_calls,
|
||||
:redis_shared_state_duration_s,
|
||||
:redis_shared_state_read_bytes,
|
||||
:redis_shared_state_write_bytes
|
||||
]
|
||||
|
||||
expect(described_class.known_payload_keys).to eq(expected_keys)
|
||||
end
|
||||
|
||||
it 'does not call storage calculation methods' do
|
||||
described_class::STORAGES.each do |storage|
|
||||
expect(storage).not_to receive(:get_request_count)
|
||||
expect(storage).not_to receive(:query_time)
|
||||
expect(storage).not_to receive(:read_bytes)
|
||||
expect(storage).not_to receive(:write_bytes)
|
||||
end
|
||||
|
||||
described_class.known_payload_keys
|
||||
end
|
||||
end
|
||||
|
||||
describe '.payload', :request_store do
|
||||
before do
|
||||
Gitlab::Redis::Cache.with { |redis| redis.set('cache-test', 321) }
|
||||
|
|
|
@ -11,7 +11,6 @@ RSpec.describe ::Gitlab::Nav::TopNavMenuItem do
|
|||
active: true,
|
||||
icon: 'icon',
|
||||
href: 'href',
|
||||
method: 'method',
|
||||
view: 'view',
|
||||
css_class: 'css_class',
|
||||
data: {}
|
||||
|
|
|
@ -24,58 +24,10 @@ RSpec.describe Gitlab::SidekiqMiddleware::InstrumentationLogger do
|
|||
stub_const('TestWorker', worker)
|
||||
end
|
||||
|
||||
describe '.keys' do
|
||||
it 'returns all available payload keys' do
|
||||
expected_keys = [
|
||||
:cpu_s,
|
||||
:gitaly_calls,
|
||||
:gitaly_duration_s,
|
||||
:rugged_calls,
|
||||
:rugged_duration_s,
|
||||
:elasticsearch_calls,
|
||||
:elasticsearch_duration_s,
|
||||
:elasticsearch_timed_out_count,
|
||||
:mem_objects,
|
||||
:mem_bytes,
|
||||
:mem_mallocs,
|
||||
:redis_calls,
|
||||
:redis_duration_s,
|
||||
:redis_read_bytes,
|
||||
:redis_write_bytes,
|
||||
:redis_action_cable_calls,
|
||||
:redis_action_cable_duration_s,
|
||||
:redis_action_cable_read_bytes,
|
||||
:redis_action_cable_write_bytes,
|
||||
:redis_cache_calls,
|
||||
:redis_cache_duration_s,
|
||||
:redis_cache_read_bytes,
|
||||
:redis_cache_write_bytes,
|
||||
:redis_queues_calls,
|
||||
:redis_queues_duration_s,
|
||||
:redis_queues_read_bytes,
|
||||
:redis_queues_write_bytes,
|
||||
:redis_shared_state_calls,
|
||||
:redis_shared_state_duration_s,
|
||||
:redis_shared_state_read_bytes,
|
||||
:redis_shared_state_write_bytes,
|
||||
:db_count,
|
||||
:db_write_count,
|
||||
:db_cached_count,
|
||||
:external_http_count,
|
||||
:external_http_duration_s,
|
||||
:rack_attack_redis_count,
|
||||
:rack_attack_redis_duration_s
|
||||
]
|
||||
|
||||
expect(described_class.keys).to include(*expected_keys)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#call', :request_store do
|
||||
let(:instrumentation_values) do
|
||||
{
|
||||
cpu_s: 10,
|
||||
unknown_attribute: 123,
|
||||
db_count: 0,
|
||||
db_cached_count: 0,
|
||||
db_write_count: 0,
|
||||
|
@ -90,12 +42,10 @@ RSpec.describe Gitlab::SidekiqMiddleware::InstrumentationLogger do
|
|||
end
|
||||
end
|
||||
|
||||
it 'merges correct instrumentation data in the job' do
|
||||
it 'merges all instrumentation data in the job' do
|
||||
expect { |b| subject.call(worker, job, queue, &b) }.to yield_control
|
||||
|
||||
expected_values = instrumentation_values.except(:unknown_attribute)
|
||||
|
||||
expect(job[:instrumentation]).to eq(expected_values)
|
||||
expect(job[:instrumentation]).to eq(instrumentation_values)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -594,6 +594,18 @@ RSpec.describe Member do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when called with a known user secondary email' do
|
||||
let(:secondary_email) { create(:email, email: 'secondary@example.com', user: user) }
|
||||
|
||||
it 'adds the user as a member' do
|
||||
expect(source.users).not_to include(user)
|
||||
|
||||
described_class.add_user(source, secondary_email.email, :maintainer)
|
||||
|
||||
expect(source.users.reload).to include(user)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when called with an unknown user email' do
|
||||
it 'creates an invited member' do
|
||||
expect(source.users).not_to include(user)
|
||||
|
|
|
@ -139,13 +139,14 @@ RSpec.describe IssuePolicy do
|
|||
create(:project_group_link, group: group, project: project)
|
||||
end
|
||||
|
||||
it 'does not allow guest to create todos' do
|
||||
it 'does not allow anonymous user to create todos' do
|
||||
expect(permissions(nil, issue)).to be_allowed(:read_issue)
|
||||
expect(permissions(nil, issue)).to be_disallowed(:create_todo)
|
||||
expect(permissions(nil, issue)).to be_disallowed(:update_subscription)
|
||||
end
|
||||
|
||||
it 'allows guests to read issues' do
|
||||
expect(permissions(guest, issue)).to be_allowed(:read_issue, :read_issue_iid, :create_todo)
|
||||
expect(permissions(guest, issue)).to be_allowed(:read_issue, :read_issue_iid, :create_todo, :update_subscription)
|
||||
expect(permissions(guest, issue)).to be_disallowed(:update_issue, :admin_issue, :reopen_issue)
|
||||
|
||||
expect(permissions(guest, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid)
|
||||
|
@ -205,12 +206,18 @@ RSpec.describe IssuePolicy do
|
|||
it 'forbids visitors from commenting' do
|
||||
expect(permissions(visitor, issue)).to be_disallowed(:create_note)
|
||||
end
|
||||
it 'forbids visitors from subscribing' do
|
||||
expect(permissions(visitor, issue)).to be_disallowed(:update_subscription)
|
||||
end
|
||||
it 'allows guests to view' do
|
||||
expect(permissions(guest, issue)).to be_allowed(:read_issue)
|
||||
end
|
||||
it 'allows guests to comment' do
|
||||
expect(permissions(guest, issue)).to be_allowed(:create_note)
|
||||
end
|
||||
it 'allows guests to subscribe' do
|
||||
expect(permissions(guest, issue)).to be_allowed(:update_subscription)
|
||||
end
|
||||
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it 'allows admins to view' do
|
||||
|
|
|
@ -26,7 +26,8 @@ RSpec.describe MergeRequestPolicy do
|
|||
read_merge_request
|
||||
create_todo
|
||||
approve_merge_request
|
||||
create_note].freeze
|
||||
create_note
|
||||
update_subscription].freeze
|
||||
|
||||
shared_examples_for 'a denied user' do
|
||||
let(:perms) { permissions(subject, merge_request) }
|
||||
|
@ -55,7 +56,7 @@ RSpec.describe MergeRequestPolicy do
|
|||
subject { permissions(nil, merge_request) }
|
||||
|
||||
it do
|
||||
is_expected.to be_disallowed(:create_todo)
|
||||
is_expected.to be_disallowed(:create_todo, :update_subscription)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Members::InviteService, :aggregate_failures, :clean_gitlab_redis_shared_state, :sidekiq_inline do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:project, reload: true) { create(:project) }
|
||||
let_it_be(:user) { project.owner }
|
||||
let_it_be(:project_user) { create(:user) }
|
||||
let_it_be(:namespace) { project.namespace }
|
||||
|
@ -23,6 +23,18 @@ RSpec.describe Members::InviteService, :aggregate_failures, :clean_gitlab_redis_
|
|||
it_behaves_like 'records an onboarding progress action', :user_added
|
||||
end
|
||||
|
||||
context 'when email belongs to an existing user as a secondary email' do
|
||||
let(:secondary_email) { create(:email, email: 'secondary@example.com', user: project_user) }
|
||||
let(:params) { { email: secondary_email.email } }
|
||||
|
||||
it 'adds an existing user to members', :aggregate_failures do
|
||||
expect_to_create_members(count: 1)
|
||||
expect(result[:status]).to eq(:success)
|
||||
expect(project.users).to include project_user
|
||||
expect(project.members.last).not_to be_invite
|
||||
end
|
||||
end
|
||||
|
||||
context 'when email is not a valid email' do
|
||||
let(:params) { { email: '_bogus_' } }
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state do
|
|||
let(:opts) do
|
||||
{
|
||||
title: 'Awesome merge_request',
|
||||
description: "well this is not done yet\n/wip",
|
||||
description: "well this is not done yet\n/draft",
|
||||
source_branch: 'feature',
|
||||
target_branch: 'master',
|
||||
assignees: [user2]
|
||||
|
|
|
@ -307,7 +307,7 @@ RSpec.describe Notes::CreateService do
|
|||
),
|
||||
# Set WIP status
|
||||
QuickAction.new(
|
||||
action_text: "/wip",
|
||||
action_text: "/draft",
|
||||
before_action: -> {
|
||||
issuable.reload.update!(title: "title")
|
||||
},
|
||||
|
@ -317,7 +317,7 @@ RSpec.describe Notes::CreateService do
|
|||
),
|
||||
# Remove WIP status
|
||||
QuickAction.new(
|
||||
action_text: "/wip",
|
||||
action_text: "/draft",
|
||||
before_action: -> {
|
||||
issuable.reload.update!(title: "WIP: title")
|
||||
},
|
||||
|
|
|
@ -1201,16 +1201,6 @@ RSpec.describe QuickActions::InterpretService do
|
|||
let(:issuable) { issue }
|
||||
end
|
||||
|
||||
it_behaves_like 'draft command' do
|
||||
let(:content) { '/wip' }
|
||||
let(:issuable) { merge_request }
|
||||
end
|
||||
|
||||
it_behaves_like 'undraft command' do
|
||||
let(:content) { '/wip' }
|
||||
let(:issuable) { merge_request }
|
||||
end
|
||||
|
||||
it_behaves_like 'draft command' do
|
||||
let(:content) { '/draft' }
|
||||
let(:issuable) { merge_request }
|
||||
|
|
|
@ -2,44 +2,37 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.shared_examples 'a subscribeable not accessible graphql resource' do
|
||||
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
|
||||
|
||||
subject { mutation.resolve(project_path: resource.project.full_path, iid: resource.iid, subscribed_state: true) }
|
||||
|
||||
it 'raises an error if the resource is not accessible to the user' do
|
||||
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'a subscribeable graphql resource' do
|
||||
let(:project) { resource.project }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let(:mutated_resource) { subject[resource.class.name.underscore.to_sym] }
|
||||
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
|
||||
let(:subscribe) { true }
|
||||
|
||||
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
|
||||
subject { mutation.resolve(project_path: resource.project.full_path, iid: resource.iid, subscribed_state: subscribe) }
|
||||
|
||||
specify { expect(described_class).to require_graphql_authorizations(permission_name) }
|
||||
it 'subscribes to the resource' do
|
||||
expect(mutated_resource).to eq(resource)
|
||||
expect(mutated_resource.subscribed?(user, project)).to eq(true)
|
||||
expect(subject[:errors]).to be_empty
|
||||
end
|
||||
|
||||
describe '#resolve' do
|
||||
let(:subscribe) { true }
|
||||
let(:mutated_resource) { subject[resource.class.name.underscore.to_sym] }
|
||||
context 'when passing subscribe as false' do
|
||||
let(:subscribe) { false }
|
||||
|
||||
subject { mutation.resolve(project_path: resource.project.full_path, iid: resource.iid, subscribed_state: subscribe) }
|
||||
it 'unsubscribes from the discussion' do
|
||||
resource.subscribe(user, project)
|
||||
|
||||
it 'raises an error if the resource is not accessible to the user' do
|
||||
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
|
||||
end
|
||||
|
||||
context 'when the user can update the resource' do
|
||||
before do
|
||||
resource.project.add_developer(user)
|
||||
end
|
||||
|
||||
it 'subscribes to the resource' do
|
||||
expect(mutated_resource).to eq(resource)
|
||||
expect(mutated_resource.subscribed?(user, project)).to eq(true)
|
||||
expect(subject[:errors]).to be_empty
|
||||
end
|
||||
|
||||
context 'when passing subscribe as false' do
|
||||
let(:subscribe) { false }
|
||||
|
||||
it 'unsubscribes from the discussion' do
|
||||
resource.subscribe(user, project)
|
||||
|
||||
expect(mutated_resource.subscribed?(user, project)).to eq(false)
|
||||
end
|
||||
end
|
||||
expect(mutated_resource.subscribed?(user, project)).to eq(false)
|
||||
expect(subject[:errors]).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -36,7 +36,7 @@ RSpec.describe 'projects/settings/operations/show' do
|
|||
it 'renders the Operations Settings page' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_content _('Alert integrations')
|
||||
expect(rendered).to have_content _('Alerts')
|
||||
expect(rendered).to have_content _('Display alerts from all configured monitoring tools.')
|
||||
end
|
||||
end
|
||||
|
@ -77,41 +77,11 @@ RSpec.describe 'projects/settings/operations/show' do
|
|||
end
|
||||
|
||||
describe 'Operations > Tracing' do
|
||||
context 'with project.tracing_external_url' do
|
||||
it 'links to project.tracing_external_url' do
|
||||
context 'Settings page ' do
|
||||
it 'renders the Tracing Settings page' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_link('Tracing', href: tracing_setting.external_url)
|
||||
end
|
||||
|
||||
context 'with malicious external_url' do
|
||||
let(:malicious_tracing_url) { "https://replaceme.com/'><script>alert(document.cookie)</script>" }
|
||||
let(:cleaned_url) { "https://replaceme.com/'>" }
|
||||
|
||||
before do
|
||||
tracing_setting.update_column(:external_url, malicious_tracing_url)
|
||||
end
|
||||
|
||||
it 'sanitizes external_url' do
|
||||
render
|
||||
|
||||
expect(tracing_setting.external_url).to eq(malicious_tracing_url)
|
||||
expect(rendered).to have_link('Tracing', href: cleaned_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without project.tracing_external_url' do
|
||||
let(:tracing_setting) { build(:project_tracing_setting, project: project) }
|
||||
|
||||
before do
|
||||
tracing_setting.external_url = nil
|
||||
end
|
||||
|
||||
it 'links to Tracing page' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_link('Tracing', href: project_tracing_path(project))
|
||||
expect(rendered).to have_content _('Embed an image of your existing Jaeger server in GitLab.')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue