Merge pull request #6941 from betagouv/main

2022-02-08-02
This commit is contained in:
Paul Chavard 2022-02-08 17:24:32 +01:00 committed by GitHub
commit 7587a9910b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 442 additions and 204 deletions

View file

@ -1,15 +1,15 @@
require: require:
- rubocop/rspec/focused - rubocop-performance
- rubocop-rails
- rubocop-rspec
- ./lib/cops/add_concurrent_index.rb - ./lib/cops/add_concurrent_index.rb
- ./lib/cops/application_name.rb - ./lib/cops/application_name.rb
- ./lib/cops/unscoped.rb - ./lib/cops/unscoped.rb
inherit_gem:
rubocop-rails_config:
- config/rails.yml
AllCops: AllCops:
TargetRubyVersion: 2.7 TargetRubyVersion: 3.0
DisabledByDefault: true
SuggestExtensions: false
Exclude: Exclude:
- "db/schema.rb" - "db/schema.rb"
- "db/migrate/20190730153555_recreate_structure.rb" - "db/migrate/20190730153555_recreate_structure.rb"
@ -644,6 +644,9 @@ Performance/RedundantMatch:
Performance/RedundantMerge: Performance/RedundantMerge:
Enabled: true Enabled: true
Style/HashTransformValues:
Enabled: true
Style/RedundantSortBy: Style/RedundantSortBy:
Enabled: true Enabled: true
@ -874,7 +877,7 @@ Rails/WhereExists:
Rails/WhereNot: Rails/WhereNot:
Enabled: true Enabled: true
RSpec/Focused: RSpec/Focus:
Enabled: true Enabled: true
Security/Eval: Security/Eval:
@ -1373,4 +1376,3 @@ Style/YodaCondition:
Style/ZeroLengthPredicate: Style/ZeroLengthPredicate:
Enabled: true Enabled: true

View file

@ -111,8 +111,9 @@ group :development do
gem 'rack-mini-profiler' gem 'rack-mini-profiler'
gem 'rails-erd', require: false # generates `doc/database_models.pdf` gem 'rails-erd', require: false # generates `doc/database_models.pdf`
gem 'rubocop', require: false gem 'rubocop', require: false
gem 'rubocop-rails_config' gem 'rubocop-performance', require: false
gem 'rubocop-rspec-focused', require: false gem 'rubocop-rails', require: false
gem 'rubocop-rspec', require: false
gem 'scss_lint', require: false gem 'scss_lint', require: false
gem 'web-console' gem 'web-console'
end end

View file

@ -620,8 +620,6 @@ GEM
unicode-display_width (>= 1.4.0, < 3.0) unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.4.1) rubocop-ast (1.4.1)
parser (>= 2.7.1.5) parser (>= 2.7.1.5)
rubocop-packaging (0.5.1)
rubocop (>= 0.89, < 2.0)
rubocop-performance (1.9.2) rubocop-performance (1.9.2)
rubocop (>= 0.90.0, < 2.0) rubocop (>= 0.90.0, < 2.0)
rubocop-ast (>= 0.4.0) rubocop-ast (>= 0.4.0)
@ -629,15 +627,9 @@ GEM
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
rack (>= 1.1) rack (>= 1.1)
rubocop (>= 0.90.0, < 2.0) rubocop (>= 0.90.0, < 2.0)
rubocop-rails_config (1.3.1) rubocop-rspec (2.4.0)
railties (>= 5.0) rubocop (~> 1.0)
rubocop (>= 1.8) rubocop-ast (>= 1.1.0)
rubocop-ast (>= 1.0.1)
rubocop-packaging (~> 0.5)
rubocop-performance (~> 1.3)
rubocop-rails (~> 2.0)
rubocop-rspec-focused (1.0.0)
rubocop (>= 0.51)
ruby-graphviz (1.2.5) ruby-graphviz (1.2.5)
rexml rexml
ruby-progressbar (1.11.0) ruby-progressbar (1.11.0)
@ -874,8 +866,9 @@ DEPENDENCIES
rspec-rails rspec-rails
rspec_junit_formatter rspec_junit_formatter
rubocop rubocop
rubocop-rails_config rubocop-performance
rubocop-rspec-focused rubocop-rails
rubocop-rspec
ruby-saml-idp ruby-saml-idp
sanitize-url sanitize-url
sassc-rails sassc-rails

View file

@ -295,7 +295,6 @@ $users-breakpoint: 950px;
font-size: 24px; font-size: 24px;
font-weight: bold; font-weight: bold;
margin-top: 13px; margin-top: 13px;
color: #FFFFFF;
&.grey { &.grey {
color: $g700; color: $g700;
@ -303,15 +302,19 @@ $users-breakpoint: 950px;
} }
.cta-panel-explanation { .cta-panel-explanation {
font-size: 24px; font-size: 22px;
margin-bottom: 10px; margin-bottom: 10px;
color: #FFFFFF;
&.grey { &.grey {
color: $g700; color: $g700;
} }
} }
.half .cta-panel-title,
.half .cta-panel-explanation {
text-align: center;
}
.role-panel-title { .role-panel-title {
font-size: 30px; font-size: 30px;
font-weight: bold; font-weight: bold;
@ -357,7 +360,7 @@ $cta-panel-button-border-size: 2px;
@include vertical-padding(15px); @include vertical-padding(15px);
display: block; display: block;
border-radius: 100px; border-radius: 100px;
font-size: 24px; font-size: 22px;
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
margin-top: 20px; margin-top: 20px;

View file

@ -3,12 +3,17 @@ module Administrateurs
before_action :retrieve_procedure before_action :retrieve_procedure
def edit def edit
@attestation_template = @procedure.attestation_template || AttestationTemplate.new(procedure: @procedure) @attestation_template = build_attestation
end end
def update def update
attestation_template = @procedure.attestation_template attestation_template = @procedure.draft_attestation_template.revise!
if attestation_template.update(activated_attestation_params) if attestation_template.update(activated_attestation_params)
AttestationTemplate
.where(id: @procedure.revisions.pluck(:attestation_template_id).compact)
.update_all(activated: attestation_template.activated?)
flash.notice = "L'attestation a bien été modifiée" flash.notice = "L'attestation a bien été modifiée"
else else
flash.alert = attestation_template.errors.full_messages.join('<br>') flash.alert = attestation_template.errors.full_messages.join('<br>')
@ -18,7 +23,7 @@ module Administrateurs
end end
def create def create
attestation_template = AttestationTemplate.new(activated_attestation_params.merge(procedure_id: @procedure.id)) attestation_template = build_attestation(activated_attestation_params)
if attestation_template.save if attestation_template.save
flash.notice = "L'attestation a bien été sauvegardée" flash.notice = "L'attestation a bien été sauvegardée"
@ -30,14 +35,19 @@ module Administrateurs
end end
def preview def preview
attestation = @procedure.attestation_template || AttestationTemplate.new @attestation = build_attestation.render_attributes_for({})
@attestation = attestation.render_attributes_for({})
render 'administrateurs/attestation_templates/show', formats: [:pdf] render 'administrateurs/attestation_templates/show', formats: [:pdf]
end end
private private
def build_attestation(attributes = {})
attestation_template = @procedure.draft_attestation_template || @procedure.draft_revision.build_attestation_template
attestation_template.attributes = attributes
attestation_template
end
def activated_attestation_params def activated_attestation_params
# cache result to avoid multiple uninterlaced computations # cache result to avoid multiple uninterlaced computations
if @activated_attestation_params.nil? if @activated_attestation_params.nil?

View file

@ -0,0 +1,38 @@
# A Mail delivery method that randomly balances the actual delivery between different
# methods.
#
# Usage:
#
# ```ruby
# ActionMailer::Base.add_delivery_method :balancer, BalancerDeliveryMethod
# config.action_mailer.balancer_settings = {
# smtp: 25,
# sendmail: 75
# }
# config.action_mailer.delivery_method = :balancer
# ```
#
# Be sure to restart your server when you modify this file.
class BalancerDeliveryMethod
# Allows configuring the random number generator used for selecting a delivery method,
# mostly for testing purposes.
mattr_accessor :random, default: Random.new
def initialize(settings)
@delivery_methods = settings
end
def deliver!(mail)
balanced_delivery_method = delivery_method(mail)
ApplicationMailer.wrap_delivery_behavior(mail, balanced_delivery_method)
mail.deliver
end
private
def delivery_method(mail)
@delivery_methods
.flat_map { |delivery_method, weight| [delivery_method] * weight }
.sample(random: self.class.random)
end
end

View file

@ -15,7 +15,8 @@ class AttestationTemplate < ApplicationRecord
include ActionView::Helpers::NumberHelper include ActionView::Helpers::NumberHelper
include TagsSubstitutionConcern include TagsSubstitutionConcern
belongs_to :procedure, optional: false belongs_to :procedure, optional: true
has_many :revisions, class_name: 'ProcedureRevision', inverse_of: :attestation_template, dependent: :nullify
has_one_attached :logo has_one_attached :logo
has_one_attached :signature has_one_attached :signature
@ -103,6 +104,25 @@ class AttestationTemplate < ApplicationRecord
} }
end end
def revise!
if revisions.size > 1
attestation_template = dup
attestation_template.save!
revisions
.last
.procedure
.draft_revision
.update!(attestation_template: attestation_template)
attestation_template
else
self
end
end
def procedure
revisions.last&.procedure || super
end
private private
def used_tags def used_tags

View file

@ -720,9 +720,11 @@ class Dossier < ApplicationRecord
{ lon: lon, lat: lat, zoom: zoom } { lon: lon, lat: lat, zoom: zoom }
end end
def unspecified_attestation_champs def attestation_template
attestation_template = procedure.attestation_template revision.attestation_template
end
def unspecified_attestation_champs
if attestation_template&.activated? if attestation_template&.activated?
attestation_template.unspecified_champs_for_dossier(self) attestation_template.unspecified_champs_for_dossier(self)
else else
@ -731,8 +733,8 @@ class Dossier < ApplicationRecord
end end
def build_attestation def build_attestation
if procedure.attestation_template&.activated? if attestation_template&.activated?
procedure.attestation_template.attestation_for(self) attestation_template.attestation_for(self)
end end
end end

View file

@ -77,12 +77,15 @@ class Procedure < ApplicationRecord
has_many :published_types_de_champ_private, through: :published_revision, source: :types_de_champ_private has_many :published_types_de_champ_private, through: :published_revision, source: :types_de_champ_private
has_many :draft_types_de_champ, through: :draft_revision, source: :types_de_champ has_many :draft_types_de_champ, through: :draft_revision, source: :types_de_champ
has_many :draft_types_de_champ_private, through: :draft_revision, source: :types_de_champ_private has_many :draft_types_de_champ_private, through: :draft_revision, source: :types_de_champ_private
has_one :draft_attestation_template, through: :draft_revision, source: :attestation_template
has_one :published_attestation_template, through: :published_revision, source: :attestation_template
has_many :experts_procedures, dependent: :destroy has_many :experts_procedures, dependent: :destroy
has_many :experts, through: :experts_procedures has_many :experts, through: :experts_procedures
has_one :module_api_carto, dependent: :destroy has_one :module_api_carto, dependent: :destroy
has_one :attestation_template, dependent: :destroy has_one :attestation_template, dependent: :destroy
has_many :attestation_templates, through: :revisions, source: :attestation_template
belongs_to :parent_procedure, class_name: 'Procedure', optional: true belongs_to :parent_procedure, class_name: 'Procedure', optional: true
belongs_to :canonical_procedure, class_name: 'Procedure', optional: true belongs_to :canonical_procedure, class_name: 'Procedure', optional: true
@ -438,7 +441,8 @@ class Procedure < ApplicationRecord
}, },
revision_types_de_champ_private: { revision_types_de_champ_private: {
type_de_champ: :types_de_champ type_de_champ: :types_de_champ
} },
attestation_template: []
} }
} }
include_list[:groupe_instructeurs] = :instructeurs if !is_different_admin include_list[:groupe_instructeurs] = :instructeurs if !is_different_admin
@ -576,13 +580,17 @@ class Procedure < ApplicationRecord
touch(:whitelisted_at) touch(:whitelisted_at)
end end
def active_attestation_template
published_attestation_template || draft_attestation_template
end
def closed_mail_template_attestation_inconsistency_state def closed_mail_template_attestation_inconsistency_state
# As an optimization, dont check the predefined templates (they are presumed correct) # As an optimization, dont check the predefined templates (they are presumed correct)
if closed_mail.present? if closed_mail.present?
tag_present = closed_mail.body.to_s.include?("--lien attestation--") tag_present = closed_mail.body.to_s.include?("--lien attestation--")
if attestation_template&.activated? && !tag_present if active_attestation_template&.activated? && !tag_present
:missing_tag :missing_tag
elsif !attestation_template&.activated? && tag_present elsif !active_attestation_template&.activated? && tag_present
:extraneous_tag :extraneous_tag
end end
end end

View file

@ -2,15 +2,17 @@
# #
# Table name: procedure_revisions # Table name: procedure_revisions
# #
# id :bigint not null, primary key # id :bigint not null, primary key
# published_at :datetime # published_at :datetime
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# procedure_id :bigint not null # attestation_template_id :bigint
# procedure_id :bigint not null
# #
class ProcedureRevision < ApplicationRecord class ProcedureRevision < ApplicationRecord
self.implicit_order_column = :created_at self.implicit_order_column = :created_at
belongs_to :procedure, -> { with_discarded }, inverse_of: :revisions, optional: false belongs_to :procedure, -> { with_discarded }, inverse_of: :revisions, optional: false
belongs_to :attestation_template, inverse_of: :revisions, optional: true, dependent: :destroy
has_many :dossiers, inverse_of: :revision, foreign_key: :revision_id has_many :dossiers, inverse_of: :revision, foreign_key: :revision_id
@ -125,6 +127,10 @@ class ProcedureRevision < ApplicationRecord
) )
end end
def attestation_template
super || procedure.attestation_template
end
private private
def compare_types_de_champ(from_tdc, to_tdc) def compare_types_de_champ(from_tdc, to_tdc)

View file

@ -138,7 +138,7 @@
.procedure-grid .procedure-grid
= link_to edit_admin_procedure_attestation_template_path(@procedure), class: 'card-admin' do = link_to edit_admin_procedure_attestation_template_path(@procedure), class: 'card-admin' do
- if @procedure.attestation_template.present? && @procedure.attestation_template.activated - if @procedure.draft_attestation_template&.activated?
%div %div
%span.icon.accept %span.icon.accept
%p.card-admin-status-accept Activée %p.card-admin-status-accept Activée

View file

@ -70,28 +70,33 @@ MATOMO_ENABLED="disabled"
MATOMO_ID="" MATOMO_ID=""
MATOMO_HOST="matomo.organisme.fr" MATOMO_HOST="matomo.organisme.fr"
# SMTP Provider: Send In Blue # Default SMTP Provider: Mailjet
MAILJET_API_KEY=""
MAILJET_SECRET_KEY=""
# Alternate SMTP Provider: SendInBlue
SENDINBLUE_ENABLED="disabled" SENDINBLUE_ENABLED="disabled"
SENDINBLUE_BALANCING=""
SENDINBLUE_BALANCING_VALUE=""
SENDINBLUE_CLIENT_KEY="" SENDINBLUE_CLIENT_KEY=""
SENDINBLUE_SMTP_KEY="" SENDINBLUE_SMTP_KEY=""
SENDINBLUE_USER_NAME="" SENDINBLUE_USER_NAME=""
# SENDINBLUE_LOGIN_URL="https://app.sendinblue.com/account/saml/login/truc" # SENDINBLUE_LOGIN_URL="https://app.sendinblue.com/account/saml/login/truc"
# SMTP Provider: Mailjet # Ratio of emails sent using SendInBlue
MAILJET_API_KEY="" # When enabled, N % of emails will be sent using SendInBlue
MAILJET_SECRET_KEY="" # (and the others using the default SMTP provider)
SENDINBLUE_BALANCING="disabled"
SENDINBLUE_BALANCING_VALUE="50"
# Alternate SMTP Provider: Mailtrap (mail catcher for staging environments)
# When enabled, all emails will be sent using this provided
MAILTRAP_ENABLED="disabled"
MAILTRAP_USERNAME=""
MAILTRAP_PASSWORD=""
# External service: live chat for admins (specific to démarches-simplifiées.fr) # External service: live chat for admins (specific to démarches-simplifiées.fr)
CRISP_ENABLED="disabled" CRISP_ENABLED="disabled"
CRISP_CLIENT_KEY="" CRISP_CLIENT_KEY=""
# External service: mail catcher for staging environments (specific to démarches-simplifiées.fr)
MAILTRAP_ENABLED="disabled"
MAILTRAP_USERNAME=""
MAILTRAP_PASSWORD=""
# API Entreprise credentials # API Entreprise credentials
# https://api.gouv.fr/api/api-entreprise.html # https://api.gouv.fr/api/api-entreprise.html
API_ENTREPRISE_KEY="" API_ENTREPRISE_KEY=""

View file

@ -75,40 +75,13 @@ Rails.application.configure do
config.assets.raise_runtime_errors = true config.assets.raise_runtime_errors = true
# Action Mailer settings # Action Mailer settings
config.action_mailer.delivery_method = :letter_opener
if ENV['SENDINBLUE_ENABLED'] == 'enabled' config.action_mailer.default_url_options = {
config.action_mailer.delivery_method = :smtp host: 'localhost',
config.action_mailer.smtp_settings = { port: 3000
user_name: Rails.application.secrets.sendinblue[:username], }
password: Rails.application.secrets.sendinblue[:smtp_key], config.action_mailer.asset_host = "http://" + ENV['APP_HOST']
address: 'smtp-relay.sendinblue.com',
domain: 'smtp-relay.sendinblue.com',
port: '587',
authentication: :cram_md5
}
else
# https://usehelo.com
if ENV['HELO_ENABLED'] == 'enabled'
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
user_name: 'demarches-simplifiees',
password: '',
address: '127.0.0.1',
domain: '127.0.0.1',
port: ENV.fetch('HELO_PORT', '2525'),
authentication: :plain
}
else
config.action_mailer.delivery_method = :letter_opener_web
end
config.action_mailer.default_url_options = {
host: 'localhost',
port: 3000
}
config.action_mailer.asset_host = "http://" + ENV['APP_HOST']
end
Rails.application.routes.default_url_options = { Rails.application.routes.default_url_options = {
host: 'localhost', host: 'localhost',

View file

@ -1,4 +1,5 @@
require "active_support/core_ext/integer/time" require "active_support/core_ext/integer/time"
require Rails.root.join("app/lib/balancer_delivery_method")
Rails.application.configure do Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb. # Settings specified here will take precedence over those in config/application.rb.
@ -76,25 +77,19 @@ Rails.application.configure do
# config.action_mailer.raise_delivery_errors = false # config.action_mailer.raise_delivery_errors = false
if ENV['MAILTRAP_ENABLED'] == 'enabled' if ENV['MAILTRAP_ENABLED'] == 'enabled'
config.action_mailer.delivery_method = :smtp config.action_mailer.delivery_method = :mailtrap
config.action_mailer.smtp_settings = {
user_name: Rails.application.secrets.mailtrap[:username], elsif ENV['SENDINBLUE_ENABLED'] == 'enabled' && ENV['SENDINBLUE_BALANCING'] == 'enabled'
password: Rails.application.secrets.mailtrap[:password], ActionMailer::Base.add_delivery_method :balancer, BalancerDeliveryMethod
address: 'smtp.mailtrap.io', config.action_mailer.balancer_settings = {
domain: 'smtp.mailtrap.io', sendinblue: ENV.fetch('SENDINBLUE_BALANCING_VALUE').to_i,
port: '2525', mailjet: 100 - ENV.fetch('SENDINBLUE_BALANCING_VALUE').to_i
authentication: :cram_md5
} }
config.action_mailer.delivery_method = :balancer
elsif ENV['SENDINBLUE_ENABLED'] == 'enabled' elsif ENV['SENDINBLUE_ENABLED'] == 'enabled'
config.action_mailer.delivery_method = :smtp config.action_mailer.delivery_method = :sendinblue
config.action_mailer.smtp_settings = {
user_name: Rails.application.secrets.sendinblue[:username],
password: Rails.application.secrets.sendinblue[:smtp_key],
address: 'smtp-relay.sendinblue.com',
domain: 'smtp-relay.sendinblue.com',
port: '587',
authentication: :cram_md5
}
else else
config.action_mailer.delivery_method = :mailjet config.action_mailer.delivery_method = :mailjet
end end

View file

@ -1,7 +1,9 @@
# rubocop:disable DS/ApplicationName # rubocop:disable DS/ApplicationName
# API URLs # API URLs
API_ADRESSE_URL = ENV.fetch("API_ADRESSE_URL", "https://api-adresse.data.gouv.fr")
API_ENTREPRISE_URL = ENV.fetch("API_ENTREPRISE_URL", "https://entreprise.api.gouv.fr/v2") API_ENTREPRISE_URL = ENV.fetch("API_ENTREPRISE_URL", "https://entreprise.api.gouv.fr/v2")
API_EDUCATION_URL = ENV.fetch("API_EDUCATION_URL", "https://data.education.gouv.fr/api/records/1.0") API_EDUCATION_URL = ENV.fetch("API_EDUCATION_URL", "https://data.education.gouv.fr/api/records/1.0")
API_GEO_URL = ENV.fetch("API_GEO_URL", "https://geo.api.gouv.fr")
API_PARTICULIER_URL = ENV.fetch("API_PARTICULIER_URL", "https://particulier.api.gouv.fr/api") API_PARTICULIER_URL = ENV.fetch("API_PARTICULIER_URL", "https://particulier.api.gouv.fr/api")
HELPSCOUT_API_URL = ENV.fetch("HELPSCOUT_API_URL", "https://api.helpscout.net/v2") HELPSCOUT_API_URL = ENV.fetch("HELPSCOUT_API_URL", "https://api.helpscout.net/v2")
PIPEDRIVE_API_URL = ENV.fetch("PIPEDRIVE_API_URL", "https://api.pipedrive.com/v1") PIPEDRIVE_API_URL = ENV.fetch("PIPEDRIVE_API_URL", "https://api.pipedrive.com/v1")
@ -11,7 +13,7 @@ UNIVERSIGN_API_URL = ENV.fetch("UNIVERSIGN_API_URL", "https://ws.universign.eu/t
FEATURE_UPVOTE_URL = ENV.fetch("FEATURE_UPVOTE_URL", "https://demarches-simplifiees.featureupvote.com") FEATURE_UPVOTE_URL = ENV.fetch("FEATURE_UPVOTE_URL", "https://demarches-simplifiees.featureupvote.com")
# Internal URLs # Internal URLs
FOG_BASE_URL = "https://static.demarches-simplifiees.fr" FOG_OPENSTACK_URL = ENV.fetch("FOG_OPENSTACK_URL", "https://static.demarches-simplifiees.fr")
# External services URLs # External services URLs
WEBINAIRE_URL = "https://app.livestorm.co/demarches-simplifiees" WEBINAIRE_URL = "https://app.livestorm.co/demarches-simplifiees"

View file

@ -4,30 +4,45 @@
# For further information see the following documentation # For further information see the following documentation
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
# rubocop:disable DS/ApplicationName
Rails.application.config.content_security_policy do |policy| Rails.application.config.content_security_policy do |policy|
# Whitelist image # Whitelist image
policy.img_src :self, "*.openstreetmap.org", "static.demarches-simplifiees.fr", "*.cloud.ovh.net", "stats.data.gouv.fr", "*", :data, :blob images_whitelist = ["*.openstreetmap.org", "*.cloud.ovh.net", "*"]
images_whitelist << URI(FOG_OPENSTACK_URL).host if FOG_OPENSTACK_URL.present?
images_whitelist << URI(MATOMO_IFRAME_URL).host if MATOMO_IFRAME_URL.present?
policy.img_src(:self, :data, :blob, *images_whitelist)
# Whitelist JS: nous, sendinblue et matomo # Whitelist JS: nous, sendinblue et matomo
# miniprofiler et nous avons quelques boutons inline :( # miniprofiler et nous avons quelques boutons inline :(
policy.script_src :self, "stats.data.gouv.fr", "*.sendinblue.com", "*.crisp.chat", "crisp.chat", "*.sibautomation.com", "sibautomation.com", 'cdn.jsdelivr.net', 'maxcdn.bootstrapcdn.com', 'code.jquery.com', :unsafe_eval, :unsafe_inline, :blob scripts_whitelist = ["*.sendinblue.com", "*.crisp.chat", "crisp.chat", "*.sibautomation.com", "sibautomation.com", "cdn.jsdelivr.net", "maxcdn.bootstrapcdn.com", "code.jquery.com"]
scripts_whitelist << URI(MATOMO_IFRAME_URL).host if MATOMO_IFRAME_URL.present?
policy.script_src(:self, :unsafe_eval, :unsafe_inline, :blob, *scripts_whitelist)
# Pour les CSS, on a beaucoup de style inline et quelques balises <style> # Pour les CSS, on a beaucoup de style inline et quelques balises <style>
# c'est trop compliqué pour être rectifié immédiatement (et sans valeur ajoutée: # c'est trop compliqué pour être rectifié immédiatement (et sans valeur ajoutée:
# c'est hardcodé dans les vues, donc pas injectable). # c'est hardcodé dans les vues, donc pas injectable).
policy.style_src :self, "*.crisp.chat", "crisp.chat", 'cdn.jsdelivr.net', 'maxcdn.bootstrapcdn.com', :unsafe_inline policy.style_src(:self, "*.crisp.chat", "crisp.chat", 'cdn.jsdelivr.net', 'maxcdn.bootstrapcdn.com', :unsafe_inline)
policy.connect_src :self, "wss://*.crisp.chat", "*.crisp.chat", "*.demarches-simplifiees.fr", "in-automate.sendinblue.com", "app.franceconnect.gouv.fr", "sentry.io", "geo.api.gouv.fr", "api-adresse.data.gouv.fr", "openmaptiles.geo.data.gouv.fr", "openmaptiles.github.io", "tiles.geo.api.gouv.fr", "wxs.ign.fr", "data.education.gouv.fr"
connect_whitelist = ["wss://*.crisp.chat", "*.crisp.chat", "in-automate.sendinblue.com", "app.franceconnect.gouv.fr", "sentry.io", "openmaptiles.geo.data.gouv.fr", "openmaptiles.github.io", "tiles.geo.api.gouv.fr", "wxs.ign.fr"]
connect_whitelist << URI(API_ADRESSE_URL).host if API_ADRESSE_URL.present?
connect_whitelist << URI(API_EDUCATION_URL).host if API_EDUCATION_URL.present?
connect_whitelist << URI(API_GEO_URL).host if API_GEO_URL.present?
connect_whitelist << "*.#{ENV.fetch('APP_HOST', 'localhost:3000')}"
policy.connect_src(:self, *connect_whitelist)
# Pour tout le reste, par défaut on accepte uniquement ce qui vient de chez nous # Pour tout le reste, par défaut on accepte uniquement ce qui vient de chez nous
# et dans la notification on inclue la source de l'erreur # et dans la notification on inclue la source de l'erreur
policy.default_src :self, :data, :blob, :report_sample, "fonts.gstatic.com", "in-automate.sendinblue.com", "player.vimeo.com", "app.franceconnect.gouv.fr", "sentry.io", "static.demarches-simplifiees.fr", "*.crisp.chat", "crisp.chat", "*.crisp.help", "*.sibautomation.com", "sibautomation.com", "data" default_whitelist = ["fonts.gstatic.com", "in-automate.sendinblue.com", "player.vimeo.com", "app.franceconnect.gouv.fr", "sentry.io", "*.crisp.chat", "crisp.chat", "*.crisp.help", "*.sibautomation.com", "sibautomation.com", "data"]
default_whitelist << URI(FOG_OPENSTACK_URL).host if FOG_OPENSTACK_URL.present?
policy.default_src(:self, :data, :blob, :report_sample, *default_whitelist)
if Rails.env.development? if Rails.env.development?
# Les CSP ne sont pas appliquées en dev: on notifie cependant une url quelconque de la violation # Les CSP ne sont pas appliquées en dev: on notifie cependant une url quelconque de la violation
# pour détecter les erreurs lors de l'ajout d'une nouvelle brique externe durant le développement # pour détecter les erreurs lors de l'ajout d'une nouvelle brique externe durant le développement
policy.report_uri "http://#{ENV['APP_HOST']}/csp/" policy.report_uri "http://#{ENV.fetch('APP_HOST', 'localhost:3000')}/csp/"
# En développement, quand bin/webpack-dev-server est utilisé, on autorise les requêtes faites par le live-reload # En développement, quand bin/webpack-dev-server est utilisé, on autorise les requêtes faites par le live-reload
policy.connect_src(*policy.connect_src, "ws://localhost:3035", "http://localhost:3035") policy.connect_src(*policy.connect_src, "ws://localhost:3035", "http://localhost:3035")
end end
end end
# rubocop:enable DS/ApplicationName
# If you are using UJS then enable automatic nonce generation # If you are using UJS then enable automatic nonce generation
# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }

View file

@ -1,14 +0,0 @@
# We want to register an interceptor, but we can't make the action idempotent
# (because there's no way to peek at the currently registered interceptors).
#
# To make zeitwerk happy, instead signal that we don't want the
# DynamicSmtpSettingsInterceptor constant to be auto-loaded, by:
# - adding it to a non-autoloaded-path (/lib),
# - requiring it explicitely.
#
# See https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#autoloading-when-the-application-boots
require 'action_mailer/dynamic_smtp_settings_interceptor'
ActiveSupport.on_load(:action_mailer) do
ActionMailer::Base.register_interceptor DynamicSmtpSettingsInterceptor
end

View file

@ -0,0 +1,17 @@
if ENV['HELO_ENABLED'] == 'enabled'
ActiveSupport.on_load(:action_mailer) do
module Helo
class SMTP < ::Mail::SMTP; end
end
ActionMailer::Base.add_delivery_method :helo, Helo::SMTP
ActionMailer::Base.helo_settings = {
user_name: 'demarches-simplifiees',
password: '',
address: '127.0.0.1',
domain: '127.0.0.1',
port: ENV.fetch('HELO_PORT', '2525'),
authentication: :plain
}
end
end

View file

@ -0,0 +1,17 @@
if ENV.fetch('MAILTRAP_ENABLED') == 'enabled'
ActiveSupport.on_load(:action_mailer) do
module Mailtrap
class SMTP < ::Mail::SMTP; end
end
ActionMailer::Base.add_delivery_method :mailtrap, Mailtrap::SMTP
ActionMailer::Base.mailtrap_settings = {
user_name: Rails.application.secrets.mailtrap[:username],
password: Rails.application.secrets.mailtrap[:password],
address: 'smtp.mailtrap.io',
domain: 'smtp.mailtrap.io',
port: '2525',
authentication: :cram_md5
}
end
end

View file

@ -1,5 +1,23 @@
require 'sib-api-v3-sdk' if ENV.fetch('SENDINBLUE_ENABLED') == 'enabled'
require 'sib-api-v3-sdk'
SibApiV3Sdk.configure do |config| ActiveSupport.on_load(:action_mailer) do
config.api_key['api-key'] = ENV.fetch('SENDINBLUE_API_V3_KEY', '') module Sendinblue
class SMTP < ::Mail::SMTP; end
end
ActionMailer::Base.add_delivery_method :sendinblue, Sendinblue::SMTP
ActionMailer::Base.sendinblue_settings = {
user_name: Rails.application.secrets.sendinblue[:username],
password: Rails.application.secrets.sendinblue[:smtp_key],
address: 'smtp-relay.sendinblue.com',
domain: 'smtp-relay.sendinblue.com',
port: '587',
authentication: :cram_md5
}
end
SibApiV3Sdk.configure do |config|
config.api_key['api-key'] = Rails.application.secrets.sendinblue[:api_v3_key]
end
end end

View file

@ -0,0 +1,5 @@
class AddAttestationTemplateIdToProcedureRevisions < ActiveRecord::Migration[6.1]
def change
add_reference :procedure_revisions, :attestation_template, foreign_key: { to_table: :attestation_templates }, null: true, index: true
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2022_01_27_135056) do ActiveRecord::Schema.define(version: 2022_01_28_135056) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -593,6 +593,8 @@ ActiveRecord::Schema.define(version: 2022_01_27_135056) do
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.datetime "published_at" t.datetime "published_at"
t.bigint "attestation_template_id"
t.index ["attestation_template_id"], name: "index_procedure_revisions_on_attestation_template_id"
t.index ["procedure_id"], name: "index_procedure_revisions_on_procedure_id" t.index ["procedure_id"], name: "index_procedure_revisions_on_procedure_id"
end end
@ -872,6 +874,7 @@ ActiveRecord::Schema.define(version: 2022_01_27_135056) do
add_foreign_key "procedure_revision_types_de_champ", "procedure_revision_types_de_champ", column: "parent_id" add_foreign_key "procedure_revision_types_de_champ", "procedure_revision_types_de_champ", column: "parent_id"
add_foreign_key "procedure_revision_types_de_champ", "procedure_revisions", column: "revision_id" add_foreign_key "procedure_revision_types_de_champ", "procedure_revisions", column: "revision_id"
add_foreign_key "procedure_revision_types_de_champ", "types_de_champ" add_foreign_key "procedure_revision_types_de_champ", "types_de_champ"
add_foreign_key "procedure_revisions", "attestation_templates"
add_foreign_key "procedure_revisions", "procedures" add_foreign_key "procedure_revisions", "procedures"
add_foreign_key "procedures", "procedure_revisions", column: "draft_revision_id" add_foreign_key "procedures", "procedure_revisions", column: "draft_revision_id"
add_foreign_key "procedures", "procedure_revisions", column: "published_revision_id" add_foreign_key "procedures", "procedure_revisions", column: "published_revision_id"

View file

@ -1,22 +0,0 @@
# Note: this class is instanciated when being added as an interceptor
# during the app initialization.
#
# If you edit this file in development env, you will need to restart
# the app to see the changes.
class DynamicSmtpSettingsInterceptor
def self.delivering_email(message)
if ENV['SENDINBLUE_BALANCING'] == 'enabled'
if rand(0..99) < ENV['SENDINBLUE_BALANCING_VALUE'].to_i
message.delivery_method.settings = {
user_name: ENV['SENDINBLUE_USER_NAME'],
password: ENV['SENDINBLUE_SMTP_KEY'],
address: 'smtp-relay.sendinblue.com',
domain: 'smtp-relay.sendinblue.com',
port: '587',
authentication: :cram_md5
}
end
end
end
end

View file

@ -1,20 +1,26 @@
namespace :after_party do namespace :after_party do
desc 'Deployment task: backfill_expert_id_on_avis_table' desc 'Deployment task: backfill_expert_id_on_avis_table'
task backfill_experts_procedure_id_on_avis_table: :environment do task backfill_experts_procedure_id_on_avis_table: :environment do
puts "Running deploy task 'backfill_expert_id_on_avis_table'" puts "Running deploy task 'backfill_experts_procedure_id_on_avis_table'"
# rubocop:disable DS/Unscoped # rubocop:disable DS/Unscoped
# rubocop:disable Rails/PluckInWhere # rubocop:disable Rails/PluckInWhere
Instructeur.includes(:user) instructeurs = Instructeur.includes(:user).where.not(users: { instructeur_id: nil })
.where(id: Avis.unscoped.pluck(:instructeur_id))
.where.not(users: { instructeur_id: nil }) instructeurs =
.find_each do |instructeur| if Avis.column_names.include?("instructeur_id")
instructeurs.where(id: Avis.unscoped.pluck(:instructeur_id))
else
instructeurs.where(id: Avis.unscoped.where(claimant_type: [nil, "Instructeur"]).pluck(:claimant_id))
end
instructeurs.find_each do |instructeur|
user = instructeur.user user = instructeur.user
User.create_or_promote_to_expert(user.email, SecureRandom.hex) User.create_or_promote_to_expert(user.email, SecureRandom.hex)
user.reload user.reload
# rubocop:enable DS/Unscoped # rubocop:enable DS/Unscoped
# rubocop:enable Rails/PluckInWhere # rubocop:enable Rails/PluckInWhere
instructeur.avis.each do |avis| Avis.where(claimant: instructeur).each do |avis|
experts_procedure = ExpertsProcedure.find_or_create_by(expert: user.expert, procedure: avis.procedure) experts_procedure = ExpertsProcedure.find_or_create_by(expert: user.expert, procedure: avis.procedure)
avis.update_column(:experts_procedure_id, experts_procedure.id) avis.update_column(:experts_procedure_id, experts_procedure.id)
end end

View file

@ -5,7 +5,8 @@ namespace :after_party do
BATCH_SIZE = 20000 BATCH_SIZE = 20000
with_dossiers = Avis.where(claimant_type: nil).includes(claimant: :assign_to).where.not(claimant: { assign_tos: { id: nil } }) without_assign_to_ids = Instructeur.includes(:assign_to).where(assign_tos: { id: nil }).pluck(:id)
with_dossiers = Avis.where(claimant_type: nil).where.not(claimant_id: without_assign_to_ids)
((with_dossiers.count / BATCH_SIZE).ceil + 1).times do ((with_dossiers.count / BATCH_SIZE).ceil + 1).times do
with_dossiers with_dossiers
@ -13,10 +14,9 @@ namespace :after_party do
.update_all(claimant_type: 'Instructeur') .update_all(claimant_type: 'Instructeur')
end end
without_dossiers = Avis.where(claimant_type: nil).includes(claimant: :assign_to).where(claimant: { assign_tos: { id: nil } }) without_dossiers = Avis.where(claimant_type: nil).where(claimant_id: without_assign_to_ids)
without_dossiers.find_each do |avis| without_dossiers.find_each do |avis|
claimant = avis.claimant.user instructeur = Instructeur.find(avis.claimant_id)
instructeur = avis.instructeur
if instructeur && avis.experts_procedure_id.blank? if instructeur && avis.experts_procedure_id.blank?
User.create_or_promote_to_expert(instructeur.user.email, SecureRandom.hex) User.create_or_promote_to_expert(instructeur.user.email, SecureRandom.hex)
@ -33,7 +33,7 @@ namespace :after_party do
elsif avis.experts_procedure_id.present? elsif avis.experts_procedure_id.present?
avis.update_column(:claimant_type, 'Expert') avis.update_column(:claimant_type, 'Expert')
elsif claimant.blank? elsif instructeur && instructeur.user.nil?
avis.destroy avis.destroy
end end
end end

View file

@ -3,7 +3,8 @@ namespace :after_party do
task backfill_claimant_id_for_experts_on_avis_table: :environment do task backfill_claimant_id_for_experts_on_avis_table: :environment do
puts "Running deploy task 'backfill_claimant_id_for_experts_on_avis_table'" puts "Running deploy task 'backfill_claimant_id_for_experts_on_avis_table'"
avis_experts_claimant = Avis.where(claimant_type: 'Expert', tmp_expert_migrated: false) avis_experts_claimant = Avis.where(claimant_type: 'Expert')
avis_experts_claimant = avis_experts_claimant.where(tmp_expert_migrated: false) if Avis.column_names.include?("tmp_expert_migrated")
progress = ProgressReport.new(avis_experts_claimant.count) progress = ProgressReport.new(avis_experts_claimant.count)
avis_experts_claimant.find_each do |avis| avis_experts_claimant.find_each do |avis|
@ -15,7 +16,10 @@ namespace :after_party do
claimant_expert = claimant_instructeur.reload.user.expert claimant_expert = claimant_instructeur.reload.user.expert
ExpertsProcedure.find_or_create_by(procedure: avis.procedure, expert: claimant_expert) ExpertsProcedure.find_or_create_by(procedure: avis.procedure, expert: claimant_expert)
end end
avis.update_columns(claimant_id: claimant_expert.id, tmp_expert_migrated: true)
if Avis.column_names.include?("tmp_expert_migrated")
avis.update_columns(claimant_id: claimant_expert.id, tmp_expert_migrated: true)
end
else else
# Avis associated to an Instructeur with no user are bad data: delete it # Avis associated to an Instructeur with no user are bad data: delete it
avis.destroy! avis.destroy!
@ -23,6 +27,7 @@ namespace :after_party do
progress.inc progress.inc
end end
progress.finish progress.finish
# Update task as completed. If you remove the line below, the task will # Update task as completed. If you remove the line below, the task will
# run with every deploy (or every time you call after_party:run). # run with every deploy (or every time you call after_party:run).
AfterParty::TaskRecord AfterParty::TaskRecord

View file

@ -3,8 +3,19 @@ namespace :after_party do
task backfill_experts_procedure_id_on_avis_table_again: :environment do task backfill_experts_procedure_id_on_avis_table_again: :environment do
puts "Running deploy task 'backfill_experts_procedure_id_on_avis_table_again'" puts "Running deploy task 'backfill_experts_procedure_id_on_avis_table_again'"
without_instructeur = Avis.where(experts_procedure_id: nil, instructeur_id: nil).where.not(email: nil) if Avis.column_names.include?("instructeur_id")
with_instructeur = Avis.where(experts_procedure_id: nil, email: nil).where.not(instructeur_id: nil) without_instructeur = Avis.where(experts_procedure_id: nil, instructeur_id: nil).where.not(email: nil)
with_instructeur = Avis.where(experts_procedure_id: nil, email: nil).where.not(instructeur_id: nil)
else
without_instructeur = Avis
.where(experts_procedure_id: nil, claimant_type: [nil, "Instructeur"])
.where.not(email: nil)
with_instructeur = Avis
.where(experts_procedure_id: nil, email: nil, claimant_type: [nil, "Instructeur"])
.where.not(claimant_id: nil)
end
progress = ProgressReport.new(without_instructeur.count) progress = ProgressReport.new(without_instructeur.count)
progress2 = ProgressReport.new(with_instructeur.count) progress2 = ProgressReport.new(with_instructeur.count)
@ -22,7 +33,8 @@ namespace :after_party do
progress.finish progress.finish
with_instructeur.find_each do |avis| with_instructeur.find_each do |avis|
instructeur = avis.instructeur instructeur = avis.respond_to?(:instructeur) ? avis.instructeur : avis.claimant
if instructeur && instructeur.user if instructeur && instructeur.user
user = User.create_or_promote_to_expert(instructeur.user.email, SecureRandom.hex) user = User.create_or_promote_to_expert(instructeur.user.email, SecureRandom.hex)
user.reload user.reload

View file

@ -3,14 +3,16 @@ namespace :after_party do
task revise_attestation_templates: :environment do task revise_attestation_templates: :environment do
rake_puts "Running deploy task 'revise_attestation_templates'" rake_puts "Running deploy task 'revise_attestation_templates'"
attestation_templates = AttestationTemplate.where.not(procedure_id: nil) revisions = ProcedureRevision
progress = ProgressReport.new(attestation_templates.count) .joins(procedure: :attestation_template)
.where(attestation_template_id: nil)
progress = ProgressReport.new(revisions.count)
revisions.find_each do |revision|
attestation_template_id = revision.procedure.attestation_template.id
revision.update_column(:attestation_template_id, attestation_template_id)
attestation_templates.find_each do |attestation_template|
ProcedureRevision
.where(procedure_id: attestation_template.procedure_id, attestation_template_id: nil)
.update_all(attestation_template_id: attestation_template)
attestation_template.update_column(:procedure_id, nil)
progress.inc progress.inc
end end
progress.finish progress.finish

View file

@ -1,8 +1,8 @@
include ActionDispatch::TestProcess include ActionDispatch::TestProcess
describe Administrateurs::AttestationTemplatesController, type: :controller do describe Administrateurs::AttestationTemplatesController, type: :controller do
let!(:attestation_template) { create(:attestation_template) }
let(:admin) { create(:administrateur) } let(:admin) { create(:administrateur) }
let(:attestation_template) { build(:attestation_template) }
let!(:procedure) { create :procedure, administrateur: admin, attestation_template: attestation_template } let!(:procedure) { create :procedure, administrateur: admin, attestation_template: attestation_template }
let(:logo) { fixture_file_upload('spec/fixtures/files/white.png', 'image/png') } let(:logo) { fixture_file_upload('spec/fixtures/files/white.png', 'image/png') }
let(:logo2) { fixture_file_upload('spec/fixtures/files/white.png', 'image/png') } let(:logo2) { fixture_file_upload('spec/fixtures/files/white.png', 'image/png') }
@ -41,7 +41,7 @@ describe Administrateurs::AttestationTemplatesController, type: :controller do
end end
context 'if an attestation template exists on the procedure' do context 'if an attestation template exists on the procedure' do
after { procedure.attestation_template.destroy } after { procedure.draft_revision.attestation_template&.destroy }
context 'with images' do context 'with images' do
let!(:attestation_template) do let!(:attestation_template) do
@ -115,14 +115,14 @@ describe Administrateurs::AttestationTemplatesController, type: :controller do
procedure.reload procedure.reload
end end
it { expect(procedure.attestation_template).to have_attributes(attestation_params) } it { expect(procedure.draft_attestation_template).to have_attributes(attestation_params) }
it { expect(procedure.attestation_template.activated).to be true } it { expect(procedure.draft_attestation_template.activated).to be true }
it { expect(procedure.attestation_template.logo.download).to eq(logo2.read) } it { expect(procedure.draft_attestation_template.logo.download).to eq(logo2.read) }
it { expect(procedure.attestation_template.signature.download).to eq(signature2.read) } it { expect(procedure.draft_attestation_template.signature.download).to eq(signature2.read) }
it { expect(response).to redirect_to edit_admin_procedure_attestation_template_path(procedure) } it { expect(response).to redirect_to edit_admin_procedure_attestation_template_path(procedure) }
it { expect(flash.notice).to eq("L'attestation a bien été sauvegardée") } it { expect(flash.notice).to eq("L'attestation a bien été sauvegardée") }
after { procedure.attestation_template.destroy } after { procedure.draft_attestation_template.destroy }
end end
context 'when something wrong happens in the attestation template creation' do context 'when something wrong happens in the attestation template creation' do
@ -140,7 +140,7 @@ describe Administrateurs::AttestationTemplatesController, type: :controller do
it { expect(response).to redirect_to edit_admin_procedure_attestation_template_path(procedure) } it { expect(response).to redirect_to edit_admin_procedure_attestation_template_path(procedure) }
it { expect(flash.alert).to be_present } it { expect(flash.alert).to be_present }
it { expect(procedure.attestation_template).to be nil } it { expect(procedure.draft_attestation_template).to be nil }
end end
end end
@ -158,13 +158,13 @@ describe Administrateurs::AttestationTemplatesController, type: :controller do
procedure.reload procedure.reload
end end
it { expect(procedure.attestation_template).to have_attributes(attestation_params) } it { expect(procedure.draft_attestation_template).to have_attributes(attestation_params) }
it { expect(procedure.attestation_template.logo.download).to eq(logo2.read) } it { expect(procedure.draft_attestation_template.logo.download).to eq(logo2.read) }
it { expect(procedure.attestation_template.signature.download).to eq(signature2.read) } it { expect(procedure.draft_attestation_template.signature.download).to eq(signature2.read) }
it { expect(response).to redirect_to edit_admin_procedure_attestation_template_path(procedure) } it { expect(response).to redirect_to edit_admin_procedure_attestation_template_path(procedure) }
it { expect(flash.notice).to eq("L'attestation a bien été modifiée") } it { expect(flash.notice).to eq("L'attestation a bien été modifiée") }
after { procedure.attestation_template.destroy } after { procedure.draft_attestation_template&.destroy }
end end
context 'when something wrong happens in the attestation template creation' do context 'when something wrong happens in the attestation template creation' do

View file

@ -4,8 +4,6 @@ FactoryBot.define do
body { 'body' } body { 'body' }
footer { 'footer' } footer { 'footer' }
activated { true } activated { true }
association :procedure
end end
trait :with_files do trait :with_files do

View file

@ -18,15 +18,16 @@ FactoryBot.define do
administrateurs { administrateur.present? ? [administrateur] : [association(:administrateur)] } administrateurs { administrateur.present? ? [administrateur] : [association(:administrateur)] }
transient do transient do
administrateur { } administrateur {}
instructeurs { [] } instructeurs { [] }
types_de_champ { [] } types_de_champ { [] }
types_de_champ_private { [] } types_de_champ_private { [] }
updated_at { nil } updated_at { nil }
attestation_template { nil }
end end
after(:build) do |procedure, evaluator| after(:build) do |procedure, evaluator|
initial_revision = build(:procedure_revision, procedure: procedure) initial_revision = build(:procedure_revision, procedure: procedure, attestation_template: evaluator.attestation_template)
add_types_de_champs(evaluator.types_de_champ, to: initial_revision, scope: :public) add_types_de_champs(evaluator.types_de_champ, to: initial_revision, scope: :public)
add_types_de_champs(evaluator.types_de_champ_private, to: initial_revision, scope: :private) add_types_de_champs(evaluator.types_de_champ_private, to: initial_revision, scope: :private)

View file

@ -0,0 +1,93 @@
RSpec.describe BalancerDeliveryMethod do
class ExampleMailer < ApplicationMailer
def greet(name)
mail(to: "smtp_to", from: "smtp_from", body: "Hello #{name}")
end
end
class TestMail
def self.deliveries
@deliveries ||= []
end
def self.deliveries=(val)
@deliveries = val
end
attr_accessor :settings
def initialize(values)
@settings = values.dup
end
def deliver!(mail)
Mail::CheckDeliveryParams.check(mail)
self.class.deliveries << mail
end
end
class MockSmtp < TestMail; end
class MockSendmail < TestMail; end
class FixedSequence
def initialize(sequence)
@enumerator = sequence.each
end
def rand(_)
@enumerator.next
end
end
before do
ActionMailer::Base.add_delivery_method :mock_smtp, MockSmtp
ActionMailer::Base.add_delivery_method :mock_sendmail, MockSendmail
ActionMailer::Base.add_delivery_method :balancer, BalancerDeliveryMethod
ExampleMailer.delivery_method = :balancer
end
context 'when a single delivery method is provided' do
before do
ActionMailer::Base.balancer_settings = { mock_smtp: 10 }
end
it 'sends emails to the selected delivery method' do
mail = ExampleMailer.greet('Joshua').deliver_now
expect(mail).to have_been_delivered_using(MockSmtp)
end
end
context 'when multiple delivery methods are provided' do
before do
ActionMailer::Base.balancer_settings = { mock_smtp: 10, mock_sendmail: 5 }
rng_sequence = [3, 14, 1]
BalancerDeliveryMethod.random = FixedSequence.new(rng_sequence)
end
after do
BalancerDeliveryMethod.random = Random.new
end
it 'sends emails randomly, given the provided weights' do
mail1 = ExampleMailer.greet('Lucia').deliver_now
expect(mail1).to have_been_delivered_using(MockSmtp)
mail2 = ExampleMailer.greet('Damian').deliver_now
expect(mail2).to have_been_delivered_using(MockSendmail)
mail3 = ExampleMailer.greet('Rahwa').deliver_now
expect(mail3).to have_been_delivered_using(MockSmtp)
end
end
# Helpers
def have_been_delivered_using(delivery_class)
satisfy("have been delivered using #{delivery_class}") do |mail|
delivery_class.deliveries.include?(mail)
end
end
end

View file

@ -0,0 +1,22 @@
describe '20220112184331_revise_attestation_templates' do
let(:rake_task) { Rake::Task['after_party:revise_attestation_templates'] }
let(:procedure) { create(:procedure) }
let(:attestation_template) { create(:attestation_template, procedure: procedure) }
subject(:run_task) do
attestation_template
rake_task.invoke
attestation_template.reload
end
after { rake_task.reenable }
describe 'revise_attestation_templates' do
it 'attaches the attestation_template to the latest revision (without removing the link between attestation_template and procedure for now)' do
expect(attestation_template.procedure.revisions.first.attestation_template_id).to be_nil
run_task
expect(attestation_template.procedure_id).to eq(procedure.id)
expect(attestation_template.procedure.revisions.first.attestation_template_id).to eq(attestation_template.id)
end
end
end

View file

@ -4,6 +4,10 @@ describe Universign::API do
let(:digest) { Digest::SHA256.hexdigest("CECI EST UN HASH") } let(:digest) { Digest::SHA256.hexdigest("CECI EST UN HASH") }
before do
stub_const("UNIVERSIGN_API_URL", "https://ws.universign.eu/tsa/post/")
end
it { is_expected.not_to be_nil } it { is_expected.not_to be_nil }
end end
end end

View file

@ -66,16 +66,12 @@ describe Procedure do
end end
describe '#closed_mail_template_attestation_inconsistency_state' do describe '#closed_mail_template_attestation_inconsistency_state' do
let(:procedure_without_attestation) { create(:procedure, closed_mail: closed_mail) } let(:procedure_without_attestation) { create(:procedure, closed_mail: closed_mail, attestation_template: nil) }
let(:procedure_with_active_attestation) do let(:procedure_with_active_attestation) do
procedure = create(:procedure, closed_mail: closed_mail) create(:procedure, closed_mail: closed_mail, attestation_template: build(:attestation_template, activated: true))
create(:attestation_template, procedure: procedure, activated: true)
procedure
end end
let(:procedure_with_inactive_attestation) do let(:procedure_with_inactive_attestation) do
procedure = create(:procedure, closed_mail: closed_mail) create(:procedure, closed_mail: closed_mail, attestation_template: build(:attestation_template, activated: false))
create(:attestation_template, procedure: procedure, activated: false)
procedure
end end
subject { procedure.closed_mail_template_attestation_inconsistency_state } subject { procedure.closed_mail_template_attestation_inconsistency_state }

View file

@ -9,6 +9,7 @@ end
Capybara.register_driver :headless_chrome do |app| Capybara.register_driver :headless_chrome do |app|
options = Selenium::WebDriver::Chrome::Options.new options = Selenium::WebDriver::Chrome::Options.new
options.add_argument('--no-sandbox') unless ENV['SANDBOX']
options.add_argument('--headless') unless ENV['NO_HEADLESS'] options.add_argument('--headless') unless ENV['NO_HEADLESS']
options.add_argument('--window-size=1440,900') options.add_argument('--window-size=1440,900')

View file

@ -1,5 +1,6 @@
describe 'admin/_closed_mail_template_attestation_inconsistency_alert.html.haml', type: :view do describe 'admin/_closed_mail_template_attestation_inconsistency_alert.html.haml', type: :view do
let(:procedure) { create(:procedure, closed_mail: closed_mail) } let(:procedure) { create(:procedure, closed_mail: closed_mail, attestation_template: attestation_template) }
let(:attestation_template) { nil }
def alert def alert
assign(:procedure, procedure) assign(:procedure, procedure)
@ -23,7 +24,7 @@ describe 'admin/_closed_mail_template_attestation_inconsistency_alert.html.haml'
context 'when there is an active attestation but the closed mail template does not mention it' do context 'when there is an active attestation but the closed mail template does not mention it' do
let(:closed_mail) { create(:closed_mail) } let(:closed_mail) { create(:closed_mail) }
let!(:attestation_template) { create(:attestation_template, procedure: procedure, activated: true) } let(:attestation_template) { build(:attestation_template) }
it { expect(alert).to include("Cette démarche comporte une attestation, mais laccusé dacceptation ne la mentionne pas") } it { expect(alert).to include("Cette démarche comporte une attestation, mais laccusé dacceptation ne la mentionne pas") }
it { expect(alert).to include(edit_admin_procedure_mail_template_path(procedure, Mails::ClosedMail::SLUG)) } it { expect(alert).to include(edit_admin_procedure_mail_template_path(procedure, Mails::ClosedMail::SLUG)) }