Merge pull request #8103 from tchak/feat-graphql-improuve-groupe-instructeur

feat(graphql): expose groupe_instructeur state and update mutation
This commit is contained in:
Paul Chavard 2022-11-24 14:42:41 +01:00 committed by GitHub
commit 4f3e29873e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 183 additions and 18 deletions

View file

@ -32,7 +32,7 @@ module Administrateurs
.new({ instructeurs: [current_administrateur.instructeur] }.merge(groupe_instructeur_params))
if @groupe_instructeur.save
routing_notice = " et le routage a été activé" if procedure.groupe_instructeurs.actif.size == 2
routing_notice = " et le routage a été activé" if procedure.groupe_instructeurs.active.size == 2
redirect_to admin_procedure_groupe_instructeur_path(procedure, @groupe_instructeur),
notice: "Le groupe dinstructeurs « #{@groupe_instructeur.label} » a été créé#{routing_notice}."
else
@ -56,7 +56,7 @@ module Administrateurs
@instructeurs = paginated_instructeurs
@available_instructeur_emails = available_instructeur_emails
flash[:alert] = @groupe_instructeur.errors.values.join('<br>')
flash[:alert] = @groupe_instructeur.errors.full_messages.to_sentence
render :show
end
end
@ -70,7 +70,7 @@ module Administrateurs
flash[:alert] = "Suppression impossible : il doit y avoir au moins un groupe instructeur sur chaque procédure"
else
@groupe_instructeur.destroy!
if procedure.groupe_instructeurs.actif.one?
if procedure.groupe_instructeurs.active.one?
procedure.update!(routing_enabled: false)
routing_notice = " et le routage a été désactivé"
end

View file

@ -711,5 +711,16 @@ class API::V2::StoredQuery
}
}
}
mutation groupeInstructeurModifier($input: GroupeInstructeurModifierInput!) {
groupeInstructeurModifier(input: $input) {
groupeInstructeur {
id
}
errors {
message
}
}
}
GRAPHQL
end

View file

@ -0,0 +1,20 @@
module Mutations
class GroupeInstructeurModifier < Mutations::BaseMutation
description "Modifier un groupe instructeur."
argument :groupe_instructeur_id, ID, "Groupe instructeur ID", required: true, loads: Types::GroupeInstructeurType
argument :label, String, "Libellé du groupe instructeur.", required: false
argument :closed, Boolean, "Létat du groupe instructeur.", required: false
field :groupe_instructeur, Types::GroupeInstructeurType, null: true
field :errors, [Types::ValidationErrorType], null: true
def resolve(groupe_instructeur:, label: nil, closed: nil)
if groupe_instructeur.update({ label: label, closed: closed }.compact)
{ groupe_instructeur: groupe_instructeur }
else
{ errors: groupe_instructeur.errors.full_messages }
end
end
end
end

View file

@ -572,7 +572,7 @@ type Demarche {
updatedSince: ISO8601DateTime
): DossierConnection!
draftRevision: Revision!
groupeInstructeurs: [GroupeInstructeur!]!
groupeInstructeurs(closed: Boolean): [GroupeInstructeur!]!
id: ID!
"""
@ -1523,8 +1523,16 @@ type GeoJSON {
Un groupe instructeur
"""
type GroupeInstructeur {
"""
Létat du groupe instructeur.
"""
closed: Boolean!
id: ID!
instructeurs: [Profile!]!
"""
Libellé du groupe instructeur.
"""
label: String!
"""
@ -1533,10 +1541,52 @@ type GroupeInstructeur {
number: Int!
}
"""
Autogenerated input type of GroupeInstructeurModifier
"""
input GroupeInstructeurModifierInput {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Létat du groupe instructeur.
"""
closed: Boolean
"""
Groupe instructeur ID
"""
groupeInstructeurId: ID!
"""
Libellé du groupe instructeur.
"""
label: String
}
"""
Autogenerated return type of GroupeInstructeurModifier.
"""
type GroupeInstructeurModifierPayload {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
errors: [ValidationError!]
groupeInstructeur: GroupeInstructeur
}
"""
Un groupe instructeur avec ses dossiers
"""
type GroupeInstructeurWithDossiers {
"""
Létat du groupe instructeur.
"""
closed: Boolean!
"""
Liste de tous les dossiers supprimés dun groupe instructeur.
"""
@ -1638,6 +1688,10 @@ type GroupeInstructeurWithDossiers {
): DossierConnection!
id: ID!
instructeurs: [Profile!]!
"""
Libellé du groupe instructeur.
"""
label: String!
"""
@ -1866,6 +1920,16 @@ type Mutation {
"""
input: DossierRepasserEnInstructionInput!
): DossierRepasserEnInstructionPayload
"""
Modifier un groupe instructeur.
"""
groupeInstructeurModifier(
"""
Parameters for GroupeInstructeurModifier
"""
input: GroupeInstructeurModifierInput!
): GroupeInstructeurModifierPayload
}
enum Order {

View file

@ -29,7 +29,9 @@ module Types
field :date_depublication, GraphQL::Types::ISO8601DateTime, "Date de la dépublication.", null: true, method: :unpublished_at
field :date_fermeture, GraphQL::Types::ISO8601DateTime, "Date de la fermeture.", null: true, method: :closed_at
field :groupe_instructeurs, [Types::GroupeInstructeurType], null: false
field :groupe_instructeurs, [Types::GroupeInstructeurType], null: false do
argument :closed, Boolean, required: false
end
field :service, Types::ServiceType, null: true
field :dossiers, Types::DossierType.connection_type, "Liste de tous les dossiers dune démarche.", null: false, extras: [:lookahead] do
@ -60,8 +62,14 @@ module Types
object.aasm.current_state
end
def groupe_instructeurs
Loaders::Association.for(object.class, groupe_instructeurs: { procedure: [:administrateurs] }).load(object)
def groupe_instructeurs(closed: nil)
if closed.nil?
Loaders::Association.for(object.class, groupe_instructeurs: { procedure: [:administrateurs] }).load(object)
elsif closed.true?
Loaders::Association.for(object.class, active_groupe_instructeurs: { procedure: [:administrateurs] }).load(object)
else
Loaders::Association.for(object.class, closed_groupe_instructeurs: { procedure: [:administrateurs] }).load(object)
end
end
def service

View file

@ -4,8 +4,9 @@ module Types
global_id_field :id
field :number, Int, "Le numero du groupe instructeur.", null: false, method: :id
field :label, String, null: false
field :label, String, "Libellé du groupe instructeur.", null: false
field :instructeurs, [Types::ProfileType], null: false
field :closed, Boolean, "Létat du groupe instructeur.", null: false
def instructeurs
Loaders::Association.for(object.class, :instructeurs).load(object)

View file

@ -18,5 +18,7 @@ module Types
field :dossier_modifier_annotation_datetime, mutation: Mutations::DossierModifierAnnotationDatetime
field :dossier_modifier_annotation_integer_number, mutation: Mutations::DossierModifierAnnotationIntegerNumber
field :dossier_modifier_annotation_ajouter_ligne, mutation: Mutations::DossierModifierAnnotationAjouterLigne
field :groupe_instructeur_modifier, mutation: Mutations::GroupeInstructeurModifier
end
end

View file

@ -19,16 +19,17 @@ class GroupeInstructeur < ApplicationRecord
has_and_belongs_to_many :exports, dependent: :destroy
has_and_belongs_to_many :bulk_messages, dependent: :destroy
validates :label, presence: { message: 'doit être renseigné' }, allow_nil: false
validates :label, uniqueness: { scope: :procedure, message: 'existe déjà' }
validates :closed, acceptance: { accept: [false], message: "Modification impossible : il doit y avoir au moins un groupe instructeur actif sur chaque procédure" }, if: -> { self.procedure.groupe_instructeurs.actif.one? }
validates :label, presence: true, allow_nil: false
validates :label, uniqueness: { scope: :procedure }
validates :closed, acceptance: { accept: [false] }, if: -> { self.procedure.groupe_instructeurs.active.one? }
before_validation -> { label&.strip! }
after_save :toggle_routing
scope :without_group, -> (group) { where.not(id: group) }
scope :for_api_v2, -> { includes(procedure: [:administrateurs]) }
scope :actif, -> { where(closed: false) }
scope :active, -> { where(closed: false) }
scope :closed, -> { where(closed: true) }
def add(instructeur)
return if in?(instructeur.groupe_instructeurs)
@ -48,12 +49,12 @@ class GroupeInstructeur < ApplicationRecord
end
def can_delete?
dossiers.empty? && (procedure.groupe_instructeurs.actif.many? || (procedure.groupe_instructeurs.actif.one? && closed))
dossiers.empty? && (procedure.groupe_instructeurs.active.many? || (procedure.groupe_instructeurs.active.one? && closed))
end
private
def toggle_routing
procedure.update!(routing_enabled: procedure.groupe_instructeurs.actif.many?)
procedure.update!(routing_enabled: procedure.groupe_instructeurs.active.many?)
end
end

View file

@ -185,6 +185,9 @@ class Procedure < ApplicationRecord
has_many :groupe_instructeurs, -> { order(:label) }, inverse_of: :procedure, dependent: :destroy
has_many :instructeurs, through: :groupe_instructeurs
has_many :active_groupe_instructeurs, -> { active }, class_name: 'GroupeInstructeur', inverse_of: false
has_many :closed_groupe_instructeurs, -> { closed }, class_name: 'GroupeInstructeur', inverse_of: false
# This relationship is used in following dossiers through. We can not use revisions relationship
# as order scope introduces invalid sql in some combinations.
has_many :unordered_revisions, class_name: 'ProcedureRevision', inverse_of: :procedure, dependent: :destroy
@ -196,7 +199,7 @@ class Procedure < ApplicationRecord
has_one :refused_mail, class_name: "Mails::RefusedMail", dependent: :destroy
has_one :without_continuation_mail, class_name: "Mails::WithoutContinuationMail", dependent: :destroy
has_one :defaut_groupe_instructeur, -> { actif.order(:label) }, class_name: 'GroupeInstructeur', inverse_of: false
has_one :defaut_groupe_instructeur, -> { active.order(:label) }, class_name: 'GroupeInstructeur', inverse_of: false
has_one_attached :logo
has_one_attached :notice

View file

@ -40,7 +40,7 @@
= dossier.procedure.routing_criteria_name
%span.mandatory *
= f.select :groupe_instructeur_id,
dossier.procedure.groupe_instructeurs.actif.map { |gi| [gi.label, gi.id] },
dossier.procedure.groupe_instructeurs.active.map { |gi| [gi.label, gi.id] },
{ include_blank: dossier.brouillon? }
- dossier.champs_public.each do |champ|

View file

@ -0,0 +1,18 @@
fr:
activerecord:
models:
procedure:
one: Groupe instructeur
other: Groupes instructeur
attributes:
groupe_instructeur:
label: Libellé
errors:
models:
groupe_instructeur:
attributes:
label:
format: "Le libellé %{message}"
closed:
format: "%{message}"
accepted: Il doit y avoir au moins un groupe instructeur actif sur chaque démarche

View file

@ -206,7 +206,7 @@ describe Administrateurs::GroupeInstructeursController, type: :controller do
it { expect(response).to render_template(:show) }
it { expect(gi_1_1.label).not_to eq(new_name) }
it { expect(gi_1_1.closed).to eq(false) }
it { expect(flash.alert).to be_present }
it { expect(flash.alert).to eq('Il doit y avoir au moins un groupe instructeur actif sur chaque démarche') }
end
context 'when the name is already taken' do
@ -214,7 +214,7 @@ describe Administrateurs::GroupeInstructeursController, type: :controller do
let(:new_name) { gi_1_2.label }
it { expect(gi_1_1.label).not_to eq(new_name) }
it { expect(flash.alert).to be_present }
it { expect(flash.alert).to eq('Le libellé est déjà utilisé(e)') }
end
end

View file

@ -1003,6 +1003,43 @@ describe API::V2::GraphqlController do
expect(gql_data[:dossierClasserSansSuite][:dossier][:state]).to eq('sans_suite')
}
end
context 'groupeInstructeurModifier' do
let(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure: procedure) }
let(:variables) { { input: { groupeInstructeurId: dossier.groupe_instructeur.to_typed_id, label: 'nouveau groupe instructeur' } } }
let(:operation_name) { 'groupeInstructeurModifier' }
it {
expect(gql_errors).to be_nil
expect(gql_data[:groupeInstructeurModifier][:errors]).to be_nil
expect(gql_data[:groupeInstructeurModifier][:groupeInstructeur][:id]).to eq(dossier.groupe_instructeur.to_typed_id)
expect(dossier.groupe_instructeur.reload.label).to eq('nouveau groupe instructeur')
}
context 'close groupe instructeur' do
let(:variables) { { input: { groupeInstructeurId: dossier.groupe_instructeur.to_typed_id, closed: true } } }
context 'with multiple groupes' do
before do
create(:groupe_instructeur, procedure: procedure)
end
it {
expect(gql_errors).to be_nil
expect(gql_data[:groupeInstructeurModifier][:errors]).to be_nil
expect(gql_data[:groupeInstructeurModifier][:groupeInstructeur][:id]).to eq(dossier.groupe_instructeur.to_typed_id)
expect(dossier.groupe_instructeur.reload.closed).to be_truthy
}
end
context 'validation error' do
it {
expect(gql_errors).to be_nil
expect(gql_data[:groupeInstructeurModifier][:errors].first[:message]).to eq('Il doit y avoir au moins un groupe instructeur actif sur chaque démarche')
}
end
end
end
end
end