Commit 19e245d5 authored by Sean Arnold's avatar Sean Arnold

Update mutation logic

- Stop using public_send
- Use ServiceResponse
- Fix spec styles
- Update docs
parent 74967bd0
......@@ -20,7 +20,7 @@ module Mutations
private
def find_alert(project_path:, iid:)
def find_object(project_path:, iid:)
project = resolve_project(full_path: project_path)
return unless project
......
# frozen_string_literal: true
module Mutations
module AlertManagement
class ChangeAlertStatus < Base
graphql_name 'ChangeAlertStatus'
argument :status, Types::AlertManagement::StatusEnum,
required: true,
description: "The status to set the alert"
def resolve(args)
alert = find_alert(project_path: args[:project_path], iid: args[:iid])
if alert
service = ::AlertManagement::AlertService.new(alert)
service.set_status!(args[:status])
else
self.errors = Array('Alert could not be found')
end
{
alert: alert,
errors: errors
}
end
private
attr_accessor :errors
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'
authorize :read_alert_management_alerts
def resolve(args)
alert = authorized_find!(project_path: args[:project_path], iid: args[:iid])
if alert
result = update_status(alert, args[:status])
prepare_response(result)
else
{
alert: alert,
errors: ['Alert could not be found']
}
end
end
private
def update_status(alert, status)
service = ::AlertManagement::UpdateAlertStatusService.new(alert, status)
service.execute!
end
def prepare_response(result)
{
alert: result.payload[:alert],
errors: result.message.present? ? [result.message] : []
}
end
end
end
end
......@@ -7,7 +7,7 @@ module Types
graphql_name 'Mutation'
mount_mutation Mutations::Admin::SidekiqQueues::DeleteJobs
mount_mutation Mutations::AlertManagement::ChangeAlertStatus
mount_mutation Mutations::AlertManagement::UpdateAlertStatus
mount_mutation Mutations::AwardEmojis::Add
mount_mutation Mutations::AwardEmojis::Remove
mount_mutation Mutations::AwardEmojis::Toggle
......
# frozen_string_literal: true
module AlertManagement
class AlertService
attr_reader :alert
def initialize(alert)
@alert = alert
end
def set_status!(status)
return unless AlertManagement::Alert.statuses.key?(status.to_s)
# rubocop:disable GitlabSecurity/PublicSend
alert.public_send("#{status}!")
# rubocop:enable GitlabSecurity/PublicSend
end
end
end
# frozen_string_literal: true
module AlertManagement
class UpdateAlertStatusService
def initialize(alert, status)
@alert = alert
@status = status
end
def execute!
return error_response('Invalid status') unless AlertManagement::Alert.statuses.key?(status.to_s)
alert.status = status
return ServiceResponse.success(payload: { alert: alert }) if alert.save
error_response
end
private
def error_response(message)
ServiceResponse.error(payload: { alert: alert }, message: message)
end
attr_reader :alert, :status
end
end
......@@ -653,51 +653,6 @@ type Branch {
name: String!
}
"""
Autogenerated input type of ChangeAlertStatus
"""
input ChangeAlertStatusInput {
"""
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 ChangeAlertStatus
"""
type ChangeAlertStatusPayload {
"""
The alert after mutation
"""
alert: AlertManagementAlert
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Reasons why the mutation failed.
"""
errors: [String!]!
}
type Commit {
"""
Author of the commit
......@@ -6087,7 +6042,6 @@ type Mutation {
addProjectToSecurityDashboard(input: AddProjectToSecurityDashboardInput!): AddProjectToSecurityDashboardPayload
adminSidekiqQueuesDeleteJobs(input: AdminSidekiqQueuesDeleteJobsInput!): AdminSidekiqQueuesDeleteJobsPayload
boardListUpdateLimitMetrics(input: BoardListUpdateLimitMetricsInput!): BoardListUpdateLimitMetricsPayload
changeAlertStatus(input: ChangeAlertStatusInput!): ChangeAlertStatusPayload
createBranch(input: CreateBranchInput!): CreateBranchPayload
createDiffNote(input: CreateDiffNoteInput!): CreateDiffNotePayload
createEpic(input: CreateEpicInput!): CreateEpicPayload
......@@ -6120,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
"""
......@@ -9775,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
......
......@@ -1888,136 +1888,6 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "ChangeAlertStatusInput",
"description": "Autogenerated input type of ChangeAlertStatus",
"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": "ChangeAlertStatusPayload",
"description": "Autogenerated return type of ChangeAlertStatus",
"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": "OBJECT",
"name": "Commit",
......@@ -17395,33 +17265,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "changeAlertStatus",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "ChangeAlertStatusInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "ChangeAlertStatusPayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "createBranch",
"description": null,
......@@ -18286,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,
......@@ -29405,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",
......
......@@ -140,16 +140,6 @@ Autogenerated return type of BoardListUpdateLimitMetrics
| `commit` | Commit | Commit for the branch |
| `name` | String! | Name of the branch |
## ChangeAlertStatusPayload
Autogenerated return type of ChangeAlertStatus
| 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. |
## Commit
| Name | Type | Description |
......@@ -1525,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
......
......@@ -2,9 +2,9 @@
require 'spec_helper'
describe Mutations::AlertManagement::ChangeAlertStatus do
describe Mutations::AlertManagement::UpdateAlertStatus do
let_it_be(:current_user) { create(:user) }
let_it_be(:alert) { create(:alert_management_alert) }
let_it_be(:alert) { create(:alert_management_alert, status: 'triggered') }
let(:project) { alert.project }
let(:new_status) { 'acknowledged' }
let(:args) { { status: new_status, project_path: project.full_path, iid: alert.iid } }
......@@ -17,19 +17,13 @@ describe Mutations::AlertManagement::ChangeAlertStatus do
project.add_developer(current_user)
end
it 'does not change the status' do
it 'changes the status' do
expect { resolve }.to change { alert.reload.status }.from(alert.status).to(new_status)
end
end
context 'user has no access' do
it 'does not change the status' do
expect { resolve }.not_to change { alert.reload.status }
end
it 'sets the error' do
expect(resolve.dig(:errors)).not_to be_empty
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
......
......@@ -2,26 +2,27 @@
require 'spec_helper'
describe AlertManagement::AlertService do
let_it_be(:alert) { create(:alert_management_alert) }
let(:instance) { described_class.new(alert) }
describe AlertManagement::UpdateAlertStatusService do
let_it_be(:alert) { create(:alert_management_alert, status: 'triggered') }
describe '#set_status!' do
subject(:set_status) { instance.set_status!(new_status) }
describe '#execute!' do
subject(:execute) { described_class.new(alert, new_status).execute! }
let(:new_status) { 'acknowledged' }
it 'updates the status' do
expect { set_status }.to change { alert.status }.to(new_status)
expect { execute }.to change { alert.status }.to(new_status)
end
context 'with unknown status' do
let(:new_status) { 'random_status' }
it { is_expected.to be_nil }
it 'returns an error' do
expect(execute.status).to eq(:error)
end
it 'does not update the status' do
expect { set_status }.not_to change { alert.status }
expect { execute }.not_to change { alert.status }
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