commit
b8a9e737a5
11 changed files with 140 additions and 70 deletions
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
"""
|
||||||
|
L‘ordre ascendant.
|
||||||
|
"""
|
||||||
|
ASC
|
||||||
|
|
||||||
|
"""
|
||||||
|
L‘ordre descendant.
|
||||||
|
"""
|
||||||
|
DESC
|
||||||
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Information about pagination in a connection.
|
Information about pagination in a connection.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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
|
||||||
|
|
6
app/graphql/types/order.rb
Normal file
6
app/graphql/types/order.rb
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
module Types
|
||||||
|
class Order < Types::BaseEnum
|
||||||
|
value('ASC', 'L‘ordre ascendant.', value: :asc)
|
||||||
|
value('DESC', 'L‘ordre descendant.', value: :desc)
|
||||||
|
end
|
||||||
|
end
|
4
app/lib/active_storage/file_not_found_error.rb
Normal file
4
app/lib/active_storage/file_not_found_error.rb
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
module ActiveStorage
|
||||||
|
# activestorage-openstack uses ActiveStorage::FileNotFoundError which only exists in rails 6
|
||||||
|
class FileNotFoundError < StandardError; end
|
||||||
|
end
|
|
@ -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: [
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,31 +71,65 @@ 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
|
||||||
expect(gql_errors).to eq(nil)
|
it "should be returned" do
|
||||||
expect(gql_data).to eq(demarche: {
|
expect(gql_errors).to eq(nil)
|
||||||
id: procedure.to_typed_id,
|
expect(gql_data).to eq(demarche: {
|
||||||
number: procedure.id.to_s,
|
id: procedure.to_typed_id,
|
||||||
title: procedure.libelle,
|
number: procedure.id.to_s,
|
||||||
description: procedure.description,
|
title: procedure.libelle,
|
||||||
state: 'brouillon',
|
description: procedure.description,
|
||||||
archivedAt: nil,
|
state: 'brouillon',
|
||||||
createdAt: procedure.created_at.iso8601,
|
archivedAt: nil,
|
||||||
updatedAt: procedure.updated_at.iso8601,
|
createdAt: procedure.created_at.iso8601,
|
||||||
groupeInstructeurs: [{ instructeurs: [], label: "défaut" }],
|
updatedAt: procedure.updated_at.iso8601,
|
||||||
champDescriptors: procedure.types_de_champ.map do |tdc|
|
groupeInstructeurs: [
|
||||||
{
|
{
|
||||||
id: tdc.to_typed_id,
|
instructeurs: [{ email: instructeur.email }],
|
||||||
label: tdc.libelle,
|
label: "défaut"
|
||||||
type: tdc.type_champ,
|
}
|
||||||
description: tdc.description,
|
],
|
||||||
required: tdc.mandatory?
|
champDescriptors: procedure.types_de_champ.map do |tdc|
|
||||||
|
{
|
||||||
|
id: tdc.to_typed_id,
|
||||||
|
label: tdc.libelle,
|
||||||
|
type: tdc.type_champ,
|
||||||
|
description: tdc.description,
|
||||||
|
required: tdc.mandatory?
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
dossiers: {
|
||||||
|
nodes: dossiers.map { |dossier| { id: dossier.to_typed_id } }
|
||||||
}
|
}
|
||||||
end,
|
})
|
||||||
dossiers: {
|
end
|
||||||
nodes: []
|
|
||||||
}
|
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
|
end
|
||||||
|
|
||||||
context "dossier" do
|
context "dossier" 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,
|
||||||
|
|
Loading…
Reference in a new issue