2019-11-12-01 (#4507)

2019-11-12-01
This commit is contained in:
Pierre de La Morinerie 2019-11-12 13:58:31 +01:00 committed by GitHub
commit b8a9e737a5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 140 additions and 70 deletions

View file

@ -53,7 +53,7 @@ class API::V1::DossiersController < APIController
end end
order = ORDER_DIRECTIONS.fetch(params[:order], :asc) order = ORDER_DIRECTIONS.fetch(params[:order], :asc)
@dossiers = @procedure.dossiers.state_not_brouillon.order_for_api(order) @dossiers = @procedure.dossiers.state_not_brouillon.order_by_created_at(order)
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render json: {}, status: :not_found render json: {}, status: :not_found

View file

@ -128,25 +128,35 @@ type Demarche {
""" """
before: String before: String
"""
Dossiers déposés depuis la date.
"""
createdSince: ISO8601DateTime
""" """
Returns the first _n_ elements from the list. Returns the first _n_ elements from the list.
""" """
first: Int first: Int
"""
Filtrer les dossiers par ID.
"""
ids: [ID!]
""" """
Returns the last _n_ elements from the list. Returns the last _n_ elements from the list.
""" """
last: Int last: Int
""" """
Dossiers crées depuis la date. L'ordre des dossiers.
""" """
since: ISO8601DateTime order: Order = ASC
"""
Dossiers avec statut.
"""
state: DossierState
"""
Dossiers mis à jour depuis la date.
"""
updatedSince: ISO8601DateTime
): DossierConnection! ): DossierConnection!
groupeInstructeurs: [GroupeInstructeur!]! groupeInstructeurs: [GroupeInstructeur!]!
id: ID! id: ID!
@ -401,6 +411,18 @@ type Mutation {
createDirectUpload(input: CreateDirectUploadInput!): CreateDirectUploadPayload createDirectUpload(input: CreateDirectUploadInput!): CreateDirectUploadPayload
} }
enum Order {
"""
Lordre ascendant.
"""
ASC
"""
Lordre descendant.
"""
DESC
}
""" """
Information about pagination in a connection. Information about pagination in a connection.
""" """

View file

@ -21,8 +21,10 @@ module Types
field :groupe_instructeurs, [Types::GroupeInstructeurType], null: false field :groupe_instructeurs, [Types::GroupeInstructeurType], null: false
field :dossiers, Types::DossierType.connection_type, "Liste de tous les dossiers d'une démarche.", null: false do field :dossiers, Types::DossierType.connection_type, "Liste de tous les dossiers d'une démarche.", null: false do
argument :ids, [ID], required: false, description: "Filtrer les dossiers par ID." argument :order, Types::Order, default_value: :asc, required: false, description: "L'ordre des dossiers."
argument :since, GraphQL::Types::ISO8601DateTime, required: false, description: "Dossiers crées depuis la date." argument :created_since, GraphQL::Types::ISO8601DateTime, required: false, description: "Dossiers déposés depuis la date."
argument :updated_since, GraphQL::Types::ISO8601DateTime, required: false, description: "Dossiers mis à jour depuis la date."
argument :state, Types::DossierType::DossierState, required: false, description: "Dossiers avec statut."
end end
field :champ_descriptors, [Types::ChampDescriptorType], null: false, method: :types_de_champ field :champ_descriptors, [Types::ChampDescriptorType], null: false, method: :types_de_champ
@ -36,15 +38,21 @@ module Types
Loaders::Association.for(object.class, groupe_instructeurs: { procedure: [:administrateurs] }).load(object) Loaders::Association.for(object.class, groupe_instructeurs: { procedure: [:administrateurs] }).load(object)
end end
def dossiers(ids: nil, since: nil) def dossiers(updated_since: nil, created_since: nil, state: nil, order:)
dossiers = object.dossiers.for_api_v2 dossiers = object.dossiers.state_not_brouillon.for_api_v2
if ids.present? if state.present?
dossiers = dossiers.where(id: ids) dossiers = dossiers.where(state: state)
end end
if since.present? if updated_since.present?
dossiers = dossiers.since(since) dossiers = dossiers.updated_since(updated_since).order_by_updated_at(order)
else
if created_since.present?
dossiers = dossiers.created_since(created_since)
end
dossiers = dossiers.order_by_created_at(order)
end end
dossiers dossiers

View file

@ -0,0 +1,6 @@
module Types
class Order < Types::BaseEnum
value('ASC', 'Lordre ascendant.', value: :asc)
value('DESC', 'Lordre descendant.', value: :desc)
end
end

View file

@ -0,0 +1,4 @@
module ActiveStorage
# activestorage-openstack uses ActiveStorage::FileNotFoundError which only exists in rails 6
class FileNotFoundError < StandardError; end
end

View file

@ -105,7 +105,9 @@ class Dossier < ApplicationRecord
scope :not_archived, -> { where(archived: false) } scope :not_archived, -> { where(archived: false) }
scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) } scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) }
scope :order_for_api, -> (order = :asc) { order(en_construction_at: order, created_at: order, id: order) } scope :order_by_created_at, -> (order = :asc) { order(en_construction_at: order, created_at: order, id: order) }
scope :updated_since, -> (since) { where('dossiers.updated_at >= ?', since) }
scope :created_since, -> (since) { where('dossiers.en_construction_at >= ?', since) }
scope :all_state, -> { not_archived.state_not_brouillon } scope :all_state, -> { not_archived.state_not_brouillon }
scope :en_construction, -> { not_archived.state_en_construction } scope :en_construction, -> { not_archived.state_en_construction }
@ -134,7 +136,6 @@ class Dossier < ApplicationRecord
scope :without_followers, -> { left_outer_joins(:follows).where(follows: { id: nil }) } scope :without_followers, -> { left_outer_joins(:follows).where(follows: { id: nil }) }
scope :with_champs, -> { includes(champs: :type_de_champ) } scope :with_champs, -> { includes(champs: :type_de_champ) }
scope :nearing_end_of_retention, -> (duration = '1 month') { joins(:procedure).where("en_instruction_at + (duree_conservation_dossiers_dans_ds * interval '1 month') - now() < interval ?", duration) } scope :nearing_end_of_retention, -> (duration = '1 month') { joins(:procedure).where("en_instruction_at + (duree_conservation_dossiers_dans_ds * interval '1 month') - now() < interval ?", duration) }
scope :since, -> (since) { where('dossiers.en_construction_at >= ?', since) }
scope :for_api, -> { scope :for_api, -> {
includes(commentaires: { piece_jointe_attachment: :blob }, includes(commentaires: { piece_jointe_attachment: :blob },
champs: [ champs: [

View file

@ -444,7 +444,7 @@ class Procedure < ApplicationRecord
version = options.delete(:version) version = options.delete(:version)
if version == 'v2' if version == 'v2'
options.delete(:tables) options.delete(:tables)
ProcedureExportV2Service.new(self, dossiers, **options.to_h.symbolize_keys) ProcedureExportV2Service.new(self, dossiers)
else else
ProcedureExportService.new(self, dossiers, **options.to_h.symbolize_keys) ProcedureExportService.new(self, dossiers, **options.to_h.symbolize_keys)
end end

View file

@ -49,7 +49,7 @@ class ProcedureExportService
:prenom :prenom
] ]
def initialize(procedure, dossiers, tables: [], ids: nil, since: nil, limit: nil) def initialize(procedure, dossiers, tables: [])
@procedure = procedure @procedure = procedure
@attributes = ATTRIBUTES.dup @attributes = ATTRIBUTES.dup
@ -59,15 +59,6 @@ class ProcedureExportService
end end
@dossiers = dossiers.downloadable_sorted @dossiers = dossiers.downloadable_sorted
if ids
@dossiers = @dossiers.where(id: ids)
end
if since
@dossiers = @dossiers.since(since)
end
if limit
@dossiers = @dossiers.limit(limit)
end
@dossiers = @dossiers.to_a @dossiers = @dossiers.to_a
@tables = tables.map(&:to_sym) @tables = tables.map(&:to_sym)
end end

View file

@ -1,18 +1,9 @@
class ProcedureExportV2Service class ProcedureExportV2Service
attr_reader :dossiers attr_reader :dossiers
def initialize(procedure, dossiers, ids: nil, since: nil, limit: nil) def initialize(procedure, dossiers)
@procedure = procedure @procedure = procedure
@dossiers = dossiers.downloadable_sorted @dossiers = dossiers.downloadable_sorted
if ids
@dossiers = @dossiers.where(id: ids)
end
if since
@dossiers = @dossiers.since(since)
end
if limit
@dossiers = @dossiers.limit(limit)
end
@tables = [:dossiers, :etablissements, :avis] + champs_repetables_options @tables = [:dossiers, :etablissements, :avis] + champs_repetables_options
end end

View file

@ -1,9 +1,8 @@
Rails.application.config.content_security_policy do |policy| Rails.application.config.content_security_policy do |policy|
# En cas de non respect d'une des règles, faire un POST sur cette URL if Rails.env.development?
if Rails.env.production? # les CSP ne sont pas appliquées en dev: on notifie cependant une url quelconque de la violation
policy.report_uri "https://demarchessimplifieestest.report-uri.com/r/d/csp/reportOnly" # pour détecter les erreurs lors de l'ajout d'une nouvelle brique externe durant le développement
else policy.report_uri "http://#{ENV['APP_HOST']}/csp/"
policy.report_uri "http://#{ENV['APP_HOST']}/csp/" # ne pas notifier report-uri en dev/test
end end
# Whitelist image # Whitelist image
policy.img_src :self, "*.openstreetmap.org", "static.demarches-simplifiees.fr", "*.cloud.ovh.net", "stats.data.gouv.fr", "*", :data policy.img_src :self, "*.openstreetmap.org", "static.demarches-simplifiees.fr", "*.cloud.ovh.net", "stats.data.gouv.fr", "*", :data

View file

@ -12,6 +12,15 @@ describe API::V2::GraphqlController do
create(:commentaire, dossier: dossier, email: 'test@test.com') create(:commentaire, dossier: dossier, email: 'test@test.com')
dossier dossier
end end
let(:dossier1) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: 1.day.ago) }
let(:dossier2) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: 3.days.ago) }
let!(:dossier_brouillon) { create(:dossier, procedure: procedure) }
let(:dossiers) { [dossier2, dossier1, dossier] }
let(:instructeur) { create(:instructeur, followed_dossiers: dossiers) }
before do
instructeur.assign_to_procedure(procedure)
end
let(:query) do let(:query) do
"{ "{
@ -62,7 +71,8 @@ describe API::V2::GraphqlController do
request.env['HTTP_AUTHORIZATION'] = authorization_header request.env['HTTP_AUTHORIZATION'] = authorization_header
end end
it "should return demarche" do context "demarche" do
it "should be returned" do
expect(gql_errors).to eq(nil) expect(gql_errors).to eq(nil)
expect(gql_data).to eq(demarche: { expect(gql_data).to eq(demarche: {
id: procedure.to_typed_id, id: procedure.to_typed_id,
@ -73,7 +83,12 @@ describe API::V2::GraphqlController do
archivedAt: nil, archivedAt: nil,
createdAt: procedure.created_at.iso8601, createdAt: procedure.created_at.iso8601,
updatedAt: procedure.updated_at.iso8601, updatedAt: procedure.updated_at.iso8601,
groupeInstructeurs: [{ instructeurs: [], label: "défaut" }], groupeInstructeurs: [
{
instructeurs: [{ email: instructeur.email }],
label: "défaut"
}
],
champDescriptors: procedure.types_de_champ.map do |tdc| champDescriptors: procedure.types_de_champ.map do |tdc|
{ {
id: tdc.to_typed_id, id: tdc.to_typed_id,
@ -84,11 +99,39 @@ describe API::V2::GraphqlController do
} }
end, end,
dossiers: { dossiers: {
nodes: [] nodes: dossiers.map { |dossier| { id: dossier.to_typed_id } }
} }
}) })
end end
context "filter dossiers" do
let(:query) do
"{
demarche(number: #{procedure.id}) {
id
number
dossiers(createdSince: \"#{2.days.ago.iso8601}\") {
nodes {
id
}
}
}
}"
end
it "should be returned" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(demarche: {
id: procedure.to_typed_id,
number: procedure.id.to_s,
dossiers: {
nodes: [{ id: dossier1.to_typed_id }, { id: dossier.to_typed_id }]
}
})
end
end
end
context "dossier" do context "dossier" do
let(:query) do let(:query) do
"{ "{
@ -130,7 +173,7 @@ describe API::V2::GraphqlController do
}" }"
end end
it "should return dossier" do it "should be returned" do
expect(gql_errors).to eq(nil) expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossier: { expect(gql_data).to eq(dossier: {
id: dossier.to_typed_id, id: dossier.to_typed_id,
@ -146,7 +189,12 @@ describe API::V2::GraphqlController do
id: dossier.user.to_typed_id, id: dossier.user.to_typed_id,
email: dossier.user.email email: dossier.user.email
}, },
instructeurs: [], instructeurs: [
{
id: instructeur.to_typed_id,
email: instructeur.email
}
],
messages: dossier.commentaires.map do |commentaire| messages: dossier.commentaires.map do |commentaire|
{ {
body: commentaire.body, body: commentaire.body,