Commit 0c6f4618 authored by Peter Leitzen's avatar Peter Leitzen

Merge branch '214542-graphql-status-mutation' into 'master'

Add mutation for AlertManagementAlert status

See merge request gitlab-org/gitlab!30576
parents ca6aa800 3627c31d
# frozen_string_literal: true
module Mutations
module AlertManagement
class Base < BaseMutation
include Mutations::ResolvesProject
argument :project_path, GraphQL::ID_TYPE,
required: true,
description: "The project the alert to mutate is in"
argument :iid, GraphQL::STRING_TYPE,
required: true,
description: "The iid of the alert to mutate"
field :alert,
Types::AlertManagement::AlertType,
null: true,
description: "The alert after mutation"
authorize :update_alert_management_alerts
private
def find_object(project_path:, iid:)
project = resolve_project(full_path: project_path)
return unless project
resolver = Resolvers::AlertManagementAlertResolver.single.new(object: project, context: context, field: nil)
resolver.resolve(iid: iid)
end
end
end
end
# frozen_string_literal: true
module Mutations
module AlertManagement
class UpdateAlertStatus < Base
graphql_name 'UpdateAlertStatus'
argument :status, Types::AlertManagement::StatusEnum,
required: true,
description: 'The status to set the alert'
def resolve(args)
alert = authorized_find!(project_path: args[:project_path], iid: args[:iid])
result = update_status(alert, args[:status])
prepare_response(result)
end
private
def update_status(alert, status)
::AlertManagement::UpdateAlertStatusService.new(alert, status).execute
end
def prepare_response(result)
{
alert: result.payload[:alert],
errors: result.error? ? [result.message] : []
}
end
end
end
end
......@@ -7,6 +7,7 @@ module Types
graphql_name 'Mutation'
mount_mutation Mutations::Admin::SidekiqQueues::DeleteJobs
mount_mutation Mutations::AlertManagement::UpdateAlertStatus
mount_mutation Mutations::AwardEmojis::Add
mount_mutation Mutations::AwardEmojis::Remove
mount_mutation Mutations::AwardEmojis::Toggle
......
......@@ -240,6 +240,7 @@ class ProjectPolicy < BasePolicy
enable :read_prometheus
enable :read_metrics_dashboard_annotation
enable :read_alert_management_alerts
enable :update_alert_management_alerts
enable :metrics_dashboard
end
......
# frozen_string_literal: true
module AlertManagement
class UpdateAlertStatusService
def initialize(alert, status)
@alert = alert
@status = status
end
def execute
return error('Invalid status') unless AlertManagement::Alert.statuses.key?(status.to_s)
alert.status = status
if alert.save
success
else
error(alert.errors.full_messages.to_sentence)
end
end
private
attr_reader :alert, :status
def success
ServiceResponse.success(payload: { alert: alert })
end
def error(message)
ServiceResponse.error(payload: { alert: alert }, message: message)
end
end
end
---
title: Add mutation for AlertManagement's Alert status
merge_request: 30576
author:
type: added
......@@ -6074,6 +6074,7 @@ type Mutation {
todoRestoreMany(input: TodoRestoreManyInput!): TodoRestoreManyPayload
todosMarkAllDone(input: TodosMarkAllDoneInput!): TodosMarkAllDonePayload
toggleAwardEmoji(input: ToggleAwardEmojiInput!): ToggleAwardEmojiPayload
updateAlertStatus(input: UpdateAlertStatusInput!): UpdateAlertStatusPayload
updateEpic(input: UpdateEpicInput!): UpdateEpicPayload
"""
......@@ -9729,6 +9730,51 @@ enum TypeEnum {
project
}
"""
Autogenerated input type of UpdateAlertStatus
"""
input UpdateAlertStatusInput {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
The iid of the alert to mutate
"""
iid: String!
"""
The project the alert to mutate is in
"""
projectPath: ID!
"""
The status to set the alert
"""
status: AlertManagementStatus!
}
"""
Autogenerated return type of UpdateAlertStatus
"""
type UpdateAlertStatusPayload {
"""
The alert after mutation
"""
alert: AlertManagementAlert
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Reasons why the mutation failed.
"""
errors: [String!]!
}
input UpdateDiffImagePositionInput {
"""
Total height of the image
......
......@@ -18129,6 +18129,33 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updateAlertStatus",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "UpdateAlertStatusInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "UpdateAlertStatusPayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updateEpic",
"description": null,
......@@ -29248,6 +29275,136 @@
],
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "UpdateAlertStatusInput",
"description": "Autogenerated input type of UpdateAlertStatus",
"fields": null,
"inputFields": [
{
"name": "projectPath",
"description": "The project the alert to mutate is in",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "iid",
"description": "The iid of the alert to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "status",
"description": "The status to set the alert",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "AlertManagementStatus",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "UpdateAlertStatusPayload",
"description": "Autogenerated return type of UpdateAlertStatus",
"fields": [
{
"name": "alert",
"description": "The alert after mutation",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "AlertManagementAlert",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "errors",
"description": "Reasons why the mutation failed.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "UpdateDiffImagePositionInput",
......
......@@ -1515,6 +1515,16 @@ Represents a directory
| `type` | EntryType! | Type of tree entry |
| `webUrl` | String | Web URL for the tree entry (directory) |
## UpdateAlertStatusPayload
Autogenerated return type of UpdateAlertStatus
| Name | Type | Description |
| --- | ---- | ---------- |
| `alert` | AlertManagementAlert | The alert after mutation |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Reasons why the mutation failed. |
## UpdateEpicPayload
Autogenerated return type of UpdateEpic
......
# frozen_string_literal: true
require 'spec_helper'
describe Mutations::AlertManagement::UpdateAlertStatus do
let_it_be(:current_user) { create(:user) }
let_it_be(:alert) { create(:alert_management_alert, status: 'triggered') }
let_it_be(:project) { alert.project }
let(:new_status) { 'acknowledged' }
let(:args) { { status: new_status, project_path: project.full_path, iid: alert.iid } }
specify { expect(described_class).to require_graphql_authorizations(:update_alert_management_alerts) }
describe '#resolve' do
subject(:resolve) { mutation_for(project, current_user).resolve(args) }
context 'user has access to project' do
before do
project.add_developer(current_user)
end
it 'changes the status' do
expect { resolve }.to change { alert.reload.status }.from(alert.status).to(new_status)
end
it 'returns the alert with no errors' do
expect(resolve).to eq(
alert: alert,
errors: []
)
end
context 'error occurs when updating' do
it 'returns the alert with errors' do
# Stub an error on the alert
allow_next_instance_of(Resolvers::AlertManagementAlertResolver) do |resolver|
allow(resolver).to receive(:resolve).and_return(alert)
end
allow(alert).to receive(:save).and_return(false)
allow(alert).to receive(:errors).and_return(
double(full_messages: %w(foo bar))
)
expect(resolve).to eq(
alert: alert,
errors: ['foo and bar']
)
end
context 'invalid status given' do
let(:new_status) { 'invalid_status' }
it 'returns the alert with errors' do
expect(resolve).to eq(
alert: alert,
errors: ['Invalid status']
)
end
end
end
end
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
private
def mutation_for(project, user)
described_class.new(object: project, context: { current_user: user }, field: nil)
end
end
......@@ -6,17 +6,20 @@ describe AlertManagement::AlertPolicy, :models do
let(:alert) { create(:alert_management_alert) }
let(:project) { alert.project }
let(:user) { create(:user) }
let(:policy) { described_class.new(user, alert) }
subject(:policy) { described_class.new(user, alert) }
describe 'rules' do
it { expect(policy).to be_disallowed :read_alert_management_alerts }
it { is_expected.to be_disallowed :read_alert_management_alerts }
it { is_expected.to be_disallowed :update_alert_management_alerts }
context 'when developer' do
before do
project.add_developer(user)
end
it { expect(policy).to be_allowed :read_alert_management_alerts }
it { is_expected.to be_allowed :read_alert_management_alerts }
it { is_expected.to be_allowed :update_alert_management_alerts }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe AlertManagement::UpdateAlertStatusService do
let_it_be(:alert) { create(:alert_management_alert, status: 'triggered') }
describe '#execute' do
subject(:execute) { described_class.new(alert, new_status).execute }
let(:new_status) { 'acknowledged' }
it 'updates the status' do
expect { execute }.to change { alert.status }.to(new_status)
end
context 'with unknown status' do
let(:new_status) { 'random_status' }
it 'returns an error' do
expect(execute.status).to eq(:error)
end
it 'does not update the status' do
expect { execute }.not_to change { alert.status }
end
end
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