commit
f86bc605a7
10 changed files with 82 additions and 66 deletions
25
.github/workflows/ci.yml
vendored
25
.github/workflows/ci.yml
vendored
|
@ -55,15 +55,16 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
pattern:
|
||||
- spec/controllers/*_spec.rb
|
||||
- spec/controllers/[a-l]**/*_spec.rb
|
||||
- spec/controllers/[m-z]**/*_spec.rb
|
||||
- spec/features
|
||||
- spec/helpers spec/lib spec/middlewares
|
||||
- spec/mailers spec/jobs spec/policies
|
||||
- spec/models
|
||||
- spec/serializers spec/services
|
||||
- spec/views
|
||||
- bin/rake zeitwerk:check
|
||||
- bin/rspec spec/controllers/*_spec.rb
|
||||
- bin/rspec spec/controllers/[a-l]**/*_spec.rb
|
||||
- bin/rspec spec/controllers/[m-z]**/*_spec.rb
|
||||
- bin/rspec spec/features
|
||||
- bin/rspec spec/helpers spec/lib spec/middlewares
|
||||
- bin/rspec spec/mailers spec/jobs spec/policies
|
||||
- bin/rspec spec/models
|
||||
- bin/rspec spec/serializers spec/services
|
||||
- bin/rspec spec/views
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
|
@ -99,5 +100,9 @@ jobs:
|
|||
run: |
|
||||
bundle exec rake db:create db:schema:load db:migrate
|
||||
|
||||
- name: Setup environment variables
|
||||
run: |
|
||||
cp config/env.example .env
|
||||
|
||||
- name: Run tests
|
||||
run: bundle exec rspec ${{ matrix.pattern }}
|
||||
run: ${{ matrix.pattern }}
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -11,6 +11,7 @@
|
|||
/log/*
|
||||
!/log/.keep
|
||||
/tmp
|
||||
failing_specs.txt
|
||||
|
||||
public/uploads
|
||||
public/downloads
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -22,7 +22,7 @@ gem 'delayed_job_web'
|
|||
gem 'devise' # Gestion des comptes utilisateurs
|
||||
gem 'devise-async'
|
||||
gem 'devise-i18n'
|
||||
gem 'devise-two-factor', github: 'jason-hobbs/devise-two-factor', branch: 'master' # Rails 6.1 compatibility: https://github.com/tinfoil/devise-two-factor/issues/183
|
||||
gem 'devise-two-factor'
|
||||
gem 'discard'
|
||||
gem 'dotenv-rails', require: 'dotenv/rails-now' # dotenv should always be loaded before rails
|
||||
gem 'flipper'
|
||||
|
|
24
Gemfile.lock
24
Gemfile.lock
|
@ -1,15 +1,3 @@
|
|||
GIT
|
||||
remote: https://github.com/jason-hobbs/devise-two-factor.git
|
||||
revision: e153f16ab86de01df034672dfffa321acd891c45
|
||||
branch: master
|
||||
specs:
|
||||
devise-two-factor (3.1.0)
|
||||
activesupport (< 7.0)
|
||||
attr_encrypted (>= 1.3, < 4, != 2)
|
||||
devise
|
||||
railties (< 7.0)
|
||||
rotp (~> 6)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/mina-deploy/mina.git
|
||||
revision: 84fa84c7f7f94f9518ef9b7099396ab6676b5881
|
||||
|
@ -226,6 +214,12 @@ GEM
|
|||
devise (>= 4.0)
|
||||
devise-i18n (1.9.2)
|
||||
devise (>= 4.7.1)
|
||||
devise-two-factor (4.0.0)
|
||||
activesupport (< 6.2)
|
||||
attr_encrypted (>= 1.3, < 4, != 2)
|
||||
devise (~> 4.0)
|
||||
railties (< 6.2)
|
||||
rotp (~> 6.0)
|
||||
diff-lcs (1.4.4)
|
||||
discard (1.2.0)
|
||||
activerecord (>= 4.2, < 7)
|
||||
|
@ -403,7 +397,7 @@ GEM
|
|||
railties (>= 4)
|
||||
request_store (~> 1.0)
|
||||
logstash-event (1.2.02)
|
||||
loofah (2.9.0)
|
||||
loofah (2.9.1)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.7.1)
|
||||
|
@ -430,7 +424,7 @@ GEM
|
|||
ruby2_keywords (~> 0.0.1)
|
||||
netrc (0.11.0)
|
||||
nio4r (2.5.7)
|
||||
nokogiri (1.11.2)
|
||||
nokogiri (1.11.3)
|
||||
mini_portile2 (~> 2.5.0)
|
||||
racc (~> 1.4)
|
||||
open4 (1.3.4)
|
||||
|
@ -803,7 +797,7 @@ DEPENDENCIES
|
|||
devise
|
||||
devise-async
|
||||
devise-i18n
|
||||
devise-two-factor!
|
||||
devise-two-factor
|
||||
discard
|
||||
dotenv-rails
|
||||
factory_bot
|
||||
|
|
|
@ -109,6 +109,10 @@ Pour exécuter les tests de l'application, plusieurs possibilités :
|
|||
bin/rake spec SPEC=file_path/file_name_spec.rb
|
||||
bin/rspec file_path/file_name_spec.rb
|
||||
|
||||
- Relancer uniquement les tests qui ont échoué précédemment
|
||||
|
||||
bin/rspec --only-failures
|
||||
|
||||
### Ajout de taches à exécuter au déploiement
|
||||
|
||||
rails generate after_party:task task_name
|
||||
|
|
|
@ -16,6 +16,12 @@ class ProcedurePresentation < ApplicationRecord
|
|||
'self' => ['id', 'state']
|
||||
}
|
||||
|
||||
TABLE = 'table'
|
||||
COLUMN = 'column'
|
||||
SLASH = '/'
|
||||
TYPE_DE_CHAMP = 'type_de_champ'
|
||||
TYPE_DE_CHAMP_PRIVATE = 'type_de_champ_private'
|
||||
|
||||
belongs_to :assign_to, optional: false
|
||||
|
||||
delegate :procedure, to: :assign_to
|
||||
|
@ -65,12 +71,14 @@ class ProcedurePresentation < ApplicationRecord
|
|||
fields.concat procedure.types_de_champ
|
||||
.where.not(type_champ: explanatory_types_de_champ)
|
||||
.order(:id)
|
||||
.map { |type_de_champ| field_hash(type_de_champ.libelle, 'type_de_champ', type_de_champ.stable_id.to_s) }
|
||||
.pluck(:libelle, :stable_id)
|
||||
.map { |(libelle, stable_id)| field_hash(libelle, TYPE_DE_CHAMP, stable_id.to_s) }
|
||||
|
||||
fields.concat procedure.types_de_champ_private
|
||||
.where.not(type_champ: explanatory_types_de_champ)
|
||||
.order(:id)
|
||||
.map { |type_de_champ| field_hash(type_de_champ.libelle, 'type_de_champ_private', type_de_champ.stable_id.to_s) }
|
||||
.pluck(:libelle, :stable_id)
|
||||
.map { |(libelle, stable_id)| field_hash(libelle, TYPE_DE_CHAMP_PRIVATE, stable_id.to_s) }
|
||||
|
||||
fields
|
||||
end
|
||||
|
@ -83,11 +91,11 @@ class ProcedurePresentation < ApplicationRecord
|
|||
end
|
||||
|
||||
def displayed_fields_values(dossier)
|
||||
displayed_fields.map { |field| get_value(dossier, field['table'], field['column']) }
|
||||
displayed_fields.map { |field| get_value(dossier, field[TABLE], field[COLUMN]) }
|
||||
end
|
||||
|
||||
def sorted_ids(dossiers, instructeur)
|
||||
table, column, order = sort.values_at('table', 'column', 'order')
|
||||
table, column, order = sort.values_at(TABLE, COLUMN, 'order')
|
||||
|
||||
case table
|
||||
when 'notifications'
|
||||
|
@ -99,12 +107,12 @@ class ProcedurePresentation < ApplicationRecord
|
|||
(dossiers.order('dossiers.updated_at asc').ids - dossiers_id_with_notification) +
|
||||
dossiers_id_with_notification
|
||||
end
|
||||
when 'type_de_champ'
|
||||
when TYPE_DE_CHAMP
|
||||
dossiers
|
||||
.with_type_de_champ(column)
|
||||
.order("champs.value #{order}")
|
||||
.pluck(:id)
|
||||
when 'type_de_champ_private'
|
||||
when TYPE_DE_CHAMP_PRIVATE
|
||||
dossiers
|
||||
.with_type_de_champ_private(column)
|
||||
.order("champs.value #{order}")
|
||||
|
@ -125,7 +133,7 @@ class ProcedurePresentation < ApplicationRecord
|
|||
end
|
||||
|
||||
def filtered_ids(dossiers, statut)
|
||||
filters[statut].group_by { |filter| filter.values_at('table', 'column') } .map do |(table, column), filters|
|
||||
filters[statut].group_by { |filter| filter.values_at(TABLE, COLUMN) } .map do |(table, column), filters|
|
||||
values = filters.pluck('value')
|
||||
case table
|
||||
when 'self'
|
||||
|
@ -133,10 +141,10 @@ class ProcedurePresentation < ApplicationRecord
|
|||
.map { |v| Time.zone.parse(v).beginning_of_day rescue nil }
|
||||
.compact
|
||||
dossiers.filter_by_datetimes(column, dates)
|
||||
when 'type_de_champ'
|
||||
when TYPE_DE_CHAMP
|
||||
dossiers.with_type_de_champ(column)
|
||||
.filter_ilike(:champs, :value, values)
|
||||
when 'type_de_champ_private'
|
||||
when TYPE_DE_CHAMP_PRIVATE
|
||||
dossiers.with_type_de_champ_private(column)
|
||||
.filter_ilike(:champs_private, :value, values)
|
||||
when 'etablissement'
|
||||
|
@ -172,14 +180,14 @@ class ProcedurePresentation < ApplicationRecord
|
|||
|
||||
def eager_load_displayed_fields(dossiers)
|
||||
relations_to_include = displayed_fields
|
||||
.pluck('table')
|
||||
.pluck(TABLE)
|
||||
.reject { |table| table == 'self' }
|
||||
.map do |table|
|
||||
case table
|
||||
when 'type_de_champ'
|
||||
:champs
|
||||
when 'type_de_champ_private'
|
||||
:champs_private
|
||||
when TYPE_DE_CHAMP
|
||||
{ champs: :type_de_champ }
|
||||
when TYPE_DE_CHAMP_PRIVATE
|
||||
{ champs_private: :type_de_champ }
|
||||
else
|
||||
table
|
||||
end
|
||||
|
@ -190,9 +198,9 @@ class ProcedurePresentation < ApplicationRecord
|
|||
end
|
||||
|
||||
def human_value_for_filter(filter)
|
||||
case filter['table']
|
||||
when 'type_de_champ', 'type_de_champ_private'
|
||||
find_type_de_champ(filter['column']).dynamic_type.filter_to_human(filter['value'])
|
||||
case filter[TABLE]
|
||||
when TYPE_DE_CHAMP, TYPE_DE_CHAMP_PRIVATE
|
||||
find_type_de_champ(filter[COLUMN]).dynamic_type.filter_to_human(filter['value'])
|
||||
else
|
||||
filter['value']
|
||||
end
|
||||
|
@ -200,19 +208,19 @@ class ProcedurePresentation < ApplicationRecord
|
|||
|
||||
def add_filter(statut, field, value)
|
||||
if value.present?
|
||||
table, column = field.split('/')
|
||||
table, column = field.split(SLASH)
|
||||
label = find_field(table, column)['label']
|
||||
|
||||
case table
|
||||
when 'type_de_champ', 'type_de_champ_private'
|
||||
when TYPE_DE_CHAMP, TYPE_DE_CHAMP_PRIVATE
|
||||
value = find_type_de_champ(column).dynamic_type.human_to_filter(value)
|
||||
end
|
||||
|
||||
updated_filters = filters.dup
|
||||
updated_filters[statut] << {
|
||||
'label' => label,
|
||||
'table' => table,
|
||||
'column' => column,
|
||||
TABLE => table,
|
||||
COLUMN => column,
|
||||
'value' => value
|
||||
}
|
||||
|
||||
|
@ -221,11 +229,11 @@ class ProcedurePresentation < ApplicationRecord
|
|||
end
|
||||
|
||||
def remove_filter(statut, field, value)
|
||||
table, column = field.split('/')
|
||||
table, column = field.split(SLASH)
|
||||
|
||||
updated_filters = filters.dup
|
||||
updated_filters[statut] = filters[statut].reject do |filter|
|
||||
filter.values_at('table', 'column', 'value') == [table, column, value]
|
||||
filter.values_at(TABLE, COLUMN, 'value') == [table, column, value]
|
||||
end
|
||||
|
||||
update!(filters: updated_filters)
|
||||
|
@ -236,7 +244,7 @@ class ProcedurePresentation < ApplicationRecord
|
|||
values = []
|
||||
end
|
||||
|
||||
fields = values.map { |value| find_field(*value.split('/')) }
|
||||
fields = values.map { |value| find_field(*value.split(SLASH)) }
|
||||
|
||||
update!(displayed_fields: fields)
|
||||
|
||||
|
@ -246,15 +254,15 @@ class ProcedurePresentation < ApplicationRecord
|
|||
end
|
||||
|
||||
def update_sort(table, column)
|
||||
order = if sort.values_at('table', 'column') == [table, column]
|
||||
order = if sort.values_at(TABLE, COLUMN) == [table, column]
|
||||
sort['order'] == 'asc' ? 'desc' : 'asc'
|
||||
else
|
||||
'asc'
|
||||
end
|
||||
|
||||
update!(sort: {
|
||||
'table' => table,
|
||||
'column' => column,
|
||||
TABLE => table,
|
||||
COLUMN => column,
|
||||
'order' => order
|
||||
})
|
||||
end
|
||||
|
@ -262,11 +270,11 @@ class ProcedurePresentation < ApplicationRecord
|
|||
private
|
||||
|
||||
def field_id(field)
|
||||
field.values_at('table', 'column').join('/')
|
||||
field.values_at(TABLE, COLUMN).join(SLASH)
|
||||
end
|
||||
|
||||
def find_field(table, column)
|
||||
fields.find { |field| field.values_at('table', 'column') == [table, column] }
|
||||
fields.find { |field| field.values_at(TABLE, COLUMN) == [table, column] }
|
||||
end
|
||||
|
||||
def find_type_de_champ(column)
|
||||
|
@ -300,7 +308,7 @@ class ProcedurePresentation < ApplicationRecord
|
|||
end
|
||||
|
||||
def check_allowed_field(kind, field, extra_columns = {})
|
||||
table, column = field.values_at('table', 'column')
|
||||
table, column = field.values_at(TABLE, COLUMN)
|
||||
if !valid_column?(table, column, extra_columns)
|
||||
errors.add(kind, "#{table}.#{column} n’est pas une colonne permise")
|
||||
end
|
||||
|
@ -314,9 +322,9 @@ class ProcedurePresentation < ApplicationRecord
|
|||
dossier.send(table)&.send(column)
|
||||
when 'followers_instructeurs'
|
||||
dossier.send(table)&.map { |g| g.send(column) }&.join(', ')
|
||||
when 'type_de_champ'
|
||||
when TYPE_DE_CHAMP
|
||||
dossier.champs.find { |c| c.stable_id == column.to_i }.to_s
|
||||
when 'type_de_champ_private'
|
||||
when TYPE_DE_CHAMP_PRIVATE
|
||||
dossier.champs_private.find { |c| c.stable_id == column.to_i }.to_s
|
||||
when 'groupe_instructeur'
|
||||
dossier.groupe_instructeur.label
|
||||
|
@ -326,8 +334,8 @@ class ProcedurePresentation < ApplicationRecord
|
|||
def field_hash(label, table, column)
|
||||
{
|
||||
'label' => label,
|
||||
'table' => table,
|
||||
'column' => column
|
||||
TABLE => table,
|
||||
COLUMN => column
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -338,8 +346,8 @@ class ProcedurePresentation < ApplicationRecord
|
|||
|
||||
def valid_columns_for_table(table)
|
||||
@column_whitelist ||= fields
|
||||
.group_by { |field| field['table'] }
|
||||
.transform_values { |fields| Set.new(fields.pluck('column')) }
|
||||
.group_by { |field| field[TABLE] }
|
||||
.transform_values { |fields| Set.new(fields.pluck(COLUMN)) }
|
||||
|
||||
@column_whitelist[table] || []
|
||||
end
|
||||
|
|
|
@ -162,12 +162,16 @@ class User < ApplicationRecord
|
|||
instructeur_id.present?
|
||||
end
|
||||
|
||||
def expert?
|
||||
expert_id.present?
|
||||
end
|
||||
|
||||
def can_france_connect?
|
||||
!administrateur? && !instructeur?
|
||||
end
|
||||
|
||||
def can_be_deleted?
|
||||
administrateur.nil? && instructeur.nil? && dossiers.with_discarded.state_instruction_commencee.empty?
|
||||
!administrateur? && !instructeur? && !expert? && dossiers.with_discarded.state_instruction_commencee.empty?
|
||||
end
|
||||
|
||||
def delete_and_keep_track_dossiers(administration)
|
||||
|
|
|
@ -99,7 +99,7 @@ SKYLIGHT_AUTHENTICATION_KEY=""
|
|||
LOGRAGE_ENABLED="disabled"
|
||||
|
||||
# Service externe d'horodatage des changements de statut des dossiers (effectué quotidiennement)
|
||||
UNIVERSIGN_API_URL=""
|
||||
UNIVERSIGN_API_URL="https://ws.universign.eu/tsa/post/"
|
||||
UNIVERSIGN_USERPWD=""
|
||||
|
||||
# API Geo / Adresse
|
||||
|
@ -112,4 +112,3 @@ API_EDUCATION_URL="https://data.education.gouv.fr/api/records/1.0"
|
|||
# Modifier le nb de tentatives de relance de job si echec
|
||||
# MAX_ATTEMPTS_JOBS=25
|
||||
# MAX_ATTEMPTS_API_ENTREPRISE_JOBS=5
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ LISTE_DES_DEMARCHES_URL = [DOC_URL, "listes-des-demarches"].join("/")
|
|||
CGU_URL = ENV.fetch("CGU_URL", [DOC_URL, "cgu"].join("/"))
|
||||
MENTIONS_LEGALES_URL = ENV.fetch("MENTIONS_LEGALES_URL", [DOC_URL, "mentions-legales"].join("/"))
|
||||
ACCESSIBILITE_URL = ENV.fetch("ACCESSIBILITE_URL", [DOC_URL, "declaration-daccessibilite"].join("/"))
|
||||
API_DOC_URL = [DOC_URL, "pour-aller-plus-loin", "api"].join("/")
|
||||
API_DOC_URL = [DOC_URL, "pour-aller-plus-loin", "graphql"].join("/")
|
||||
WEBHOOK_DOC_URL = [DOC_URL, "pour-aller-plus-loin", "webhook"].join("/")
|
||||
ARCHIVAGE_DOC_URL = [DOC_URL, "pour-aller-plus-loin", "archivage-longue-duree-des-demarches"].join("/")
|
||||
DOC_INTEGRATION_MONAVIS_URL = [DOC_URL, "tutoriels", "integration-du-bouton-mon-avis"].join("/")
|
||||
|
|
|
@ -22,6 +22,7 @@ RSpec.configure do |config|
|
|||
config.color = true
|
||||
config.tty = true
|
||||
|
||||
config.example_status_persistence_file_path = 'failing_specs.txt'
|
||||
config.run_all_when_everything_filtered = true
|
||||
config.filter_run :focus => true
|
||||
|
||||
|
|
Loading…
Reference in a new issue