From 3e40b6d7bc0b42eb18dcb25d7fd4d82686762ea7 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Thu, 21 Jul 2022 14:54:14 +0200 Subject: [PATCH] fix(graphql): handle annotations inside blocks --- .../mutations/dossier_modifier_annotation.rb | 32 +++-- ...ssier_modifier_annotation_ajouter_ligne.rb | 38 +++++ .../dossier_modifier_annotation_checkbox.rb | 15 +- .../dossier_modifier_annotation_date.rb | 15 +- .../dossier_modifier_annotation_datetime.rb | 15 +- ...sier_modifier_annotation_integer_number.rb | 15 +- .../dossier_modifier_annotation_text.rb | 15 +- app/graphql/schema.graphql | 44 ++++++ app/graphql/types/mutation_type.rb | 1 + app/models/champ.rb | 11 +- spec/factories/champ.rb | 4 +- spec/factories/procedure.rb | 6 + spec/graphql/annotation_spec.rb | 131 ++++++++++++++++++ 13 files changed, 303 insertions(+), 39 deletions(-) create mode 100644 app/graphql/mutations/dossier_modifier_annotation_ajouter_ligne.rb create mode 100644 spec/graphql/annotation_spec.rb diff --git a/app/graphql/mutations/dossier_modifier_annotation.rb b/app/graphql/mutations/dossier_modifier_annotation.rb index 3d441be40..6af888fb6 100644 --- a/app/graphql/mutations/dossier_modifier_annotation.rb +++ b/app/graphql/mutations/dossier_modifier_annotation.rb @@ -7,8 +7,12 @@ module Mutations field :annotation, Types::ChampType, null: true field :errors, [Types::ValidationErrorType], null: true - def resolve_with_type(type, dossier, annotation_id, instructeur, value) - annotation = find_annotation(dossier, type, annotation_id) + def resolve_with_type(dossier:, annotation_id:, instructeur:, value:) + annotation = find_annotation(dossier, annotation_id) + + if annotation.nil? + return { errors: ["L’annotation \"#{annotation_id}\" n’existe pas"] } + end if block_given? annotation.value = yield annotation.type_champ, value @@ -31,18 +35,22 @@ module Mutations private - def find_annotation(dossier, type, annotation_id) - _, stable_id = GraphQL::Schema::UniqueWithinType.decode(annotation_id) - dossier.champs_private - .joins(:type_de_champ) - .find_by!(types_de_champ: { - type_champ: annotation_type_champ(type), - stable_id: stable_id - }) + def input_type + :text end - def annotation_type_champ(type) - case type + def find_annotation(dossier, annotation_id) + stable_id, row = Champ.decode_typed_id(annotation_id) + + Champ.joins(:type_de_champ).find_by(type_de_champ: { + type_champ: annotation_type_champ, + stable_id: stable_id, + private: true + }, private: true, row: row, dossier: dossier) + end + + def annotation_type_champ + case input_type when :text [ TypeDeChamp.type_champs.fetch(:text), diff --git a/app/graphql/mutations/dossier_modifier_annotation_ajouter_ligne.rb b/app/graphql/mutations/dossier_modifier_annotation_ajouter_ligne.rb new file mode 100644 index 000000000..ad59cdecc --- /dev/null +++ b/app/graphql/mutations/dossier_modifier_annotation_ajouter_ligne.rb @@ -0,0 +1,38 @@ +module Mutations + class DossierModifierAnnotationAjouterLigne < Mutations::BaseMutation + argument :dossier_id, ID, "Dossier ID", required: true, loads: Types::DossierType + argument :instructeur_id, ID, "Instructeur qui demande la modification.", required: true, loads: Types::ProfileType + argument :annotation_id, ID, "Annotation ID", required: true + + field :annotation, Types::Champs::RepetitionChampType, null: true + field :errors, [Types::ValidationErrorType], null: true + + def resolve(dossier:, annotation_id:, instructeur:) + annotation = find_annotation(dossier, annotation_id) + + if annotation.nil? + return { errors: ["L’annotation \"#{annotation_id}\" n’existe pas"] } + end + + annotation.add_row(dossier.revision) + + { annotation: annotation, errors: nil } + end + + def authorized?(dossier:, instructeur:, **args) + dossier_authorized_for?(dossier, instructeur) + end + + private + + def find_annotation(dossier, annotation_id) + stable_id, row = Champ.decode_typed_id(annotation_id) + + Champ.joins(:type_de_champ).find_by(type_de_champ: { + type_champ: TypeDeChamp.type_champs.fetch(:repetition), + stable_id: stable_id, + private: true + }, private: true, row: row, dossier: dossier) + end + end +end diff --git a/app/graphql/mutations/dossier_modifier_annotation_checkbox.rb b/app/graphql/mutations/dossier_modifier_annotation_checkbox.rb index 898b95786..36aeda7fd 100644 --- a/app/graphql/mutations/dossier_modifier_annotation_checkbox.rb +++ b/app/graphql/mutations/dossier_modifier_annotation_checkbox.rb @@ -6,11 +6,10 @@ module Mutations def resolve(dossier:, annotation_id:, instructeur:, value:) resolve_with_type( - :checkbox, - dossier, - annotation_id, - instructeur, - value + dossier: dossier, + annotation_id: annotation_id, + instructeur: instructeur, + value: value ) do |type_champ, value| if type_champ == TypeDeChamp.type_champs.fetch(:yes_no) value ? 'true' : 'false' @@ -19,5 +18,11 @@ module Mutations end end end + + private + + def input_type + :checkbox + end end end diff --git a/app/graphql/mutations/dossier_modifier_annotation_date.rb b/app/graphql/mutations/dossier_modifier_annotation_date.rb index 7a6579918..a3e50ad2b 100644 --- a/app/graphql/mutations/dossier_modifier_annotation_date.rb +++ b/app/graphql/mutations/dossier_modifier_annotation_date.rb @@ -6,12 +6,17 @@ module Mutations def resolve(dossier:, annotation_id:, instructeur:, value:) resolve_with_type( - :date, - dossier, - annotation_id, - instructeur, - value + dossier: dossier, + annotation_id: annotation_id, + instructeur: instructeur, + value: value ) end + + private + + def input_type + :date + end end end diff --git a/app/graphql/mutations/dossier_modifier_annotation_datetime.rb b/app/graphql/mutations/dossier_modifier_annotation_datetime.rb index cdddc958b..a524c3641 100644 --- a/app/graphql/mutations/dossier_modifier_annotation_datetime.rb +++ b/app/graphql/mutations/dossier_modifier_annotation_datetime.rb @@ -6,12 +6,17 @@ module Mutations def resolve(dossier:, annotation_id:, instructeur:, value:) resolve_with_type( - :datetime, - dossier, - annotation_id, - instructeur, - value + dossier: dossier, + annotation_id: annotation_id, + instructeur: instructeur, + value: value ) end + + private + + def input_type + :datetime + end end end diff --git a/app/graphql/mutations/dossier_modifier_annotation_integer_number.rb b/app/graphql/mutations/dossier_modifier_annotation_integer_number.rb index 4fa9d5970..2906de550 100644 --- a/app/graphql/mutations/dossier_modifier_annotation_integer_number.rb +++ b/app/graphql/mutations/dossier_modifier_annotation_integer_number.rb @@ -6,12 +6,17 @@ module Mutations def resolve(dossier:, annotation_id:, instructeur:, value:) resolve_with_type( - :integer_number, - dossier, - annotation_id, - instructeur, - value + dossier: dossier, + annotation_id: annotation_id, + instructeur: instructeur, + value: value ) end + + private + + def input_type + :integer_number + end end end diff --git a/app/graphql/mutations/dossier_modifier_annotation_text.rb b/app/graphql/mutations/dossier_modifier_annotation_text.rb index 94083049d..c70c055da 100644 --- a/app/graphql/mutations/dossier_modifier_annotation_text.rb +++ b/app/graphql/mutations/dossier_modifier_annotation_text.rb @@ -6,12 +6,17 @@ module Mutations def resolve(dossier:, annotation_id:, instructeur:, value:) resolve_with_type( - :text, - dossier, - annotation_id, - instructeur, - value + dossier: dossier, + annotation_id: annotation_id, + instructeur: instructeur, + value: value ) end + + private + + def input_type + :text + end end end diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index b04715cbe..a3bb3ffb2 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -1026,6 +1026,44 @@ type DossierLinkChamp implements Champ { stringValue: String } +""" +Autogenerated input type of DossierModifierAnnotationAjouterLigne +""" +input DossierModifierAnnotationAjouterLigneInput { + """ + Annotation ID + """ + annotationId: ID! + + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + + """ + Dossier ID + """ + dossierId: ID! + + """ + Instructeur qui demande la modification. + """ + instructeurId: ID! +} + +""" +Autogenerated return type of DossierModifierAnnotationAjouterLigne +""" +type DossierModifierAnnotationAjouterLignePayload { + annotation: RepetitionChamp + + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + errors: [ValidationError!] +} + """ Autogenerated input type of DossierModifierAnnotationCheckbox """ @@ -1664,6 +1702,12 @@ type Mutation { """ input: DossierEnvoyerMessageInput! ): DossierEnvoyerMessagePayload + dossierModifierAnnotationAjouterLigne( + """ + Parameters for DossierModifierAnnotationAjouterLigne + """ + input: DossierModifierAnnotationAjouterLigneInput! + ): DossierModifierAnnotationAjouterLignePayload """ Modifier l’annotation au format oui/non. diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index 3f6e2eaca..91dd0c49b 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -17,5 +17,6 @@ module Types field :dossier_modifier_annotation_date, mutation: Mutations::DossierModifierAnnotationDate 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 end end diff --git a/app/models/champ.rb b/app/models/champ.rb index c69907f8a..3a0558dce 100644 --- a/app/models/champ.rb +++ b/app/models/champ.rb @@ -126,7 +126,16 @@ class Champ < ApplicationRecord end def to_typed_id - type_de_champ.to_typed_id + if row.present? + GraphQL::Schema::UniqueWithinType.encode('Champ', "#{stable_id}|#{row}") + else + type_de_champ.to_typed_id + end + end + + def self.decode_typed_id(typed_id) + _, stable_id_with_maybe_row = GraphQL::Schema::UniqueWithinType.decode(typed_id) + stable_id_with_maybe_row.split('|') end def html_label? diff --git a/spec/factories/champ.rb b/spec/factories/champ.rb index 10e79fcae..2c9d64886 100644 --- a/spec/factories/champ.rb +++ b/spec/factories/champ.rb @@ -224,6 +224,7 @@ FactoryBot.define do type_de_champ_text = build(:type_de_champ_text, procedure: champ_repetition.type_de_champ.procedure, position: 0, + private: champ_repetition.private?, parent: parent, libelle: 'Nom') types_de_champ.push(type_de_champ_text) @@ -234,6 +235,7 @@ FactoryBot.define do type_de_champ_number = build(:type_de_champ_number, procedure: champ_repetition.type_de_champ.procedure, position: 1, + private: champ_repetition.private?, parent: parent, libelle: 'Age') types_de_champ.push(type_de_champ_number) @@ -241,7 +243,7 @@ FactoryBot.define do evaluator.rows.times do |row| champ_repetition.champs << types_de_champ.map do |type_de_champ| - build(:"champ_#{type_de_champ.type_champ}", dossier: champ_repetition.dossier, row: row, type_de_champ: type_de_champ, parent: champ_repetition) + build(:"champ_#{type_de_champ.type_champ}", dossier: champ_repetition.dossier, row: row, type_de_champ: type_de_champ, parent: champ_repetition, private: champ_repetition.private?) end end end diff --git a/spec/factories/procedure.rb b/spec/factories/procedure.rb index ac84834a3..1d509fc58 100644 --- a/spec/factories/procedure.rb +++ b/spec/factories/procedure.rb @@ -190,6 +190,12 @@ FactoryBot.define do end end + trait :with_private_repetition do + after(:build) do |procedure, _evaluator| + build(:type_de_champ_repetition, :private, procedure: procedure) + end + end + trait :with_number do after(:build) do |procedure, _evaluator| build(:type_de_champ_number, procedure: procedure) diff --git a/spec/graphql/annotation_spec.rb b/spec/graphql/annotation_spec.rb new file mode 100644 index 000000000..704e67450 --- /dev/null +++ b/spec/graphql/annotation_spec.rb @@ -0,0 +1,131 @@ +RSpec.describe Mutations::DossierModifierAnnotation, type: :graphql do + let(:admin) { create(:administrateur) } + let(:procedure) { create(:procedure, :published, :for_individual, :with_private_repetition, :with_type_de_champ_private, administrateurs: [admin]) } + let(:dossiers) { [] } + let(:instructeur) { create(:instructeur, followed_dossiers: dossiers) } + + let(:query) { '' } + let(:context) { { administrateur_id: admin.id } } + let(:variables) { {} } + + subject { API::V2::Schema.execute(query, variables: variables, context: context) } + + let(:data) { subject['data'].deep_symbolize_keys } + let(:errors) { subject['errors'].deep_symbolize_keys } + + before do + instructeur.assign_to_procedure(procedure) + end + + describe 'dossierModifierAnnotationAjouterLigne' do + let(:dossier) { create(:dossier, :en_construction, :with_populated_annotations, procedure: procedure) } + let(:dossiers) { [dossier] } + + let(:annotation) { dossier.champs_private.find(&:repetition?) } + let(:query) { DOSSIER_MODIFIER_ANNOTATION_AJOUTER_LIGNE_MUTATION } + let(:variables) do + { + input: { + dossierId: dossier.to_typed_id, + annotationId: annotation.to_typed_id, + instructeurId: instructeur.to_typed_id + } + } + end + + context 'with invalid champ' do + let(:annotation) { dossier.champs_private.last } + + it 'return error' do + expect(data).to eq(dossierModifierAnnotationAjouterLigne: { + annotation: nil, + errors: [{ message: "L’annotation \"#{annotation.to_typed_id}\" n’existe pas" }] + }) + end + end + + it 'add row' do + expect(annotation.champs.size).to eq(4) + expect(data).to eq(dossierModifierAnnotationAjouterLigne: { + annotation: { + id: annotation.to_typed_id + }, + errors: nil + }) + expect(annotation.reload.champs.size).to eq(6) + end + end + + describe 'dossierModifierAnnotationText' do + let(:dossier) { create(:dossier, :en_construction, :with_populated_annotations, procedure: procedure) } + let(:dossiers) { [dossier] } + + let(:annotation) { dossier.champs_private.last } + let(:query) { DOSSIER_MODIFIER_ANNOTATION_TEXT_MUTATION } + let(:variables) do + { + input: { + dossierId: dossier.to_typed_id, + annotationId: annotation.to_typed_id, + instructeurId: instructeur.to_typed_id, + value: 'Hello world' + } + } + end + + it 'update champ' do + expect(data).to eq(dossierModifierAnnotationText: { + annotation: { + id: annotation.to_typed_id + }, + errors: nil + }) + expect(annotation.reload.value).to eq('Hello world') + end + + context 'with invalid champ' do + let(:annotation) { dossier.champs_private.find(&:repetition?) } + + it 'return error' do + expect(data).to eq(dossierModifierAnnotationText: { + annotation: nil, + errors: [{ message: "L’annotation \"#{annotation.to_typed_id}\" n’existe pas" }] + }) + end + end + + context 'with rows' do + let(:annotation) { dossier.champs_private.find(&:repetition?).rows.first.first } + let(:other_annotation) { dossier.champs_private.find(&:repetition?).rows.second.first } + + it 'update champ' do + expect(data).to eq(dossierModifierAnnotationText: { + annotation: { + id: annotation.to_typed_id + }, + errors: nil + }) + expect(annotation.reload.value).to eq('Hello world') + expect(other_annotation.reload.value).not_to eq('Hello world') + end + end + end + + DOSSIER_MODIFIER_ANNOTATION_AJOUTER_LIGNE_MUTATION = <<-GRAPHQL + mutation($input: DossierModifierAnnotationAjouterLigneInput!) { + dossierModifierAnnotationAjouterLigne(input: $input) { + annotation { id } + errors { message } + } + } + GRAPHQL + + DOSSIER_MODIFIER_ANNOTATION_TEXT_MUTATION = <<-GRAPHQL + mutation($input: DossierModifierAnnotationTextInput!) { + dossierModifierAnnotationText(input: $input) { + annotation { id } + errors { message } + } + } + GRAPHQL +end