From e600aceccc60843a44abf9b493bf5ca7477d0dbd Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Wed, 13 Nov 2019 20:01:58 +0100 Subject: [PATCH] [GraphQL]: add dossier state change mutations --- app/graphql/mutations/dossier_accepter.rb | 29 +++ .../mutations/dossier_classer_sans_suite.rb | 29 +++ .../dossier_passer_en_instruction.rb | 27 ++ app/graphql/mutations/dossier_refuser.rb | 29 +++ app/graphql/schema.graphql | 154 ++++++++++++ app/graphql/types/mutation_type.rb | 4 + .../api/v2/graphql_controller_spec.rb | 230 +++++++++++++++++- 7 files changed, 501 insertions(+), 1 deletion(-) create mode 100644 app/graphql/mutations/dossier_accepter.rb create mode 100644 app/graphql/mutations/dossier_classer_sans_suite.rb create mode 100644 app/graphql/mutations/dossier_passer_en_instruction.rb create mode 100644 app/graphql/mutations/dossier_refuser.rb diff --git a/app/graphql/mutations/dossier_accepter.rb b/app/graphql/mutations/dossier_accepter.rb new file mode 100644 index 000000000..0ad5af78a --- /dev/null +++ b/app/graphql/mutations/dossier_accepter.rb @@ -0,0 +1,29 @@ +module Mutations + class DossierAccepter < Mutations::BaseMutation + include DossierHelper + + description "Accepter le dossier." + + argument :dossier_id, ID, "Dossier ID", required: true, loads: Types::DossierType + argument :instructeur_id, ID, "Instructeur qui prend la décision sur le dossier.", required: true, loads: Types::ProfileType + argument :motivation, String, required: false + argument :justificatif, ID, required: false + + field :dossier, Types::DossierType, null: true + field :errors, [Types::ValidationErrorType], null: true + + def resolve(dossier:, instructeur:, motivation: nil, justificatif: nil) + if dossier.en_instruction? + dossier.accepter!(instructeur, motivation, justificatif) + + { dossier: dossier } + else + { errors: ["Le dossier est déjà #{dossier_display_state(dossier, lower: true)}"] } + end + end + + def authorized?(dossier:, instructeur:, motivation: nil) + instructeur.is_a?(Instructeur) && instructeur.dossiers.exists?(id: dossier.id) + end + end +end diff --git a/app/graphql/mutations/dossier_classer_sans_suite.rb b/app/graphql/mutations/dossier_classer_sans_suite.rb new file mode 100644 index 000000000..fea8a0fa8 --- /dev/null +++ b/app/graphql/mutations/dossier_classer_sans_suite.rb @@ -0,0 +1,29 @@ +module Mutations + class DossierClasserSansSuite < Mutations::BaseMutation + include DossierHelper + + description "Classer le dossier sans suite." + + argument :dossier_id, ID, "Dossier ID", required: true, loads: Types::DossierType + argument :instructeur_id, ID, "Instructeur qui prend la décision sur le dossier.", required: true, loads: Types::ProfileType + argument :motivation, String, required: true + argument :justificatif, ID, required: false + + field :dossier, Types::DossierType, null: true + field :errors, [Types::ValidationErrorType], null: true + + def resolve(dossier:, instructeur:, motivation:, justificatif: nil) + if dossier.en_instruction? + dossier.classer_sans_suite!(instructeur, motivation, justificatif) + + { dossier: dossier } + else + { errors: ["Le dossier est déjà #{dossier_display_state(dossier, lower: true)}"] } + end + end + + def authorized?(dossier:, instructeur:, motivation:) + instructeur.is_a?(Instructeur) && instructeur.dossiers.exists?(id: dossier.id) + end + end +end diff --git a/app/graphql/mutations/dossier_passer_en_instruction.rb b/app/graphql/mutations/dossier_passer_en_instruction.rb new file mode 100644 index 000000000..5ddd118ad --- /dev/null +++ b/app/graphql/mutations/dossier_passer_en_instruction.rb @@ -0,0 +1,27 @@ +module Mutations + class DossierPasserEnInstruction < Mutations::BaseMutation + include DossierHelper + + description "Passer le dossier en instruction." + + argument :dossier_id, ID, "Dossier ID", required: true, loads: Types::DossierType + argument :instructeur_id, ID, "Instructeur qui prend la décision sur le dossier.", required: true, loads: Types::ProfileType + + field :dossier, Types::DossierType, null: true + field :errors, [Types::ValidationErrorType], null: true + + def resolve(dossier:, instructeur:) + if dossier.en_construction? + dossier.passer_en_instruction!(instructeur) + + { dossier: dossier } + else + { errors: ["Le dossier est déjà #{dossier_display_state(dossier, lower: true)}"] } + end + end + + def authorized?(dossier:, instructeur:) + instructeur.is_a?(Instructeur) && instructeur.dossiers.exists?(id: dossier.id) + end + end +end diff --git a/app/graphql/mutations/dossier_refuser.rb b/app/graphql/mutations/dossier_refuser.rb new file mode 100644 index 000000000..25328bee4 --- /dev/null +++ b/app/graphql/mutations/dossier_refuser.rb @@ -0,0 +1,29 @@ +module Mutations + class DossierRefuser < Mutations::BaseMutation + include DossierHelper + + description "Refuser le dossier." + + argument :dossier_id, ID, "Dossier ID", required: true, loads: Types::DossierType + argument :instructeur_id, ID, "Instructeur qui prend la décision sur le dossier.", required: true, loads: Types::ProfileType + argument :motivation, String, required: true + argument :justificatif, ID, required: false + + field :dossier, Types::DossierType, null: true + field :errors, [Types::ValidationErrorType], null: true + + def resolve(dossier:, instructeur:, motivation:, justificatif: nil) + if dossier.en_instruction? + dossier.refuser!(instructeur, motivation, justificatif) + + { dossier: dossier } + else + { errors: ["Le dossier est déjà #{dossier_display_state(dossier, lower: true)}"] } + end + end + + def authorized?(dossier:, instructeur:, motivation:) + instructeur.is_a?(Instructeur) && instructeur.dossiers.exists?(id: dossier.id) + end + end +end diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index b60fc03ae..318d46fe3 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -317,6 +317,74 @@ type Dossier { usager: Profile! } +""" +Autogenerated input type of DossierAccepter +""" +input DossierAccepterInput { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + + """ + Dossier ID + """ + dossierId: ID! + + """ + Instructeur qui prend la décision sur le dossier. + """ + instructeurId: ID! + justificatif: ID + motivation: String +} + +""" +Autogenerated return type of DossierAccepter +""" +type DossierAccepterPayload { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + dossier: Dossier + errors: [ValidationError!] +} + +""" +Autogenerated input type of DossierClasserSansSuite +""" +input DossierClasserSansSuiteInput { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + + """ + Dossier ID + """ + dossierId: ID! + + """ + Instructeur qui prend la décision sur le dossier. + """ + instructeurId: ID! + justificatif: ID + motivation: String! +} + +""" +Autogenerated return type of DossierClasserSansSuite +""" +type DossierClasserSansSuitePayload { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + dossier: Dossier + errors: [ValidationError!] +} + """ The connection type for Dossier. """ @@ -394,6 +462,72 @@ type DossierLinkChamp implements Champ { stringValue: String } +""" +Autogenerated input type of DossierPasserEnInstruction +""" +input DossierPasserEnInstructionInput { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + + """ + Dossier ID + """ + dossierId: ID! + + """ + Instructeur qui prend la décision sur le dossier. + """ + instructeurId: ID! +} + +""" +Autogenerated return type of DossierPasserEnInstruction +""" +type DossierPasserEnInstructionPayload { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + dossier: Dossier + errors: [ValidationError!] +} + +""" +Autogenerated input type of DossierRefuser +""" +input DossierRefuserInput { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + + """ + Dossier ID + """ + dossierId: ID! + + """ + Instructeur qui prend la décision sur le dossier. + """ + instructeurId: ID! + justificatif: ID + motivation: String! +} + +""" +Autogenerated return type of DossierRefuser +""" +type DossierRefuserPayload { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + dossier: Dossier + errors: [ValidationError!] +} + enum DossierState { """ Accepté @@ -523,10 +657,30 @@ type Mutation { """ createDirectUpload(input: CreateDirectUploadInput!): CreateDirectUploadPayload + """ + Accepter le dossier. + """ + dossierAccepter(input: DossierAccepterInput!): DossierAccepterPayload + + """ + Classer le dossier sans suite. + """ + dossierClasserSansSuite(input: DossierClasserSansSuiteInput!): DossierClasserSansSuitePayload + """ Envoyer un message à l'usager du dossier. """ dossierEnvoyerMessage(input: DossierEnvoyerMessageInput!): DossierEnvoyerMessagePayload + + """ + Passer le dossier en instruction. + """ + dossierPasserEnInstruction(input: DossierPasserEnInstructionInput!): DossierPasserEnInstructionPayload + + """ + Refuser le dossier. + """ + dossierRefuser(input: DossierRefuserInput!): DossierRefuserPayload } enum Order { diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index 92da07a80..22ad55981 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -3,5 +3,9 @@ module Types field :create_direct_upload, mutation: Mutations::CreateDirectUpload field :dossier_envoyer_message, mutation: Mutations::DossierEnvoyerMessage + field :dossier_passer_en_instruction, mutation: Mutations::DossierPasserEnInstruction + field :dossier_classer_sans_suite, mutation: Mutations::DossierClasserSansSuite + field :dossier_refuser, mutation: Mutations::DossierRefuser + field :dossier_accepter, mutation: Mutations::DossierAccepter end end diff --git a/spec/controllers/api/v2/graphql_controller_spec.rb b/spec/controllers/api/v2/graphql_controller_spec.rb index eb2721145..6910643bb 100644 --- a/spec/controllers/api/v2/graphql_controller_spec.rb +++ b/spec/controllers/api/v2/graphql_controller_spec.rb @@ -291,7 +291,235 @@ describe API::V2::GraphqlController do end end - context 'createDirectUpload' do + describe 'dossierPasserEnInstruction' do + let(:dossier) { create(:dossier, :en_construction, procedure: procedure) } + let(:query) do + "mutation { + dossierPasserEnInstruction(input: { + dossierId: \"#{dossier.to_typed_id}\", + instructeurId: \"#{instructeur.to_typed_id}\" + }) { + dossier { + id + state + motivation + } + errors { + message + } + } + }" + end + + context 'success' do + it "should passer en instruction dossier" do + expect(gql_errors).to eq(nil) + + expect(gql_data).to eq(dossierPasserEnInstruction: { + dossier: { + id: dossier.to_typed_id, + state: "en_instruction", + motivation: nil + }, + errors: nil + }) + end + end + + context 'validation error' do + let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) } + + it "should fail" do + expect(gql_errors).to eq(nil) + expect(gql_data).to eq(dossierPasserEnInstruction: { + errors: [{ message: "Le dossier est déjà en instruction" }], + dossier: nil + }) + end + end + end + + describe 'dossierClasserSansSuite' do + let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) } + let(:query) do + "mutation { + dossierClasserSansSuite(input: { + dossierId: \"#{dossier.to_typed_id}\", + instructeurId: \"#{instructeur.to_typed_id}\", + motivation: \"Parce que\" + }) { + dossier { + id + state + motivation + } + errors { + message + } + } + }" + end + + context 'success' do + it "should classer sans suite dossier" do + expect(gql_errors).to eq(nil) + + expect(gql_data).to eq(dossierClasserSansSuite: { + dossier: { + id: dossier.to_typed_id, + state: "sans_suite", + motivation: "Parce que" + }, + errors: nil + }) + end + end + + context 'validation error' do + let(:dossier) { create(:dossier, :accepte, procedure: procedure) } + + it "should fail" do + expect(gql_errors).to eq(nil) + expect(gql_data).to eq(dossierClasserSansSuite: { + errors: [{ message: "Le dossier est déjà accepté" }], + dossier: nil + }) + end + end + end + + describe 'dossierRefuser' do + let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) } + let(:query) do + "mutation { + dossierRefuser(input: { + dossierId: \"#{dossier.to_typed_id}\", + instructeurId: \"#{instructeur.to_typed_id}\", + motivation: \"Parce que\" + }) { + dossier { + id + state + motivation + } + errors { + message + } + } + }" + end + + context 'success' do + it "should refuser dossier" do + expect(gql_errors).to eq(nil) + + expect(gql_data).to eq(dossierRefuser: { + dossier: { + id: dossier.to_typed_id, + state: "refuse", + motivation: "Parce que" + }, + errors: nil + }) + end + end + + context 'validation error' do + let(:dossier) { create(:dossier, :sans_suite, procedure: procedure) } + + it "should fail" do + expect(gql_errors).to eq(nil) + expect(gql_data).to eq(dossierRefuser: { + errors: [{ message: "Le dossier est déjà sans suite" }], + dossier: nil + }) + end + end + end + + describe 'dossierAccepter' do + let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) } + let(:query) do + "mutation { + dossierAccepter(input: { + dossierId: \"#{dossier.to_typed_id}\", + instructeurId: \"#{instructeur.to_typed_id}\", + motivation: \"Parce que\" + }) { + dossier { + id + state + motivation + } + errors { + message + } + } + }" + end + + context 'success' do + it "should accepter dossier" do + expect(gql_errors).to eq(nil) + + expect(gql_data).to eq(dossierAccepter: { + dossier: { + id: dossier.to_typed_id, + state: "accepte", + motivation: "Parce que" + }, + errors: nil + }) + end + end + + context 'success without motivation' do + let(:query) do + "mutation { + dossierAccepter(input: { + dossierId: \"#{dossier.to_typed_id}\", + instructeurId: \"#{instructeur.to_typed_id}\" + }) { + dossier { + id + state + motivation + } + errors { + message + } + } + }" + end + + it "should accepter dossier" do + expect(gql_errors).to eq(nil) + + expect(gql_data).to eq(dossierAccepter: { + dossier: { + id: dossier.to_typed_id, + state: "accepte", + motivation: nil + }, + errors: nil + }) + end + end + + context 'validation error' do + let(:dossier) { create(:dossier, :refuse, procedure: procedure) } + + it "should fail" do + expect(gql_errors).to eq(nil) + expect(gql_data).to eq(dossierAccepter: { + errors: [{ message: "Le dossier est déjà refusé" }], + dossier: nil + }) + end + end + end + + describe 'createDirectUpload' do let(:query) do "mutation { createDirectUpload(input: {