Commit 3abef158 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents d838e6c3 82f32b6f
...@@ -124,7 +124,7 @@ export default { ...@@ -124,7 +124,7 @@ export default {
type="submit" type="submit"
class="js-no-auto-disable" class="js-no-auto-disable"
category="primary" category="primary"
variant="success" variant="confirm"
:disabled="submitDisabled" :disabled="submitDisabled"
:loading="isSaving" :loading="isSaving"
> >
......
# frozen_string_literal: true
module Mutations
module ReleaseAssetLinks
class Delete < BaseMutation
graphql_name 'ReleaseAssetLinkDelete'
authorize :destroy_release
ReleaseAssetLinkID = ::Types::GlobalIDType[::Releases::Link]
argument :id, ReleaseAssetLinkID,
required: true,
description: 'ID of the release asset link to delete.'
field :link,
Types::ReleaseAssetLinkType,
null: true,
description: 'The deleted release asset link.'
def resolve(id:)
link = authorized_find!(id)
unless link.destroy
return { link: nil, errors: link.errors.full_messages }
end
{ link: link, errors: [] }
end
def find_object(id)
# TODO: remove this line when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
id = ReleaseAssetLinkID.coerce_isolated_input(id)
GitlabSchema.find_by_gid(id)
end
end
end
end
...@@ -68,6 +68,7 @@ module Types ...@@ -68,6 +68,7 @@ module Types
mount_mutation Mutations::Releases::Delete mount_mutation Mutations::Releases::Delete
mount_mutation Mutations::ReleaseAssetLinks::Create mount_mutation Mutations::ReleaseAssetLinks::Create
mount_mutation Mutations::ReleaseAssetLinks::Update mount_mutation Mutations::ReleaseAssetLinks::Update
mount_mutation Mutations::ReleaseAssetLinks::Delete
mount_mutation Mutations::Terraform::State::Delete mount_mutation Mutations::Terraform::State::Delete
mount_mutation Mutations::Terraform::State::Lock mount_mutation Mutations::Terraform::State::Lock
mount_mutation Mutations::Terraform::State::Unlock mount_mutation Mutations::Terraform::State::Unlock
......
...@@ -39,5 +39,5 @@ ...@@ -39,5 +39,5 @@
= f.check_box :active, required: false, value: @schedule.active? = f.check_box :active, required: false, value: @schedule.active?
= f.label :active, _('Active'), class: 'gl-font-weight-normal' = f.label :active, _('Active'), class: 'gl-font-weight-normal'
.footer-block.row-content-block .footer-block.row-content-block
= f.submit _('Save pipeline schedule'), class: 'btn gl-button btn-success' = f.submit _('Save pipeline schedule'), class: 'btn gl-button btn-confirm'
= link_to _('Cancel'), pipeline_schedules_path(@project), class: 'btn gl-button btn-default btn-cancel' = link_to _('Cancel'), pipeline_schedules_path(@project), class: 'btn gl-button btn-default btn-cancel'
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
- if can?(current_user, :create_pipeline_schedule, @project) - if can?(current_user, :create_pipeline_schedule, @project)
.nav-controls .nav-controls
= link_to new_project_pipeline_schedule_path(@project), class: 'btn gl-button btn-success' do = link_to new_project_pipeline_schedule_path(@project), class: 'btn gl-button btn-confirm' do
%span= _('New schedule') %span= _('New schedule')
- if @schedules.present? - if @schedules.present?
......
---
title: Move to confirm varient from success in pipeline_editor directory
merge_request: 56200
author: Yogi (@yo)
type: changed
---
title: Move from btn-success to btn-confirm in pipeline_schedules directory
merge_request: 56201
author: Yogi (@yo)
type: changed
---
title: Add GraphQL mutation to delete an existing release asset link
merge_request: 56417
author:
type: added
---
title: Update android template to default branch
merge_request: 56738
author:
type: other
...@@ -5048,6 +5048,16 @@ Autogenerated return type of ReleaseAssetLinkCreate. ...@@ -5048,6 +5048,16 @@ Autogenerated return type of ReleaseAssetLinkCreate.
| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| `link` | [`ReleaseAssetLink`](#releaseassetlink) | The asset link after mutation. | | `link` | [`ReleaseAssetLink`](#releaseassetlink) | The asset link after mutation. |
### `ReleaseAssetLinkDeletePayload`
Autogenerated return type of ReleaseAssetLinkDelete.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| `link` | [`ReleaseAssetLink`](#releaseassetlink) | The deleted release asset link. |
### `ReleaseAssetLinkEdge` ### `ReleaseAssetLinkEdge`
An edge in a connection. An edge in a connection.
......
...@@ -957,7 +957,10 @@ Example aggregated metric entries: ...@@ -957,7 +957,10 @@ Example aggregated metric entries:
```yaml ```yaml
- name: example_metrics_union - name: example_metrics_union
operator: OR operator: OR
events: ['i_search_total', 'i_search_advanced', 'i_search_paid'] events:
- 'i_search_total'
- 'i_search_advanced'
- 'i_search_paid'
source: redis source: redis
time_frame: time_frame:
- 7d - 7d
...@@ -968,7 +971,9 @@ Example aggregated metric entries: ...@@ -968,7 +971,9 @@ Example aggregated metric entries:
time_frame: time_frame:
- 28d - 28d
- all - all
events: ['dependency_scanning_pipeline_all_time', 'container_scanning_pipeline_all_time'] events:
- 'dependency_scanning_pipeline_all_time'
- 'container_scanning_pipeline_all_time'
feature_flag: example_aggregated_metric feature_flag: example_aggregated_metric
``` ```
...@@ -1099,7 +1104,9 @@ Example definition: ...@@ -1099,7 +1104,9 @@ Example definition:
- name: example_metrics_intersection_database_sourced - name: example_metrics_intersection_database_sourced
operator: AND operator: AND
source: database source: database
events: ['dependency_scanning_pipeline', 'container_scanning_pipeline'] events:
- 'dependency_scanning_pipeline'
- 'container_scanning_pipeline'
time_frame: time_frame:
- 28d - 28d
- all - all
......
...@@ -19,11 +19,8 @@ export default { ...@@ -19,11 +19,8 @@ export default {
GlLoadingIcon, GlLoadingIcon,
VulnerabilitiesCountList, VulnerabilitiesCountList,
}, },
inject: ['groupFullPath'],
props: { props: {
groupFullPath: {
type: String,
required: true,
},
vulnerabilitiesExportEndpoint: { vulnerabilitiesExportEndpoint: {
type: String, type: String,
required: true, required: true,
...@@ -88,7 +85,7 @@ export default { ...@@ -88,7 +85,7 @@ export default {
<template #sticky> <template #sticky>
<filters :projects="projects" @filterChange="handleFilterChange" /> <filters :projects="projects" @filterChange="handleFilterChange" />
</template> </template>
<group-security-vulnerabilities :group-full-path="groupFullPath" :filters="filters" /> <group-security-vulnerabilities :filters="filters" />
</security-dashboard-layout> </security-dashboard-layout>
</div> </div>
</template> </template>
...@@ -13,11 +13,8 @@ export default { ...@@ -13,11 +13,8 @@ export default {
GlIntersectionObserver, GlIntersectionObserver,
VulnerabilityList, VulnerabilityList,
}, },
inject: ['groupFullPath'],
props: { props: {
groupFullPath: {
type: String,
required: true,
},
filters: { filters: {
type: Object, type: Object,
required: false, required: false,
......
...@@ -25,9 +25,9 @@ export default { ...@@ -25,9 +25,9 @@ export default {
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
}, },
inject: ['groupFullPath'],
props: { props: {
query: { type: Object, required: true }, query: { type: Object, required: true },
groupFullPath: { type: String, required: false, default: undefined },
}, },
data() { data() {
return { return {
......
...@@ -32,6 +32,7 @@ export default { ...@@ -32,6 +32,7 @@ export default {
directives: { directives: {
'gl-tooltip': GlTooltipDirective, 'gl-tooltip': GlTooltipDirective,
}, },
inject: ['groupFullPath'],
props: { props: {
helpPagePath: { helpPagePath: {
type: String, type: String,
...@@ -42,11 +43,6 @@ export default { ...@@ -42,11 +43,6 @@ export default {
type: Object, type: Object,
required: true, required: true,
}, },
groupFullPath: {
type: String,
required: false,
default: undefined,
},
}, },
data() { data() {
return { return {
......
...@@ -18,12 +18,7 @@ export default { ...@@ -18,12 +18,7 @@ export default {
VulnerabilitySeverities, VulnerabilitySeverities,
VulnerabilityChart, VulnerabilityChart,
}, },
props: { inject: ['groupFullPath'],
groupFullPath: {
type: String,
required: true,
},
},
apollo: { apollo: {
projects: { projects: {
query: vulnerableProjectsQuery, query: vulnerableProjectsQuery,
...@@ -65,11 +60,8 @@ export default { ...@@ -65,11 +60,8 @@ export default {
<dashboard-not-configured /> <dashboard-not-configured />
</template> </template>
<template v-else-if="shouldShowCharts" #default> <template v-else-if="shouldShowCharts" #default>
<vulnerability-chart :query="vulnerabilityHistoryQuery" :group-full-path="groupFullPath" /> <vulnerability-chart :query="vulnerabilityHistoryQuery" />
<vulnerability-severities <vulnerability-severities :query="vulnerabilityGradesQuery" />
:query="vulnerabilityGradesQuery"
:group-full-path="groupFullPath"
/>
</template> </template>
<template v-else #loading> <template v-else #loading>
<gl-loading-icon size="lg" class="gl-mt-6" /> <gl-loading-icon size="lg" class="gl-mt-6" />
......
...@@ -61,6 +61,7 @@ export default (el, dashboardType) => { ...@@ -61,6 +61,7 @@ export default (el, dashboardType) => {
emptyStateSvgPath, emptyStateSvgPath,
notEnabledScannersHelpPath, notEnabledScannersHelpPath,
noPipelineRunScannersHelpPath, noPipelineRunScannersHelpPath,
groupFullPath,
securityConfigurationPath, securityConfigurationPath,
hasVulnerabilities: parseBoolean(hasVulnerabilities), hasVulnerabilities: parseBoolean(hasVulnerabilities),
scanners: scanners ? JSON.parse(scanners) : [], scanners: scanners ? JSON.parse(scanners) : [],
...@@ -92,7 +93,6 @@ export default (el, dashboardType) => { ...@@ -92,7 +93,6 @@ export default (el, dashboardType) => {
provide.autoFixMrsPath = autoFixMrsPath; provide.autoFixMrsPath = autoFixMrsPath;
} else if (dashboardType === DASHBOARD_TYPES.GROUP) { } else if (dashboardType === DASHBOARD_TYPES.GROUP) {
component = FirstClassGroupSecurityDashboard; component = FirstClassGroupSecurityDashboard;
props.groupFullPath = groupFullPath;
} else if (dashboardType === DASHBOARD_TYPES.INSTANCE) { } else if (dashboardType === DASHBOARD_TYPES.INSTANCE) {
provide.instanceDashboardSettingsPath = instanceDashboardSettingsPath; provide.instanceDashboardSettingsPath = instanceDashboardSettingsPath;
component = FirstClassInstanceSecurityDashboard; component = FirstClassInstanceSecurityDashboard;
......
...@@ -32,6 +32,7 @@ export default (el, dashboardType) => { ...@@ -32,6 +32,7 @@ export default (el, dashboardType) => {
const provide = { const provide = {
dashboardDocumentation: el.dataset.dashboardDocumentation, dashboardDocumentation: el.dataset.dashboardDocumentation,
emptyStateSvgPath: el.dataset.emptyStateSvgPath, emptyStateSvgPath: el.dataset.emptyStateSvgPath,
groupFullPath: el.dataset.groupFullPath,
securityConfigurationPath: el.dataset.securityConfigurationPath, securityConfigurationPath: el.dataset.securityConfigurationPath,
}; };
......
---
title: Use provide/inject for groupFullPath instead of props
merge_request: 56472
author:
type: other
...@@ -17,5 +17,7 @@ ...@@ -17,5 +17,7 @@
- name: product_analytics_test_metrics_intersection_database_sourced - name: product_analytics_test_metrics_intersection_database_sourced
source: database source: database
time_frame: [28d] time_frame: [28d]
events: ['dependency_scanning_pipeline', 'container_scanning_pipeline'] events:
- 'dependency_scanning_pipeline'
- 'container_scanning_pipeline'
operator: AND operator: AND
...@@ -29,9 +29,9 @@ describe('First Class Group Dashboard Component', () => { ...@@ -29,9 +29,9 @@ describe('First Class Group Dashboard Component', () => {
propsData: { propsData: {
dashboardDocumentation, dashboardDocumentation,
emptyStateSvgPath, emptyStateSvgPath,
groupFullPath,
vulnerabilitiesExportEndpoint, vulnerabilitiesExportEndpoint,
}, },
provide: { groupFullPath },
data, data,
stubs: { stubs: {
SecurityDashboardLayout, SecurityDashboardLayout,
...@@ -66,7 +66,6 @@ describe('First Class Group Dashboard Component', () => { ...@@ -66,7 +66,6 @@ describe('First Class Group Dashboard Component', () => {
it('should render correctly', () => { it('should render correctly', () => {
expect(findGroupVulnerabilities().props()).toEqual({ expect(findGroupVulnerabilities().props()).toEqual({
groupFullPath,
filters: {}, filters: {},
}); });
}); });
......
...@@ -24,15 +24,13 @@ describe('First Class Group Dashboard Vulnerabilities Component', () => { ...@@ -24,15 +24,13 @@ describe('First Class Group Dashboard Vulnerabilities Component', () => {
const createWrapper = ({ $apollo = apolloMock, stubs } = {}) => { const createWrapper = ({ $apollo = apolloMock, stubs } = {}) => {
return shallowMount(FirstClassGroupVulnerabilities, { return shallowMount(FirstClassGroupVulnerabilities, {
propsData: {
groupFullPath,
},
stubs, stubs,
mocks: { mocks: {
$apollo, $apollo,
fetchNextPage: () => {}, fetchNextPage: () => {},
}, },
provide: { provide: {
groupFullPath,
hasVulnerabilities: true, hasVulnerabilities: true,
hasJiraVulnerabilitiesIntegrationEnabled: false, hasJiraVulnerabilitiesIntegrationEnabled: false,
}, },
......
...@@ -21,10 +21,11 @@ describe('First class vulnerability chart component', () => { ...@@ -21,10 +21,11 @@ describe('First class vulnerability chart component', () => {
const findActiveChartButton = () => findChartButtons().find('.selected'); const findActiveChartButton = () => findChartButtons().find('.selected');
const find90DaysChartButton = () => findChartButtons().find('[data-days="90"]'); const find90DaysChartButton = () => findChartButtons().find('[data-days="90"]');
const createComponent = ({ $apollo, propsData, stubs, data } = {}) => { const createComponent = ({ $apollo, propsData, stubs, data, provide } = {}) => {
const instance = shallowMount(VulnerabilityChart, { const instance = shallowMount(VulnerabilityChart, {
$apollo, $apollo,
propsData: { query: {}, ...propsData }, propsData: { query: {}, ...propsData },
provide: { groupFullPath: undefined, ...provide },
stubs: { stubs: {
...stubChildren(VulnerabilityChart), ...stubChildren(VulnerabilityChart),
...stubs, ...stubs,
...@@ -84,7 +85,7 @@ describe('First class vulnerability chart component', () => { ...@@ -84,7 +85,7 @@ describe('First class vulnerability chart component', () => {
describe('when loading the history chart for group level dashboard', () => { describe('when loading the history chart for group level dashboard', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ wrapper = createComponent({
propsData: { groupFullPath: 'gitlab-org' }, provide: { groupFullPath: 'gitlab-org' },
$apollo: { $apollo: {
queries: { vulnerabilitiesHistory: { group: responseData } }, queries: { vulnerabilitiesHistory: { group: responseData } },
}, },
......
...@@ -33,7 +33,7 @@ describe('Vulnerability Severity component', () => { ...@@ -33,7 +33,7 @@ describe('Vulnerability Severity component', () => {
return createMockApollo([...queries]); return createMockApollo([...queries]);
}; };
const createComponent = ({ propsData, data, apolloProvider }) => { const createComponent = ({ propsData, data, apolloProvider, provide }) => {
return shallowMount(VulnerabilitySeverity, { return shallowMount(VulnerabilitySeverity, {
localVue, localVue,
apolloProvider, apolloProvider,
...@@ -42,6 +42,7 @@ describe('Vulnerability Severity component', () => { ...@@ -42,6 +42,7 @@ describe('Vulnerability Severity component', () => {
helpPagePath, helpPagePath,
...propsData, ...propsData,
}, },
provide: { groupFullPath: undefined, ...provide },
stubs: { stubs: {
Accordion, Accordion,
AccordionItem, AccordionItem,
...@@ -69,7 +70,8 @@ describe('Vulnerability Severity component', () => { ...@@ -69,7 +70,8 @@ describe('Vulnerability Severity component', () => {
]); ]);
wrapper = createComponent({ wrapper = createComponent({
propsData: { groupFullPath: 'gitlab-org', query: groupVulnerabilityGradesQuery }, propsData: { query: groupVulnerabilityGradesQuery },
provide: { groupFullPath: 'gitlab-org' },
apolloProvider, apolloProvider,
}); });
......
...@@ -41,7 +41,7 @@ describe('Group Security Charts component', () => { ...@@ -41,7 +41,7 @@ describe('Group Security Charts component', () => {
}, },
}, },
}, },
propsData: { groupFullPath }, provide: { groupFullPath },
stubs: { stubs: {
SecurityChartsLayout, SecurityChartsLayout,
}, },
...@@ -100,11 +100,10 @@ describe('Group Security Charts component', () => { ...@@ -100,11 +100,10 @@ describe('Group Security Charts component', () => {
expect(dashboardNotConfigured.exists()).toBe(false); expect(dashboardNotConfigured.exists()).toBe(false);
expect(loadingIcon.exists()).toBe(false); expect(loadingIcon.exists()).toBe(false);
expect(vulnerabilityChart.exists()).toBe(true); expect(vulnerabilityChart.exists()).toBe(true);
expect(vulnerabilityChart.props()).toEqual({ query: vulnerabilityHistoryQuery, groupFullPath }); expect(vulnerabilityChart.props()).toEqual({ query: vulnerabilityHistoryQuery });
expect(vulnerabilitySeverities.exists()).toBe(true); expect(vulnerabilitySeverities.exists()).toBe(true);
expect(vulnerabilitySeverities.props()).toEqual({ expect(vulnerabilitySeverities.props()).toEqual({
query: vulnerabilityGradesQuery, query: vulnerabilityGradesQuery,
groupFullPath,
helpPagePath: '', helpPagePath: '',
}); });
}); });
......
...@@ -113,9 +113,10 @@ promoteBeta: ...@@ -113,9 +113,10 @@ promoteBeta:
promoteProduction: promoteProduction:
extends: .promote_job extends: .promote_job
stage: production stage: production
# We only allow production promotion on `master` because # We only allow production promotion on the default branch because
# it has its own production scoped secret variables # it has its own production scoped secret variables.
only: only:
- master variables:
- $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script: script:
- bundle exec fastlane promote_beta_to_production - bundle exec fastlane promote_beta_to_production
...@@ -12,97 +12,94 @@ ...@@ -12,97 +12,94 @@
feature_flag: usage_data_code_review_aggregation feature_flag: usage_data_code_review_aggregation
source: redis source: redis
time_frame: [7d, 28d] time_frame: [7d, 28d]
events: [ events:
'i_code_review_user_single_file_diffs', - 'i_code_review_user_single_file_diffs'
'i_code_review_user_create_mr', - 'i_code_review_user_create_mr'
'i_code_review_user_close_mr', - 'i_code_review_user_close_mr'
'i_code_review_user_reopen_mr', - 'i_code_review_user_reopen_mr'
'i_code_review_user_resolve_thread', - 'i_code_review_user_resolve_thread'
'i_code_review_user_unresolve_thread', - 'i_code_review_user_unresolve_thread'
'i_code_review_edit_mr_title', - 'i_code_review_edit_mr_title'
'i_code_review_edit_mr_desc', - 'i_code_review_edit_mr_desc'
'i_code_review_user_merge_mr', - 'i_code_review_user_merge_mr'
'i_code_review_user_create_mr_comment', - 'i_code_review_user_create_mr_comment'
'i_code_review_user_edit_mr_comment', - 'i_code_review_user_edit_mr_comment'
'i_code_review_user_remove_mr_comment', - 'i_code_review_user_remove_mr_comment'
'i_code_review_user_create_review_note', - 'i_code_review_user_create_review_note'
'i_code_review_user_publish_review', - 'i_code_review_user_publish_review'
'i_code_review_user_create_multiline_mr_comment', - 'i_code_review_user_create_multiline_mr_comment'
'i_code_review_user_edit_multiline_mr_comment', - 'i_code_review_user_edit_multiline_mr_comment'
'i_code_review_user_remove_multiline_mr_comment', - 'i_code_review_user_remove_multiline_mr_comment'
'i_code_review_user_add_suggestion', - 'i_code_review_user_add_suggestion'
'i_code_review_user_apply_suggestion', - 'i_code_review_user_apply_suggestion'
'i_code_review_user_assigned', - 'i_code_review_user_assigned'
'i_code_review_user_review_requested', - 'i_code_review_user_review_requested'
'i_code_review_user_approve_mr', - 'i_code_review_user_approve_mr'
'i_code_review_user_unapprove_mr', - 'i_code_review_user_unapprove_mr'
'i_code_review_user_marked_as_draft', - 'i_code_review_user_marked_as_draft'
'i_code_review_user_unmarked_as_draft', - 'i_code_review_user_unmarked_as_draft'
'i_code_review_user_approval_rule_added', - 'i_code_review_user_approval_rule_added'
'i_code_review_user_approval_rule_deleted', - 'i_code_review_user_approval_rule_deleted'
'i_code_review_user_approval_rule_edited', - 'i_code_review_user_approval_rule_edited'
'i_code_review_user_vs_code_api_request', - 'i_code_review_user_vs_code_api_request'
'i_code_review_user_toggled_task_item_status', - 'i_code_review_user_toggled_task_item_status'
'i_code_review_user_create_mr_from_issue', - 'i_code_review_user_create_mr_from_issue'
'i_code_review_user_mr_discussion_locked', - 'i_code_review_user_mr_discussion_locked'
'i_code_review_user_mr_discussion_unlocked', - 'i_code_review_user_mr_discussion_unlocked'
'i_code_review_user_time_estimate_changed', - 'i_code_review_user_time_estimate_changed'
'i_code_review_user_time_spent_changed', - 'i_code_review_user_time_spent_changed'
'i_code_review_user_assignees_changed', - 'i_code_review_user_assignees_changed'
'i_code_review_user_reviewers_changed', - 'i_code_review_user_reviewers_changed'
'i_code_review_user_milestone_changed', - 'i_code_review_user_milestone_changed'
'i_code_review_user_labels_changed' - 'i_code_review_user_labels_changed'
]
- name: code_review_category_monthly_active_users - name: code_review_category_monthly_active_users
operator: OR operator: OR
feature_flag: usage_data_code_review_aggregation feature_flag: usage_data_code_review_aggregation
source: redis source: redis
time_frame: [7d, 28d] time_frame: [7d, 28d]
events: [ events:
'i_code_review_user_single_file_diffs', - 'i_code_review_user_single_file_diffs'
'i_code_review_user_create_mr', - 'i_code_review_user_create_mr'
'i_code_review_user_close_mr', - 'i_code_review_user_close_mr'
'i_code_review_user_reopen_mr', - 'i_code_review_user_reopen_mr'
'i_code_review_user_resolve_thread', - 'i_code_review_user_resolve_thread'
'i_code_review_user_unresolve_thread', - 'i_code_review_user_unresolve_thread'
'i_code_review_edit_mr_title', - 'i_code_review_edit_mr_title'
'i_code_review_edit_mr_desc', - 'i_code_review_edit_mr_desc'
'i_code_review_user_merge_mr', - 'i_code_review_user_merge_mr'
'i_code_review_user_create_mr_comment', - 'i_code_review_user_create_mr_comment'
'i_code_review_user_edit_mr_comment', - 'i_code_review_user_edit_mr_comment'
'i_code_review_user_remove_mr_comment', - 'i_code_review_user_remove_mr_comment'
'i_code_review_user_create_review_note', - 'i_code_review_user_create_review_note'
'i_code_review_user_publish_review', - 'i_code_review_user_publish_review'
'i_code_review_user_create_multiline_mr_comment', - 'i_code_review_user_create_multiline_mr_comment'
'i_code_review_user_edit_multiline_mr_comment', - 'i_code_review_user_edit_multiline_mr_comment'
'i_code_review_user_remove_multiline_mr_comment', - 'i_code_review_user_remove_multiline_mr_comment'
'i_code_review_user_add_suggestion', - 'i_code_review_user_add_suggestion'
'i_code_review_user_apply_suggestion', - 'i_code_review_user_apply_suggestion'
'i_code_review_user_assigned', - 'i_code_review_user_assigned'
'i_code_review_user_review_requested', - 'i_code_review_user_review_requested'
'i_code_review_user_approve_mr', - 'i_code_review_user_approve_mr'
'i_code_review_user_unapprove_mr', - 'i_code_review_user_unapprove_mr'
'i_code_review_user_marked_as_draft', - 'i_code_review_user_marked_as_draft'
'i_code_review_user_unmarked_as_draft', - 'i_code_review_user_unmarked_as_draft'
'i_code_review_user_approval_rule_added', - 'i_code_review_user_approval_rule_added'
'i_code_review_user_approval_rule_deleted', - 'i_code_review_user_approval_rule_deleted'
'i_code_review_user_approval_rule_edited', - 'i_code_review_user_approval_rule_edited'
'i_code_review_user_toggled_task_item_status', - 'i_code_review_user_toggled_task_item_status'
'i_code_review_user_create_mr_from_issue', - 'i_code_review_user_create_mr_from_issue'
'i_code_review_user_mr_discussion_locked', - 'i_code_review_user_mr_discussion_locked'
'i_code_review_user_mr_discussion_unlocked', - 'i_code_review_user_mr_discussion_unlocked'
'i_code_review_user_time_estimate_changed', - 'i_code_review_user_time_estimate_changed'
'i_code_review_user_time_spent_changed', - 'i_code_review_user_time_spent_changed'
'i_code_review_user_assignees_changed', - 'i_code_review_user_assignees_changed'
'i_code_review_user_reviewers_changed', - 'i_code_review_user_reviewers_changed'
'i_code_review_user_milestone_changed', - 'i_code_review_user_milestone_changed'
'i_code_review_user_labels_changed' - 'i_code_review_user_labels_changed'
]
- name: code_review_extension_category_monthly_active_users - name: code_review_extension_category_monthly_active_users
operator: OR operator: OR
feature_flag: usage_data_code_review_aggregation feature_flag: usage_data_code_review_aggregation
source: redis source: redis
time_frame: [7d, 28d] time_frame: [7d, 28d]
events: [ events:
'i_code_review_user_vs_code_api_request' - 'i_code_review_user_vs_code_api_request'
]
...@@ -21,52 +21,60 @@ ...@@ -21,52 +21,60 @@
operator: OR operator: OR
source: redis source: redis
time_frame: [7d, 28d] time_frame: [7d, 28d]
events: ['g_compliance_audit_events', 'g_compliance_dashboard', 'i_compliance_audit_events', 'a_compliance_audit_events_api', 'i_compliance_credential_inventory'] events:
- 'g_compliance_audit_events'
- 'g_compliance_dashboard'
- 'i_compliance_audit_events'
- 'a_compliance_audit_events_api'
- 'i_compliance_credential_inventory'
- name: product_analytics_test_metrics_union - name: product_analytics_test_metrics_union
operator: OR operator: OR
source: redis source: redis
time_frame: [7d, 28d] time_frame: [7d, 28d]
events: ['i_search_total', 'i_search_advanced', 'i_search_paid'] events:
- 'i_search_total'
- 'i_search_advanced'
- 'i_search_paid'
- name: product_analytics_test_metrics_intersection - name: product_analytics_test_metrics_intersection
operator: AND operator: AND
source: redis source: redis
time_frame: [7d, 28d] time_frame: [7d, 28d]
events: ['i_search_total', 'i_search_advanced', 'i_search_paid'] events:
- 'i_search_total'
- 'i_search_advanced'
- 'i_search_paid'
- name: incident_management_alerts_total_unique_counts - name: incident_management_alerts_total_unique_counts
operator: OR operator: OR
source: redis source: redis
time_frame: [7d, 28d] time_frame: [7d, 28d]
events: [ events:
'incident_management_alert_status_changed', - 'incident_management_alert_status_changed'
'incident_management_alert_assigned', - 'incident_management_alert_assigned'
'incident_management_alert_todo', - 'incident_management_alert_todo'
'incident_management_alert_create_incident' - 'incident_management_alert_create_incident'
]
- name: incident_management_incidents_total_unique_counts - name: incident_management_incidents_total_unique_counts
operator: OR operator: OR
source: redis source: redis
time_frame: [7d, 28d] time_frame: [7d, 28d]
events: [ events:
'incident_management_incident_created', - 'incident_management_incident_created'
'incident_management_incident_reopened', - 'incident_management_incident_reopened'
'incident_management_incident_closed', - 'incident_management_incident_closed'
'incident_management_incident_assigned', - 'incident_management_incident_assigned'
'incident_management_incident_todo', - 'incident_management_incident_todo'
'incident_management_incident_comment', - 'incident_management_incident_comment'
'incident_management_incident_zoom_meeting', - 'incident_management_incident_zoom_meeting'
'incident_management_incident_published', - 'incident_management_incident_published'
'incident_management_incident_relate', - 'incident_management_incident_relate'
'incident_management_incident_unrelate', - 'incident_management_incident_unrelate'
'incident_management_incident_change_confidential' - 'incident_management_incident_change_confidential'
]
- name: i_testing_paid_monthly_active_user_total - name: i_testing_paid_monthly_active_user_total
operator: OR operator: OR
source: redis source: redis
time_frame: [7d, 28d] time_frame: [7d, 28d]
events: [ events:
'i_testing_web_performance_widget_total', - 'i_testing_web_performance_widget_total'
'i_testing_full_code_quality_report_total', - 'i_testing_full_code_quality_report_total'
'i_testing_group_code_coverage_visit_total', - 'i_testing_group_code_coverage_visit_total'
'i_testing_load_performance_widget_total', - 'i_testing_load_performance_widget_total'
'i_testing_metrics_report_widget_total' - 'i_testing_metrics_report_widget_total'
]
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::ReleaseAssetLinks::Delete do
include GraphqlHelpers
let_it_be(:project) { create(:project, :private, :repository) }
let_it_be_with_reload(:release) { create(:release, project: project) }
let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
let_it_be(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } }
let_it_be_with_reload(:release_link) { create(:release_link, release: release) }
let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
let(:mutation_arguments) { { id: release_link.to_global_id } }
describe '#resolve' do
subject(:resolve) do
mutation.resolve(**mutation_arguments)
end
let(:deleted_link) { subject[:link] }
context 'when the current user has access to delete the link' do
let(:current_user) { maintainer }
it 'deletes the link and returns it', :aggregate_failures do
expect(deleted_link).to eq(release_link)
expect(release.links).to be_empty
end
context "when the link doesn't exist" do
let(:mutation_arguments) { super().merge(id: "gid://gitlab/Releases::Link/#{non_existing_record_id}") }
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context "when the provided ID is invalid" do
let(:mutation_arguments) { super().merge(id: 'not-a-valid-gid') }
it 'raises an error' do
expect { subject }.to raise_error(::GraphQL::CoercionError)
end
end
end
context 'when the current user does not have access to delete the link' do
let(:current_user) { developer }
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Deletes a release asset link' do
include GraphqlHelpers
let_it_be(:project) { create(:project, :private, :repository) }
let_it_be(:release) { create(:release, project: project) }
let_it_be(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } }
let_it_be(:release_link) { create(:release_link, release: release) }
let(:current_user) { maintainer }
let(:mutation_name) { :release_asset_link_delete }
let(:mutation_arguments) { { id: release_link.to_global_id.to_s } }
let(:mutation) do
graphql_mutation(mutation_name, mutation_arguments, <<~FIELDS)
link {
id
name
url
linkType
directAssetUrl
external
}
errors
FIELDS
end
let(:delete_link) { post_graphql_mutation(mutation, current_user: current_user) }
let(:mutation_response) { graphql_mutation_response(mutation_name)&.with_indifferent_access }
it 'deletes the release asset link and returns the deleted link', :aggregate_failures do
delete_link
expected_response = {
id: release_link.to_global_id.to_s,
name: release_link.name,
url: release_link.url,
linkType: release_link.link_type.upcase,
directAssetUrl: end_with(release_link.filepath),
external: true
}.with_indifferent_access
expect(mutation_response[:link]).to match(expected_response)
expect(mutation_response[:errors]).to eq([])
end
end
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