Commit 3e24318c authored by Sean Arnold's avatar Sean Arnold

Minor tweaks including check for dup usernames

Update docs
parent 1a5fa2b7
...@@ -10970,27 +10970,27 @@ Describes an incident management on-call rotation ...@@ -10970,27 +10970,27 @@ Describes an incident management on-call rotation
""" """
type IncidentManagementOncallRotation { type IncidentManagementOncallRotation {
""" """
ID of the on-call rotation ID of the on-call rotation.
""" """
id: IncidentManagementOncallRotationID! id: IncidentManagementOncallRotationID!
""" """
Time zone of the on-call schedule Length of the on-call schedule, in the units specified by lengthUnit.
""" """
length: Int length: Int
""" """
Unit of the on-call rotation length Unit of the on-call rotation length.
""" """
lengthUnit: OncallRotationUnitEnum lengthUnit: OncallRotationUnitEnum
""" """
Name of the on-call rotation Name of the on-call rotation.
""" """
name: String! name: String!
""" """
Participants of the on-call rotation Participants of the on-call rotation.
""" """
participants( participants(
""" """
...@@ -11015,7 +11015,7 @@ type IncidentManagementOncallRotation { ...@@ -11015,7 +11015,7 @@ type IncidentManagementOncallRotation {
): OncallParticipantTypeConnection ): OncallParticipantTypeConnection
""" """
Start date of the on-call rotation Start date of the on-call rotation.
""" """
startsAt: Time startsAt: Time
} }
...@@ -15715,7 +15715,7 @@ input OncallRotationCreateInput { ...@@ -15715,7 +15715,7 @@ input OncallRotationCreateInput {
scheduleIid: String! scheduleIid: String!
""" """
The start date and time of the on-call rotation The start date and time of the on-call rotation, in the timezone of the on-call schedule
""" """
startsAt: OncallRotationDateInputType! startsAt: OncallRotationDateInputType!
} }
...@@ -15775,17 +15775,17 @@ Rotation length unit of an on-call rotation ...@@ -15775,17 +15775,17 @@ Rotation length unit of an on-call rotation
""" """
enum OncallRotationUnitEnum { enum OncallRotationUnitEnum {
""" """
One Days Days
""" """
DAYS DAYS
""" """
One Hours Hours
""" """
HOURS HOURS
""" """
One Weeks Weeks
""" """
WEEKS WEEKS
} }
...@@ -15940,12 +15940,13 @@ The rotation user and color palette ...@@ -15940,12 +15940,13 @@ The rotation user and color palette
""" """
input OncallUserInputType { input OncallUserInputType {
""" """
The color palette to assign to the on-call user, for example: "blue". A value of DataVisualizationColorEnum. The color from the palette to assign to the on-call user.
""" """
colorPalette: DataVisualizationColorEnum colorPalette: DataVisualizationColorEnum
""" """
The color weight to assign to for the on-call user, for example "500". Max 4 chars. For easy identification of the user. A value of DataVisualizationWeightEnum. The color weight to assign to for the
on-call user, for example "500". Max 4 chars.
""" """
colorWeight: DataVisualizationWeightEnum colorWeight: DataVisualizationWeightEnum
......
...@@ -30050,7 +30050,7 @@ ...@@ -30050,7 +30050,7 @@
"fields": [ "fields": [
{ {
"name": "id", "name": "id",
"description": "ID of the on-call rotation", "description": "ID of the on-call rotation.",
"args": [ "args": [
], ],
...@@ -30068,7 +30068,7 @@ ...@@ -30068,7 +30068,7 @@
}, },
{ {
"name": "length", "name": "length",
"description": "Time zone of the on-call schedule", "description": "Length of the on-call schedule, in the units specified by lengthUnit.",
"args": [ "args": [
], ],
...@@ -30082,7 +30082,7 @@ ...@@ -30082,7 +30082,7 @@
}, },
{ {
"name": "lengthUnit", "name": "lengthUnit",
"description": "Unit of the on-call rotation length", "description": "Unit of the on-call rotation length.",
"args": [ "args": [
], ],
...@@ -30096,7 +30096,7 @@ ...@@ -30096,7 +30096,7 @@
}, },
{ {
"name": "name", "name": "name",
"description": "Name of the on-call rotation", "description": "Name of the on-call rotation.",
"args": [ "args": [
], ],
...@@ -30114,7 +30114,7 @@ ...@@ -30114,7 +30114,7 @@
}, },
{ {
"name": "participants", "name": "participants",
"description": "Participants of the on-call rotation", "description": "Participants of the on-call rotation.",
"args": [ "args": [
{ {
"name": "after", "name": "after",
...@@ -30167,7 +30167,7 @@ ...@@ -30167,7 +30167,7 @@
}, },
{ {
"name": "startsAt", "name": "startsAt",
"description": "Start date of the on-call rotation", "description": "Start date of the on-call rotation.",
"args": [ "args": [
], ],
...@@ -46650,7 +46650,7 @@ ...@@ -46650,7 +46650,7 @@
}, },
{ {
"name": "startsAt", "name": "startsAt",
"description": "The start date and time of the on-call rotation", "description": "The start date and time of the on-call rotation, in the timezone of the on-call schedule",
"type": { "type": {
"kind": "NON_NULL", "kind": "NON_NULL",
"name": null, "name": null,
...@@ -46868,19 +46868,19 @@ ...@@ -46868,19 +46868,19 @@
"enumValues": [ "enumValues": [
{ {
"name": "HOURS", "name": "HOURS",
"description": "One Hours", "description": "Hours",
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{ {
"name": "DAYS", "name": "DAYS",
"description": "One Days", "description": "Days",
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{ {
"name": "WEEKS", "name": "WEEKS",
"description": "One Weeks", "description": "Weeks",
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
} }
...@@ -47311,7 +47311,7 @@ ...@@ -47311,7 +47311,7 @@
}, },
{ {
"name": "colorPalette", "name": "colorPalette",
"description": "The color palette to assign to the on-call user, for example: \"blue\".", "description": "A value of DataVisualizationColorEnum. The color from the palette to assign to the on-call user.",
"type": { "type": {
"kind": "ENUM", "kind": "ENUM",
"name": "DataVisualizationColorEnum", "name": "DataVisualizationColorEnum",
...@@ -47321,7 +47321,7 @@ ...@@ -47321,7 +47321,7 @@
}, },
{ {
"name": "colorWeight", "name": "colorWeight",
"description": "The color weight to assign to for the on-call user, for example \"500\". Max 4 chars. For easy identification of the user.", "description": "A value of DataVisualizationWeightEnum. The color weight to assign to for the on-call user, for example \"500\". Max 4 chars.",
"type": { "type": {
"kind": "ENUM", "kind": "ENUM",
"name": "DataVisualizationWeightEnum", "name": "DataVisualizationWeightEnum",
...@@ -1718,12 +1718,12 @@ Describes an incident management on-call rotation. ...@@ -1718,12 +1718,12 @@ Describes an incident management on-call rotation.
| Field | Type | Description | | Field | Type | Description |
| ----- | ---- | ----------- | | ----- | ---- | ----------- |
| `id` | IncidentManagementOncallRotationID! | ID of the on-call rotation | | `id` | IncidentManagementOncallRotationID! | ID of the on-call rotation. |
| `length` | Int | Time zone of the on-call schedule | | `length` | Int | Length of the on-call schedule, in the units specified by lengthUnit. |
| `lengthUnit` | OncallRotationUnitEnum | Unit of the on-call rotation length | | `lengthUnit` | OncallRotationUnitEnum | Unit of the on-call rotation length. |
| `name` | String! | Name of the on-call rotation | | `name` | String! | Name of the on-call rotation. |
| `participants` | OncallParticipantTypeConnection | Participants of the on-call rotation | | `participants` | OncallParticipantTypeConnection | Participants of the on-call rotation. |
| `startsAt` | Time | Start date of the on-call rotation | | `startsAt` | Time | Start date of the on-call rotation. |
### IncidentManagementOncallSchedule ### IncidentManagementOncallSchedule
...@@ -4645,9 +4645,9 @@ Rotation length unit of an on-call rotation. ...@@ -4645,9 +4645,9 @@ Rotation length unit of an on-call rotation.
| Value | Description | | Value | Description |
| ----- | ----------- | | ----- | ----------- |
| `DAYS` | One Days | | `DAYS` | Days |
| `HOURS` | One Hours | | `HOURS` | Hours |
| `WEEKS` | One Weeks | | `WEEKS` | Weeks |
### PackageTypeEnum ### PackageTypeEnum
......
...@@ -13,10 +13,10 @@ module Mutations ...@@ -13,10 +13,10 @@ module Mutations
private private
def response(result, errors = nil) def response(result)
{ {
oncall_rotation: result.payload[:oncall_rotation], oncall_rotation: result.payload[:oncall_rotation],
errors: errors.presence || result.errors errors: result.errors
} }
end end
end end
......
...@@ -23,7 +23,7 @@ module Mutations ...@@ -23,7 +23,7 @@ module Mutations
argument :starts_at, Types::IncidentManagement::OncallRotationDateInputType, argument :starts_at, Types::IncidentManagement::OncallRotationDateInputType,
required: true, required: true,
description: 'The start date and time of the on-call rotation' description: 'The start date and time of the on-call rotation, in the timezone of the on-call schedule'
argument :rotation_length, Types::IncidentManagement::OncallRotationLengthInputType, argument :rotation_length, Types::IncidentManagement::OncallRotationLengthInputType,
required: true, required: true,
...@@ -37,7 +37,7 @@ module Mutations ...@@ -37,7 +37,7 @@ module Mutations
MAXIMUM_PARTICIPANTS = 100 MAXIMUM_PARTICIPANTS = 100
def resolve(iid:, project_path:, participants:, **args) def resolve(iid:, project_path:, participants:, **args)
project = authorized_find!(full_path: project_path) project = Project.find_by_full_path(project_path)
schedule = ::IncidentManagement::OncallSchedulesFinder.new(current_user, project, iid: iid) schedule = ::IncidentManagement::OncallSchedulesFinder.new(current_user, project, iid: iid)
.execute .execute
...@@ -45,21 +45,24 @@ module Mutations ...@@ -45,21 +45,24 @@ module Mutations
raise_schedule_not_found unless schedule raise_schedule_not_found unless schedule
result = ::IncidentManagement::OncallRotations::CreateService.new( begin
schedule, result = ::IncidentManagement::OncallRotations::CreateService.new(
project, schedule,
current_user, project,
prepare_params(schedule, participants, args) current_user,
).execute create_service_params(schedule, participants, args)
).execute
errors = result.error? ? [result.message] : [] rescue ActiveRecord::RecordInvalid => e
raise Gitlab::Graphql::Errors::ArgumentError, e.message
end
response(result, errors) response(result)
end end
private private
def prepare_params(schedule, participants, args) def create_service_params(schedule, participants, args)
rotation_length = args[:rotation_length][:length] rotation_length = args[:rotation_length][:length]
rotation_length_unit = args[:rotation_length][:unit] rotation_length_unit = args[:rotation_length][:unit]
starts_at = parse_start_time(schedule, args) starts_at = parse_start_time(schedule, args)
...@@ -76,16 +79,13 @@ module Mutations ...@@ -76,16 +79,13 @@ module Mutations
args[:starts_at].asctime.in_time_zone(schedule.timezone) args[:starts_at].asctime.in_time_zone(schedule.timezone)
end end
def find_object(full_path:)
resolve_project(full_path: full_path)
end
def find_participants(user_array) def find_participants(user_array)
raise_too_many_users_error if user_array.size > MAXIMUM_PARTICIPANTS raise_too_many_users_error if user_array.size > MAXIMUM_PARTICIPANTS
usernames = user_array.map {|h| h[:username] } usernames = user_array.map {|h| h[:username] }
matched_users = UsersFinder.new(current_user, username: usernames).execute.order_by(:username) raise_duplicate_users_error if usernames.size != usernames.uniq.size
matched_users = UsersFinder.new(current_user, username: usernames).execute.order_by(:username)
raise_user_not_found if matched_users.size != user_array.size raise_user_not_found if matched_users.size != user_array.size
user_array = user_array.sort_by! { |h| h[:username] } user_array = user_array.sort_by! { |h| h[:username] }
...@@ -101,6 +101,10 @@ module Mutations ...@@ -101,6 +101,10 @@ module Mutations
raise Gitlab::Graphql::Errors::ArgumentError, "A maximum of #{MAXIMUM_PARTICIPANTS} participants can be added" raise Gitlab::Graphql::Errors::ArgumentError, "A maximum of #{MAXIMUM_PARTICIPANTS} participants can be added"
end end
def raise_duplicate_users_error
raise Gitlab::Graphql::Errors::ArgumentError, "A duplicate username is included in the participant list"
end
def raise_user_not_found def raise_user_not_found
raise Gitlab::Graphql::Errors::ArgumentError, 'A username that was provided could not be matched to a user' raise Gitlab::Graphql::Errors::ArgumentError, 'A username that was provided could not be matched to a user'
end end
......
...@@ -7,7 +7,7 @@ module Types ...@@ -7,7 +7,7 @@ module Types
description 'Rotation length unit of an on-call rotation' description 'Rotation length unit of an on-call rotation'
::IncidentManagement::OncallRotation.length_units.keys.each do |unit| ::IncidentManagement::OncallRotation.length_units.keys.each do |unit|
value unit.upcase, value: unit, description: "One #{unit.titleize}" value unit.upcase, value: unit, description: "#{unit.titleize}"
end end
end end
end end
......
...@@ -11,32 +11,32 @@ module Types ...@@ -11,32 +11,32 @@ module Types
field :id, field :id,
Types::GlobalIDType[::IncidentManagement::OncallRotation], Types::GlobalIDType[::IncidentManagement::OncallRotation],
null: false, null: false,
description: 'ID of the on-call rotation' description: 'ID of the on-call rotation.'
field :name, field :name,
GraphQL::STRING_TYPE, GraphQL::STRING_TYPE,
null: false, null: false,
description: 'Name of the on-call rotation' description: 'Name of the on-call rotation.'
field :starts_at, field :starts_at,
Types::TimeType, Types::TimeType,
null: true, null: true,
description: 'Start date of the on-call rotation' description: 'Start date of the on-call rotation.'
field :length, field :length,
GraphQL::INT_TYPE, GraphQL::INT_TYPE,
null: true, null: true,
description: 'Time zone of the on-call schedule' description: 'Length of the on-call schedule, in the units specified by lengthUnit.'
field :length_unit, field :length_unit,
Types::IncidentManagement::OncallRotationLengthUnitEnum, Types::IncidentManagement::OncallRotationLengthUnitEnum,
null: true, null: true,
description: 'Unit of the on-call rotation length' description: 'Unit of the on-call rotation length.'
field :participants, field :participants,
::Types::IncidentManagement::OncallParticipantType.connection_type, ::Types::IncidentManagement::OncallParticipantType.connection_type,
null: true, null: true,
description: 'Participants of the on-call rotation' description: 'Participants of the on-call rotation.'
end end
end end
end end
...@@ -13,11 +13,11 @@ module Types ...@@ -13,11 +13,11 @@ module Types
argument :color_palette, ::Types::DataVisualizationPalette::ColorEnum, argument :color_palette, ::Types::DataVisualizationPalette::ColorEnum,
required: false, required: false,
description: 'The color palette to assign to the on-call user, for example: "blue".' description: 'A value of DataVisualizationColorEnum. The color from the palette to assign to the on-call user.'
argument :color_weight, ::Types::DataVisualizationPalette::WeightEnum, argument :color_weight, ::Types::DataVisualizationPalette::WeightEnum,
required: false, required: false,
description: 'The color weight to assign to for the on-call user, for example "500". Max 4 chars. For easy identification of the user.' description: 'A value of DataVisualizationWeightEnum. The color weight to assign to for the on-call user, for example "500". Max 4 chars.'
end end
# rubocop: enable Graphql/AuthorizeTypes # rubocop: enable Graphql/AuthorizeTypes
end end
......
...@@ -26,8 +26,6 @@ RSpec.describe Mutations::IncidentManagement::OncallRotation::Create do ...@@ -26,8 +26,6 @@ RSpec.describe Mutations::IncidentManagement::OncallRotation::Create do
} }
end end
specify { expect(described_class).to require_graphql_authorizations(:admin_incident_management_oncall_schedule) }
describe '#resolve' do describe '#resolve' do
subject(:resolve) { mutation_for(project, current_user).resolve(iid: schedule.iid, project_path: project.full_path, participants: args[:participants], **args) } subject(:resolve) { mutation_for(project, current_user).resolve(iid: schedule.iid, project_path: project.full_path, participants: args[:participants], **args) }
...@@ -87,7 +85,17 @@ RSpec.describe Mutations::IncidentManagement::OncallRotation::Create do ...@@ -87,7 +85,17 @@ RSpec.describe Mutations::IncidentManagement::OncallRotation::Create do
end end
it 'raises an error' do it 'raises an error' do
expect { resolve }.to raise_error(ActiveRecord::RecordInvalid, /User does not have access to the project/) expect { resolve }.to raise_error(Gitlab::Graphql::Errors::ArgumentError, /User does not have access to the project/)
end
end
context 'duplicate participants' do
before do
args[:participants] << args[:participants].first
end
it 'raises an error' do
expect { resolve }.to raise_error(Gitlab::Graphql::Errors::ArgumentError, 'A duplicate username is included in the participant list')
end end
end end
...@@ -113,7 +121,7 @@ RSpec.describe Mutations::IncidentManagement::OncallRotation::Create do ...@@ -113,7 +121,7 @@ RSpec.describe Mutations::IncidentManagement::OncallRotation::Create do
context 'when resource is not accessible to the user' do context 'when resource is not accessible to the user' do
it 'raises an error' do it 'raises an error' do
expect { resolve }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) expect { resolve }.to raise_error(Gitlab::Graphql::Errors::ArgumentError, 'The schedule could not be found')
end end
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