commit
d0026fc887
52 changed files with 2442 additions and 3020 deletions
|
@ -7,26 +7,26 @@ class Champs::SiretController < ApplicationController
|
|||
find_etablisement
|
||||
|
||||
if @siret.empty?
|
||||
@champ&.update!(value: '')
|
||||
@etablissement&.destroy
|
||||
elsif @siret.present? && @siret.length == 14
|
||||
etablissement = find_etablisement_with_siret
|
||||
if etablissement.present?
|
||||
@etablissement = etablissement
|
||||
return clear_siret_and_etablissement
|
||||
end
|
||||
|
||||
if @siret.present? && @siret.length != 14
|
||||
return siret_error(:invalid)
|
||||
end
|
||||
|
||||
begin
|
||||
etablissement = find_etablissement_with_siret
|
||||
rescue RestClient::RequestFailed
|
||||
return siret_error(:network_error)
|
||||
end
|
||||
if etablissement.blank?
|
||||
return siret_error(:not_found)
|
||||
end
|
||||
|
||||
@etablissement = etablissement
|
||||
if !@champ.nil?
|
||||
@champ.update!(value: etablissement.siret, etablissement: etablissement)
|
||||
end
|
||||
else
|
||||
@champ&.update!(value: '')
|
||||
@etablissement&.destroy
|
||||
@siret = :not_found
|
||||
end
|
||||
else
|
||||
@champ&.update!(value: '')
|
||||
@etablissement&.destroy
|
||||
@siret = :invalid
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -49,10 +49,20 @@ class Champs::SiretController < ApplicationController
|
|||
@procedure_id = @champ&.dossier&.procedure_id || 'aperçu'
|
||||
end
|
||||
|
||||
def find_etablisement_with_siret
|
||||
def find_etablissement_with_siret
|
||||
etablissement_attributes = ApiEntrepriseService.get_etablissement_params_for_siret(@siret, @procedure_id)
|
||||
if etablissement_attributes.present?
|
||||
Etablissement.new(etablissement_attributes)
|
||||
end
|
||||
end
|
||||
|
||||
def clear_siret_and_etablissement
|
||||
@champ&.update!(value: '')
|
||||
@etablissement&.destroy
|
||||
end
|
||||
|
||||
def siret_error(error)
|
||||
clear_siret_and_etablissement
|
||||
@siret = error
|
||||
end
|
||||
end
|
||||
|
|
|
@ -91,7 +91,11 @@ module Users
|
|||
end
|
||||
|
||||
sanitized_siret = siret_model.siret
|
||||
begin
|
||||
etablissement_attributes = ApiEntrepriseService.get_etablissement_params_for_siret(sanitized_siret, @dossier.procedure.id)
|
||||
rescue RestClient::RequestFailed
|
||||
return render_siret_error(t('errors.messages.siret_network_error'))
|
||||
end
|
||||
if etablissement_attributes.blank?
|
||||
return render_siret_error(t('errors.messages.siret_unknown'))
|
||||
end
|
||||
|
@ -257,7 +261,7 @@ module Users
|
|||
|
||||
def show_demarche_en_test_banner
|
||||
if @dossier.present? && @dossier.procedure.brouillon?
|
||||
flash.now.notice = "Ce dossier est déposé sur une démarche en test. Il sera supprimé lors de la publication de la démarche."
|
||||
flash.now.alert = "Ce dossier est déposé sur une démarche en test. Il sera supprimé lors de la publication de la démarche et sa soumission n’a pas de valeur juridique."
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ class TypesDeChampEditor extends Component {
|
|||
type_champ: 'text',
|
||||
types_de_champ: [],
|
||||
private: props.isAnnotation,
|
||||
drop_down_list_value: '--Premier élément du menu--\n',
|
||||
libelle: `${
|
||||
props.isAnnotation ? 'Nouvelle annotation' : 'Nouveau champ'
|
||||
} ${props.typeDeChampsTypes[0][0]}`
|
||||
|
|
|
@ -1,64 +1,47 @@
|
|||
import { CREATE } from 'leaflet-freedraw';
|
||||
import { delegate } from '@utils';
|
||||
import {
|
||||
initMap,
|
||||
getCurrentMap,
|
||||
geocodeAddress,
|
||||
drawCadastre,
|
||||
drawQuartiersPrioritaires,
|
||||
drawParcellesAgricoles,
|
||||
drawUserSelection,
|
||||
addFreeDrawEvents
|
||||
} from '../../shared/carte';
|
||||
import { initMap, drawPolygons, addFreeDrawEvents } from '../../shared/carte';
|
||||
|
||||
function initialize() {
|
||||
for (let element of document.querySelectorAll('.carte')) {
|
||||
async function initialize() {
|
||||
const elements = document.querySelectorAll('.carte');
|
||||
|
||||
if (elements.length) {
|
||||
const editable = [...elements].find(element =>
|
||||
element.classList.contains('edit')
|
||||
);
|
||||
await loadLeaflet(editable);
|
||||
|
||||
for (let element of elements) {
|
||||
diplayMap(element, null, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.DS.drawMapData = (selector, data) => {
|
||||
let element = document.querySelector(selector);
|
||||
diplayMap(element, data);
|
||||
};
|
||||
// We load leaflet dynamically, ramda and freedraw and assign them to globals.
|
||||
// Latest freedraw version build needs globals.
|
||||
async function loadLeaflet(editable) {
|
||||
window.L = await import('leaflet').then(({ default: L }) => L);
|
||||
|
||||
if (editable) {
|
||||
window.R = await import('ramda').then(({ default: R }) => R);
|
||||
await import('leaflet-freedraw/dist/leaflet-freedraw.web.js');
|
||||
}
|
||||
}
|
||||
|
||||
function diplayMap(element, data, initial = false) {
|
||||
data = data || JSON.parse(element.dataset.geo);
|
||||
let editable = element.classList.contains('edit');
|
||||
const editable = element.classList.contains('edit');
|
||||
const map = initMap(element, data.position, editable);
|
||||
|
||||
let map = initMap(element, data.position, editable);
|
||||
drawPolygons(map, data, { initial, editable });
|
||||
|
||||
// draw external polygons
|
||||
drawCadastre(map, data, editable);
|
||||
drawQuartiersPrioritaires(map, data, editable);
|
||||
drawParcellesAgricoles(map, data, editable);
|
||||
|
||||
// draw user polygon
|
||||
if (initial) {
|
||||
drawUserSelection(map, data, editable);
|
||||
|
||||
if (editable) {
|
||||
let input = element.parentElement.querySelector('input[data-remote]');
|
||||
if (initial && editable) {
|
||||
const input = element.parentElement.querySelector('input[data-remote]');
|
||||
addFreeDrawEvents(map, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addEventListener('turbolinks:load', initialize);
|
||||
|
||||
delegate('click', '.toolbar .new-area', event => {
|
||||
event.preventDefault();
|
||||
let map = getCurrentMap(event.target);
|
||||
|
||||
if (map) {
|
||||
map.freeDraw.mode(CREATE);
|
||||
}
|
||||
});
|
||||
|
||||
delegate('autocomplete:select', '.toolbar [data-address]', event => {
|
||||
let map = getCurrentMap(event.target);
|
||||
|
||||
if (map) {
|
||||
geocodeAddress(map, event.detail.label);
|
||||
}
|
||||
addEventListener('carte:update', ({ detail: { selector, data } }) => {
|
||||
const element = document.querySelector(selector);
|
||||
diplayMap(element, data);
|
||||
});
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import L from 'leaflet';
|
||||
import FreeDraw, { NONE, EDIT, DELETE } from 'leaflet-freedraw';
|
||||
/* globals FreeDraw L */
|
||||
import { fire, getJSON, delegate } from '@utils';
|
||||
|
||||
import polygonArea from './polygon_area';
|
||||
|
||||
const LAYERS = {};
|
||||
const MAPS = new WeakMap();
|
||||
|
||||
export function initMap(element, position, editable = false) {
|
||||
|
@ -22,7 +20,7 @@ export function initMap(element, position, editable = false) {
|
|||
|
||||
if (editable) {
|
||||
const freeDraw = new FreeDraw({
|
||||
mode: NONE,
|
||||
mode: FreeDraw.NONE,
|
||||
smoothFactor: 4,
|
||||
mergePolygons: false
|
||||
});
|
||||
|
@ -35,80 +33,38 @@ export function initMap(element, position, editable = false) {
|
|||
}
|
||||
}
|
||||
|
||||
export function drawCadastre(map, { cadastres }, editable = false) {
|
||||
drawLayer(
|
||||
map,
|
||||
cadastres,
|
||||
editable ? CADASTRE_POLYGON_STYLE : noEditStyle(CADASTRE_POLYGON_STYLE),
|
||||
'cadastres'
|
||||
);
|
||||
export function drawPolygons(map, data, { editable, initial }) {
|
||||
if (initial) {
|
||||
drawUserSelection(map, data, editable);
|
||||
}
|
||||
|
||||
export function drawQuartiersPrioritaires(
|
||||
map,
|
||||
{ quartiersPrioritaires },
|
||||
editable = false
|
||||
) {
|
||||
drawLayer(
|
||||
map,
|
||||
quartiersPrioritaires,
|
||||
editable ? QP_POLYGON_STYLE : noEditStyle(QP_POLYGON_STYLE),
|
||||
'quartiersPrioritaires'
|
||||
);
|
||||
}
|
||||
|
||||
export function drawParcellesAgricoles(
|
||||
map,
|
||||
{ parcellesAgricoles },
|
||||
editable = false
|
||||
) {
|
||||
drawLayer(
|
||||
map,
|
||||
parcellesAgricoles,
|
||||
editable ? RPG_POLYGON_STYLE : noEditStyle(RPG_POLYGON_STYLE),
|
||||
'parcellesAgricoles'
|
||||
);
|
||||
clearLayers(map);
|
||||
drawCadastre(map, data, editable);
|
||||
drawQuartiersPrioritaires(map, data, editable);
|
||||
drawParcellesAgricoles(map, data, editable);
|
||||
bringToFrontUserSelection(map);
|
||||
}
|
||||
|
||||
export function drawUserSelection(map, { selection }, editable = false) {
|
||||
if (selection) {
|
||||
const coordinates = toLatLngs(selection);
|
||||
let polygon;
|
||||
|
||||
if (editable) {
|
||||
coordinates.forEach(polygon => map.freeDraw.create(polygon));
|
||||
const polygon = map.freeDraw.all()[0];
|
||||
[polygon] = markFreeDrawLayers(map);
|
||||
} else {
|
||||
polygon = L.polygon(coordinates, {
|
||||
color: 'red',
|
||||
zIndex: 3
|
||||
});
|
||||
polygon.addTo(map);
|
||||
}
|
||||
|
||||
if (polygon) {
|
||||
map.fitBounds(polygon.getBounds());
|
||||
}
|
||||
} else {
|
||||
const polygon = L.polygon(coordinates, {
|
||||
color: 'red',
|
||||
zIndex: 3
|
||||
}).addTo(map);
|
||||
|
||||
map.fitBounds(polygon.getBounds());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function geocodeAddress(map, query) {
|
||||
getJSON('/address/geocode', { request: query }).then(data => {
|
||||
if (data.lat !== null) {
|
||||
map.setView(new L.LatLng(data.lat, data.lon), data.zoom);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function getCurrentMap(input) {
|
||||
let element = input.closest('.toolbar').parentElement.querySelector('.carte');
|
||||
|
||||
if (MAPS.has(element)) {
|
||||
return MAPS.get(element);
|
||||
}
|
||||
}
|
||||
|
||||
const EMPTY_GEO_JSON = '[]';
|
||||
const ERROR_GEO_JSON = '';
|
||||
|
||||
export function addFreeDrawEvents(map, selector) {
|
||||
const input = findInput(selector);
|
||||
|
@ -121,10 +77,65 @@ export function addFreeDrawEvents(map, selector) {
|
|||
input.value = ERROR_GEO_JSON;
|
||||
}
|
||||
|
||||
markFreeDrawLayers(map);
|
||||
fire(input, 'change');
|
||||
});
|
||||
}
|
||||
|
||||
function drawCadastre(map, { cadastres }, editable = false) {
|
||||
drawLayer(
|
||||
map,
|
||||
cadastres,
|
||||
editable ? CADASTRE_POLYGON_STYLE : noEditStyle(CADASTRE_POLYGON_STYLE)
|
||||
);
|
||||
}
|
||||
|
||||
function drawQuartiersPrioritaires(
|
||||
map,
|
||||
{ quartiersPrioritaires },
|
||||
editable = false
|
||||
) {
|
||||
drawLayer(
|
||||
map,
|
||||
quartiersPrioritaires,
|
||||
editable ? QP_POLYGON_STYLE : noEditStyle(QP_POLYGON_STYLE)
|
||||
);
|
||||
}
|
||||
|
||||
function drawParcellesAgricoles(map, { parcellesAgricoles }, editable = false) {
|
||||
drawLayer(
|
||||
map,
|
||||
parcellesAgricoles,
|
||||
editable ? RPG_POLYGON_STYLE : noEditStyle(RPG_POLYGON_STYLE)
|
||||
);
|
||||
}
|
||||
|
||||
function geocodeAddress(map, query) {
|
||||
getJSON('/address/geocode', { request: query }).then(data => {
|
||||
if (data.lat !== null) {
|
||||
map.setView(new L.LatLng(data.lat, data.lon), data.zoom);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getCurrentMap(element) {
|
||||
if (!element.matches('.carte')) {
|
||||
const closestCarteElement = element.closest('.carte');
|
||||
const closestToolbarElement = element.closest('.toolbar');
|
||||
|
||||
element = closestCarteElement
|
||||
? closestCarteElement
|
||||
: closestToolbarElement.parentElement.querySelector('.carte');
|
||||
}
|
||||
|
||||
if (MAPS.has(element)) {
|
||||
return MAPS.get(element);
|
||||
}
|
||||
}
|
||||
|
||||
const EMPTY_GEO_JSON = '[]';
|
||||
const ERROR_GEO_JSON = '';
|
||||
|
||||
function toLatLngs({ coordinates }) {
|
||||
return coordinates.map(polygon =>
|
||||
polygon[0].map(point => ({ lng: point[0], lat: point[1] }))
|
||||
|
@ -137,28 +148,40 @@ function findInput(selector) {
|
|||
: selector;
|
||||
}
|
||||
|
||||
function createLayer(map, layerName) {
|
||||
const layer = (LAYERS[layerName] = new L.GeoJSON(undefined, {
|
||||
function createLayer(map) {
|
||||
const layer = new L.GeoJSON(undefined, {
|
||||
interactive: false
|
||||
}));
|
||||
});
|
||||
layer.addTo(map);
|
||||
return layer;
|
||||
}
|
||||
|
||||
function removeLayer(map, layerName) {
|
||||
const layer = LAYERS[layerName];
|
||||
|
||||
if (layer) {
|
||||
delete LAYERS[layerName];
|
||||
function clearLayers(map) {
|
||||
map.eachLayer(layer => {
|
||||
if (layer instanceof L.GeoJSON) {
|
||||
map.removeLayer(layer);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function drawLayer(map, data, style, layerName = 'default') {
|
||||
removeLayer(map, layerName);
|
||||
function bringToFrontUserSelection(map) {
|
||||
map.eachLayer(layer => {
|
||||
if (layer.isFreeDraw) {
|
||||
layer.bringToFront();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function markFreeDrawLayers(map) {
|
||||
return map.freeDraw.all().map(layer => {
|
||||
layer.isFreeDraw = true;
|
||||
return layer;
|
||||
});
|
||||
}
|
||||
|
||||
function drawLayer(map, data, style) {
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
const layer = createLayer(map, layerName);
|
||||
const layer = createLayer(map);
|
||||
|
||||
data.forEach(function(item) {
|
||||
layer.addData(item.geometry);
|
||||
|
@ -197,18 +220,33 @@ const RPG_POLYGON_STYLE = Object.assign({}, POLYGON_STYLE, {
|
|||
});
|
||||
|
||||
delegate('click', '.carte.edit', event => {
|
||||
let element = event.target;
|
||||
let isPath = element.matches('.leaflet-container g path');
|
||||
let map = element.matches('.carte') ? element : element.closest('.carte');
|
||||
let freeDraw = MAPS.has(map) ? MAPS.get(map).freeDraw : null;
|
||||
const map = getCurrentMap(event.target);
|
||||
|
||||
if (freeDraw) {
|
||||
if (map) {
|
||||
const isPath = event.target.matches('.leaflet-container g path');
|
||||
if (isPath) {
|
||||
setTimeout(() => {
|
||||
freeDraw.mode(EDIT | DELETE);
|
||||
map.freeDraw.mode(FreeDraw.EDIT | FreeDraw.DELETE);
|
||||
}, 50);
|
||||
} else {
|
||||
freeDraw.mode(NONE);
|
||||
map.freeDraw.mode(FreeDraw.NONE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
delegate('click', '.toolbar .new-area', event => {
|
||||
event.preventDefault();
|
||||
const map = getCurrentMap(event.target);
|
||||
|
||||
if (map) {
|
||||
map.freeDraw.mode(FreeDraw.CREATE);
|
||||
}
|
||||
});
|
||||
|
||||
delegate('autocomplete:select', '.toolbar [data-address]', event => {
|
||||
const map = getCurrentMap(event.target);
|
||||
|
||||
if (map) {
|
||||
geocodeAddress(map, event.detail.label);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
class AntiVirusJob < ApplicationJob
|
||||
include ActiveStorage::Downloading
|
||||
|
||||
attr_reader :blob
|
||||
|
||||
def perform(virus_scan)
|
||||
@blob = ActiveStorage::Blob.find_by(key: virus_scan.blob_key)
|
||||
|
||||
if @blob.present?
|
||||
download_blob_to_tempfile do |file|
|
||||
if ClamavService.safe_file?(file.path)
|
||||
status = VirusScan.statuses.fetch(:safe)
|
||||
else
|
||||
status = VirusScan.statuses.fetch(:infected)
|
||||
end
|
||||
virus_scan.update(scanned_at: Time.zone.now, status: status)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,7 +2,11 @@ class EtablissementUpdateJob < ApplicationJob
|
|||
queue_as :default
|
||||
|
||||
def perform(dossier, siret)
|
||||
begin
|
||||
etablissement_attributes = ApiEntrepriseService.get_etablissement_params_for_siret(siret, dossier.procedure_id)
|
||||
rescue
|
||||
return
|
||||
end
|
||||
|
||||
if etablissement_attributes.present?
|
||||
if dossier.etablissement.present?
|
||||
|
|
10
app/jobs/virus_scanner_job.rb
Normal file
10
app/jobs/virus_scanner_job.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
class VirusScannerJob < ApplicationJob
|
||||
def perform(blob)
|
||||
metadata = extract_metadata_via_virus_scanner(blob)
|
||||
blob.update!(metadata: blob.metadata.merge(metadata))
|
||||
end
|
||||
|
||||
def extract_metadata_via_virus_scanner(blob)
|
||||
ActiveStorage::VirusScanner.new(blob).metadata
|
||||
end
|
||||
end
|
46
app/lib/active_storage/virus_scanner.rb
Normal file
46
app/lib/active_storage/virus_scanner.rb
Normal file
|
@ -0,0 +1,46 @@
|
|||
class ActiveStorage::VirusScanner
|
||||
include ActiveStorage::Downloading
|
||||
|
||||
def initialize(blob)
|
||||
@blob = blob
|
||||
end
|
||||
|
||||
attr_reader :blob
|
||||
|
||||
PENDING = 'pending'
|
||||
INFECTED = 'infected'
|
||||
SAFE = 'safe'
|
||||
|
||||
def pending?
|
||||
blob.metadata[:virus_scan_result] == PENDING
|
||||
end
|
||||
|
||||
def infected?
|
||||
blob.metadata[:virus_scan_result] == INFECTED
|
||||
end
|
||||
|
||||
def safe?
|
||||
blob.metadata[:virus_scan_result] == SAFE
|
||||
end
|
||||
|
||||
def analyzed?
|
||||
blob.metadata[:virus_scan_result].present?
|
||||
end
|
||||
|
||||
def analyze_later
|
||||
if !analyzed?
|
||||
blob.update!(metadata: blob.metadata.merge(virus_scan_result: PENDING))
|
||||
VirusScannerJob.perform_later(blob)
|
||||
end
|
||||
end
|
||||
|
||||
def metadata
|
||||
download_blob_to_tempfile do |file|
|
||||
if ClamavService.safe_file?(file.path)
|
||||
{ virus_scan_result: SAFE, scanned_at: Time.zone.now }
|
||||
else
|
||||
{ virus_scan_result: INFECTED, scanned_at: Time.zone.now }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -7,10 +7,12 @@ class ApiEntreprise::Adapter
|
|||
end
|
||||
|
||||
def data_source
|
||||
begin
|
||||
@data_source ||= get_resource
|
||||
rescue
|
||||
rescue RestClient::ResourceNotFound
|
||||
@data_source = nil
|
||||
end
|
||||
end
|
||||
|
||||
def to_params
|
||||
if data_source.present?
|
||||
|
|
|
@ -34,13 +34,21 @@ class ApiEntreprise::API
|
|||
|
||||
if response.success?
|
||||
JSON.parse(response.body, symbolize_names: true)
|
||||
else
|
||||
elsif response.code == 404 || response.code == 422
|
||||
raise RestClient::ResourceNotFound
|
||||
else
|
||||
raise RestClient::RequestFailed
|
||||
end
|
||||
end
|
||||
|
||||
def self.url(resource_name, siret_or_siren)
|
||||
[API_ENTREPRISE_URL, resource_name, siret_or_siren].join("/")
|
||||
base_url = [API_ENTREPRISE_URL, resource_name, siret_or_siren].join("/")
|
||||
|
||||
if Flipflop.insee_api_v3?
|
||||
base_url += "?with_insee_v3=true"
|
||||
end
|
||||
|
||||
base_url
|
||||
end
|
||||
|
||||
def self.params(siret_or_siren, procedure_id)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
class Avis < ApplicationRecord
|
||||
include EmailSanitizableConcern
|
||||
include VirusScanConcern
|
||||
|
||||
belongs_to :dossier, touch: true
|
||||
belongs_to :gestionnaire
|
||||
|
@ -22,9 +21,6 @@ class Avis < ApplicationRecord
|
|||
scope :by_latest, -> { order(updated_at: :desc) }
|
||||
scope :updated_since?, -> (date) { where('avis.updated_at > ?', date) }
|
||||
|
||||
after_commit :create_avis_virus_scan
|
||||
after_initialize { add_virus_scan_on(self.piece_justificative_file) }
|
||||
|
||||
# The form allows subtmitting avis requests to several emails at once,
|
||||
# hence this virtual attribute.
|
||||
attr_accessor :emails
|
||||
|
@ -41,6 +37,27 @@ class Avis < ApplicationRecord
|
|||
Avis.find_by(id: avis_id)&.email == email
|
||||
end
|
||||
|
||||
# FIXME remove this after migrating virus_scan to blob metadata
|
||||
def virus_scan
|
||||
VirusScan.find_by(blob_key: piece_justificative_file.blob.key)
|
||||
end
|
||||
|
||||
def virus_scan_safe?
|
||||
virus_scan&.safe? || piece_justificative_file.virus_scanner.safe?
|
||||
end
|
||||
|
||||
def virus_scan_infected?
|
||||
virus_scan&.infected? || piece_justificative_file.virus_scanner.infected?
|
||||
end
|
||||
|
||||
def virus_scan_pending?
|
||||
virus_scan&.pending? || piece_justificative_file.virus_scanner.pending?
|
||||
end
|
||||
|
||||
def virus_scan_no_scan?
|
||||
virus_scan.blank? && !piece_justificative_file.virus_scanner.analyzed?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def notify_gestionnaire
|
||||
|
@ -54,8 +71,4 @@ class Avis < ApplicationRecord
|
|||
self.email = nil
|
||||
end
|
||||
end
|
||||
|
||||
def create_avis_virus_scan
|
||||
create_virus_scan(self.piece_justificative_file)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
class Champs::PieceJustificativeChamp < Champ
|
||||
after_commit :create_virus_scan
|
||||
|
||||
PIECE_JUSTIFICATIVE_FILE_MAX_SIZE = 200.megabytes
|
||||
|
||||
PIECE_JUSTIFICATIVE_FILE_ACCEPTED_FORMATS = [
|
||||
|
@ -48,20 +46,26 @@ class Champs::PieceJustificativeChamp < Champ
|
|||
errors
|
||||
end
|
||||
|
||||
# FIXME remove this after migrating virus_scan to blob metadata
|
||||
def virus_scan_safe?
|
||||
virus_scan&.safe? || piece_justificative_file.virus_scanner.safe?
|
||||
end
|
||||
|
||||
def virus_scan_infected?
|
||||
virus_scan&.infected? || piece_justificative_file.virus_scanner.infected?
|
||||
end
|
||||
|
||||
def virus_scan_pending?
|
||||
virus_scan&.pending? || piece_justificative_file.virus_scanner.pending?
|
||||
end
|
||||
|
||||
def virus_scan_no_scan?
|
||||
virus_scan.blank? && !piece_justificative_file.virus_scanner.analyzed?
|
||||
end
|
||||
|
||||
def for_api
|
||||
if piece_justificative_file.attached? && (virus_scan&.safe? || virus_scan&.pending?)
|
||||
if piece_justificative_file.attached? && (virus_scan_safe? || virus_scan_pending?)
|
||||
Rails.application.routes.url_helpers.url_for(piece_justificative_file)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_virus_scan
|
||||
if self.piece_justificative_file&.attachment&.blob.present?
|
||||
VirusScan.where(champ: self).where.not(blob_key: self.piece_justificative_file.blob.key).delete_all
|
||||
VirusScan.find_or_create_by!(champ: self, blob_key: self.piece_justificative_file.blob.key) do |virus_scan|
|
||||
virus_scan.status = VirusScan.statuses.fetch(:pending)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
module VirusScanConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
attr_reader :attachment_attribute
|
||||
|
||||
def add_virus_scan_on(piece_justificative)
|
||||
@attachment_attribute = piece_justificative
|
||||
end
|
||||
|
||||
def virus_scan
|
||||
VirusScan.find_by(blob_key: self.attachment_attribute.blob.key)
|
||||
end
|
||||
|
||||
def create_virus_scan(piece_justificative)
|
||||
if piece_justificative&.attachment&.blob.present?
|
||||
VirusScan.find_or_create_by!(blob_key: piece_justificative.blob.key) do |virus_scan|
|
||||
virus_scan.status = VirusScan.statuses.fetch(:pending)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -346,14 +346,6 @@ class Procedure < ApplicationRecord
|
|||
percentile_time(:en_construction_at, :processed_at, 90)
|
||||
end
|
||||
|
||||
def usual_verification_time
|
||||
percentile_time(:en_construction_at, :en_instruction_at, 90)
|
||||
end
|
||||
|
||||
def usual_instruction_time
|
||||
percentile_time(:en_instruction_at, :processed_at, 90)
|
||||
end
|
||||
|
||||
PATH_AVAILABLE = :available
|
||||
PATH_AVAILABLE_PUBLIEE = :available_publiee
|
||||
PATH_NOT_AVAILABLE = :not_available
|
||||
|
|
|
@ -6,12 +6,4 @@ class VirusScan < ApplicationRecord
|
|||
safe: 'safe',
|
||||
infected: 'infected'
|
||||
}
|
||||
|
||||
validates :champ_id, uniqueness: { scope: :blob_key }
|
||||
|
||||
after_create :perform_scan
|
||||
|
||||
def perform_scan
|
||||
AntiVirusJob.perform_later(self)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
class ApiEntrepriseService
|
||||
# Retrieve all informations we can get about a SIRET.
|
||||
#
|
||||
# Returns nil if the SIRET is unknown; and nested params
|
||||
# suitable for being saved into a Etablissement object otherwise.
|
||||
#
|
||||
# Raises a RestClient::RequestFailed exception on transcient errors
|
||||
# (timeout, 5XX HTTP error code, etc.)
|
||||
def self.get_etablissement_params_for_siret(siret, procedure_id)
|
||||
etablissement_params = ApiEntreprise::EtablissementAdapter.new(siret, procedure_id).to_params
|
||||
entreprise_params = ApiEntreprise::EntrepriseAdapter.new(siret, procedure_id).to_params
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
partial: 'shared/champs/carte/geo_areas',
|
||||
locals: { champ: @champ, error: @error }) %>
|
||||
|
||||
DS.drawMapData("<%= @selector %>", <%= geo_data(@champ) %>);
|
||||
<%= fire_event('carte:update', { selector: @selector, data: @champ.to_render_data }.to_json) %>
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
= favicon_link_tag(image_url("favicons/32x32.png"), type: "image/png", sizes: "32x32")
|
||||
= favicon_link_tag(image_url("favicons/96x96.png"), type: "image/png", sizes: "96x96")
|
||||
|
||||
= javascript_pack_tag 'application', defer: true, 'data-turbolinks-track': 'reload'
|
||||
- packs = ['application', 'track', administrateur_signed_in? ? 'sendinblue' : nil].compact
|
||||
= javascript_packs_with_chunks_tag *packs, defer: true, 'data-turbolinks-track': 'reload'
|
||||
= stylesheet_link_tag 'new_design/new_application', media: 'all', 'data-turbolinks-track': 'reload'
|
||||
= stylesheet_link_tag 'new_design/print', media: 'print', 'data-turbolinks-track': 'reload'
|
||||
|
||||
|
@ -42,7 +43,3 @@
|
|||
= javascript_include_tag :xray
|
||||
|
||||
= yield :charts_js
|
||||
|
||||
= javascript_pack_tag 'track', async: true
|
||||
- if administrateur_signed_in?
|
||||
= javascript_pack_tag 'sendinblue', async: true
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
|
||||
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': "reload"
|
||||
= stylesheet_link_tag 'print', media: 'print', 'data-turbolinks-track': "reload"
|
||||
= javascript_pack_tag 'application-old', defer: true, 'data-turbolinks-track': 'reload'
|
||||
- packs = ['application-old', 'track', administrateur_signed_in? ? 'sendinblue' : nil].compact
|
||||
= javascript_packs_with_chunks_tag *packs, defer: true, 'data-turbolinks-track': 'reload'
|
||||
= javascript_include_tag 'application', defer: true, 'data-turbolinks-track': 'reload'
|
||||
= csrf_meta_tags
|
||||
= Gon::Base.render_data(camel_case: true, init: true)
|
||||
|
@ -42,6 +43,5 @@
|
|||
%i.fa.fa-times{ style: 'position: fixed; top: 10; right: 30; color: white;' }
|
||||
|
||||
= render partial: 'layouts/footer', locals: { main_container_size: main_container_size }
|
||||
= javascript_pack_tag 'track', async: true
|
||||
- if administrateur_signed_in?
|
||||
= javascript_pack_tag 'sendinblue', async: true
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
- pj = champ.piece_justificative_file
|
||||
|
||||
.pj-link
|
||||
- if champ.virus_scan.blank? || champ.virus_scan.safe?
|
||||
= link_to url_for(pj), target: '_blank', rel: 'noopener', title: "Télécharger la pièce jointe" do
|
||||
%span.icon.attachment
|
||||
= pj.filename.to_s
|
||||
- if champ.virus_scan.blank?
|
||||
(ce fichier n’a pas été analysé par notre antivirus, téléchargez-le avec précaution)
|
||||
|
||||
- else
|
||||
= pj.filename.to_s
|
||||
- if champ.virus_scan.pending?
|
||||
(analyse antivirus en cours
|
||||
= link_to "rafraichir", request.path
|
||||
)
|
||||
- elsif champ.virus_scan.infected?
|
||||
- if user_can_upload
|
||||
(virus détecté, merci d’envoyer un autre fichier)
|
||||
- else
|
||||
(virus détecté, le téléchargement de ce fichier est bloqué)
|
|
@ -1,5 +1,5 @@
|
|||
- pj = champ.piece_justificative_file
|
||||
- if pj.attached?
|
||||
= render partial: "shared/champs/piece_justificative/pj_link", locals: { champ: champ, user_can_upload: false }
|
||||
= render partial: "shared/piece_jointe/pj_link", locals: { object: champ, pj: pj, user_can_upload: false }
|
||||
- else
|
||||
Pièce justificative non fournie
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
Nous n’avons pas trouvé d’établissement correspondant à ce numéro de SIRET.
|
||||
= link_to('Plus d’informations', "https://faq.demarches-simplifiees.fr/article/4-erreur-siret", target: '_blank', rel: 'noopener')
|
||||
|
||||
- when :network_error
|
||||
= t('errors.messages.siret_network_error')
|
||||
|
||||
- else
|
||||
- if siret.present? && siret == etablissement&.siret
|
||||
= render partial: 'shared/dossiers/editable_champs/etablissement_titre', locals: { etablissement: etablissement }
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
- if pj.attached?
|
||||
.piece-justificative-actions{ id: "piece_justificative_#{champ.id}" }
|
||||
.piece-justificative-action
|
||||
= render partial: "shared/champs/piece_justificative/pj_link", locals: { champ: champ, user_can_upload: true }
|
||||
= render partial: "shared/piece_jointe/pj_link", locals: { object: champ, pj: champ.piece_justificative_file, user_can_upload: true }
|
||||
.piece-justificative-action
|
||||
- if champ.private?
|
||||
= link_to 'Supprimer', gestionnaire_champ_purge_champ_piece_justificative_path(procedure_id: champ.dossier.procedure_id, dossier_id: champ.dossier_id, champ_id: champ.id), remote: true, method: :delete, class: 'button small danger'
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
- if pj.attached?
|
||||
.pj-link
|
||||
- if object.virus_scan.blank? || object.virus_scan.safe?
|
||||
= link_to url_for(pj), target: '_blank', title: "Télécharger la pièce jointe" do
|
||||
- if object.virus_scan_safe? || object.virus_scan_no_scan?
|
||||
= link_to url_for(pj), target: '_blank', rel: 'noopener', title: "Télécharger la pièce jointe" do
|
||||
%span.icon.attachment
|
||||
= pj.filename.to_s
|
||||
- if object.virus_scan.blank?
|
||||
- if object.virus_scan_no_scan?
|
||||
(ce fichier n’a pas été analysé par notre antivirus, téléchargez-le avec précaution)
|
||||
|
||||
- else
|
||||
= pj.filename.to_s
|
||||
- if object.virus_scan.pending?
|
||||
- if object.virus_scan_pending?
|
||||
(analyse antivirus en cours
|
||||
= link_to "rafraichir", request.path
|
||||
)
|
||||
- elsif object.virus_scan.infected?
|
||||
- elsif object.virus_scan_infected?
|
||||
- if user_can_upload
|
||||
(virus détecté, merci d’envoyer un autre fichier)
|
||||
- else
|
||||
|
|
9
app/views/users/dossiers/show/_estimated_delay.html.haml
Normal file
9
app/views/users/dossiers/show/_estimated_delay.html.haml
Normal file
|
@ -0,0 +1,9 @@
|
|||
/ FIXME: remove the custom procedure switch at some point
|
||||
- procedure_id_for_which_we_hide_the_estimated_delay = 6547
|
||||
- procedure_path_for_which_we_hide_the_estimated_delay = 'deposer-une-offre-de-stage'
|
||||
- show_time_means = procedure.id != procedure_id_for_which_we_hide_the_estimated_delay && procedure.path != procedure_path_for_which_we_hide_the_estimated_delay
|
||||
|
||||
- cache(procedure.id, expires_in: 1.day) do
|
||||
- if procedure.usual_traitement_time && show_time_means
|
||||
%p
|
||||
Habituellement, les dossiers de cette démarche sont traités dans un délai de #{distance_of_time_in_words(procedure.usual_traitement_time)}.
|
|
@ -1,6 +1,4 @@
|
|||
- procedure_id_for_which_we_hide_the_time_means = 6547
|
||||
- procedure_path_for_which_we_hide_the_time_means = 'deposer-une-offre-de-stage'
|
||||
- show_time_means = dossier.procedure.id != procedure_id_for_which_we_hide_the_time_means && dossier.procedure.path != procedure_path_for_which_we_hide_the_time_means
|
||||
|
||||
|
||||
.status-overview
|
||||
- if !dossier.termine?
|
||||
|
@ -24,17 +22,11 @@
|
|||
- elsif dossier.en_construction?
|
||||
.en-construction
|
||||
%p Un instructeur de l’administration est en train de vérifier que votre dossier est bien complet. Si des modifications sont nécessaires, vous recevrez un message avec les modifications à effectuer.
|
||||
|
||||
%p
|
||||
Sinon,
|
||||
= succeed '.' do
|
||||
%strong votre dossier passera directement en instruction
|
||||
|
||||
/ FIXME: remove the custom procedure switch at some point
|
||||
- if dossier.procedure.usual_verification_time && show_time_means
|
||||
- cache(dossier.procedure, expires_in: 1.week) do
|
||||
%p
|
||||
Habituellement, les dossiers de cette démarche sont vérifiés dans un délai de #{distance_of_time_in_words(dossier.procedure.usual_verification_time)}.
|
||||
= render partial: 'users/dossiers/show/estimated_delay', locals: { procedure: dossier.procedure }
|
||||
|
||||
- elsif dossier.en_instruction?
|
||||
.en-instruction
|
||||
|
@ -44,12 +36,7 @@
|
|||
%strong
|
||||
vous recevrez un email
|
||||
avec le résultat.
|
||||
|
||||
/ FIXME: remove the custom procedure switch at some point
|
||||
- if dossier.procedure.usual_instruction_time && show_time_means
|
||||
- cache(dossier.procedure, expires_in: 1.week) do
|
||||
%p
|
||||
Habituellement, les dossiers de cette démarche sont traités dans un délai de #{distance_of_time_in_words(dossier.procedure.usual_instruction_time)}.
|
||||
= render partial: 'users/dossiers/show/estimated_delay', locals: { procedure: dossier.procedure }
|
||||
|
||||
- elsif dossier.accepte?
|
||||
.accepte
|
||||
|
|
|
@ -20,6 +20,8 @@ Flipflop.configure do
|
|||
group :production do
|
||||
feature :remote_storage,
|
||||
default: ENV['FOG_ENABLED'] == 'enabled'
|
||||
feature :insee_api_v3,
|
||||
default: true
|
||||
feature :weekly_overview,
|
||||
default: ENV['APP_NAME'] == 'tps'
|
||||
feature :pre_maintenance_mode
|
||||
|
|
|
@ -1 +1,29 @@
|
|||
ActiveStorage::Service.url_expires_in = 1.hour
|
||||
|
||||
# We want to run the virus scan on every ActiveStorage attachment,
|
||||
# regardless of its type (user-uploaded document, instructeur-uploaded attestation, form template, etc.)
|
||||
#
|
||||
# To do this, the best place to work on is the ActiveStorage::Attachment
|
||||
# objects themselves.
|
||||
#
|
||||
# We have to monkey patch ActiveStorage in order to always run an analyzer.
|
||||
# The way analyzable blob interface work is by running the first accepted analyzer.
|
||||
# This is not what we want for the virus scan. Using analyzer interface is still beneficial
|
||||
# as it takes care of downloading the blob.
|
||||
ActiveStorage::Attachment.class_eval do
|
||||
after_create_commit :virus_scan
|
||||
|
||||
private
|
||||
|
||||
def virus_scan
|
||||
ActiveStorage::VirusScanner.new(blob).analyze_later
|
||||
end
|
||||
end
|
||||
|
||||
ActiveStorage::Attached::One.class_eval do
|
||||
def virus_scanner
|
||||
if attached?
|
||||
ActiveStorage::VirusScanner.new(attachment.blob)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -182,7 +182,8 @@ fr:
|
|||
dossier_map_not_activated: "Le dossier n'a pas accès à la cartographie."
|
||||
invalid_siret: "Le siret est incorrect"
|
||||
procedure_not_found: "La démarche n'existe pas"
|
||||
siret_unknown: 'Désolé, nous n’avons pas trouvé d’établissement enregistré correspondant à ce numéro SIRET'
|
||||
siret_unknown: 'Désolé, nous n’avons pas trouvé d’établissement enregistré correspondant à ce numéro SIRET.'
|
||||
siret_network_error: 'Désolé, la récupération des informations SIRET est temporairement indisponible. Veuillez réessayer dans quelques instants.'
|
||||
etablissement_fail: 'Désolé, nous n’avons pas réussi à enregistrer l’établissement correspondant à ce numéro SIRET'
|
||||
france_connect:
|
||||
connexion: "Erreur lors de la connexion à France Connect."
|
||||
|
|
|
@ -7,5 +7,6 @@ const resolve = {
|
|||
}
|
||||
};
|
||||
|
||||
environment.splitChunks();
|
||||
environment.config.merge({ resolve });
|
||||
module.exports = environment;
|
||||
|
|
|
@ -2,8 +2,4 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
|||
|
||||
const environment = require('./environment');
|
||||
|
||||
// https://github.com/rails/webpacker/issues/1235
|
||||
environment.config.optimization.minimizer[0].options.uglifyOptions.ecma = 5; // for IE 11 support
|
||||
environment.config.optimization.minimizer[0].options.uglifyOptions.safari10 = true;
|
||||
|
||||
module.exports = environment.toWebpackConfig();
|
||||
|
|
22
lib/tasks/deployment/20190425102459_migrate_virus_scans.rake
Normal file
22
lib/tasks/deployment/20190425102459_migrate_virus_scans.rake
Normal file
|
@ -0,0 +1,22 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: migrate_virus_scans'
|
||||
task migrate_virus_scans: :environment do
|
||||
puts "Running deploy task 'migrate_virus_scans'"
|
||||
|
||||
virus_scans = VirusScan.all
|
||||
progress = ProgressReport.new(virus_scans.count)
|
||||
virus_scans.find_each do |virus_scan|
|
||||
blob = ActiveStorage::Blob.find_by(key: virus_scan.blob_key)
|
||||
if blob
|
||||
metadata = { virus_scan_result: virus_scan.status, scanned_at: virus_scan.scanned_at }
|
||||
blob.update_column(:metadata, blob.metadata.merge(metadata))
|
||||
end
|
||||
progress.inc
|
||||
end
|
||||
progress.finish
|
||||
|
||||
# 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).
|
||||
AfterParty::TaskRecord.create version: '20190425102459'
|
||||
end
|
||||
end
|
46
package.json
46
package.json
|
@ -1,44 +1,44 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.15",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.7.2",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.17",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.8.1",
|
||||
"@fortawesome/react-fontawesome": "^0.1.4",
|
||||
"@rails/webpacker": "4.0.0-pre.3",
|
||||
"@sentry/browser": "^4.6.5",
|
||||
"@rails/webpacker": "4.0.2",
|
||||
"@sentry/browser": "^5.1.0",
|
||||
"@turf/area": "^6.0.1",
|
||||
"activestorage": "^5.2.2",
|
||||
"activestorage": "^5.2.3",
|
||||
"autocomplete.js": "^0.36.0",
|
||||
"babel-plugin-macros": "^2.5.0",
|
||||
"babel-plugin-macros": "^2.5.1",
|
||||
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
|
||||
"chartkick": "^3.0.1",
|
||||
"chartkick": "^3.0.2",
|
||||
"debounce": "^1.2.0",
|
||||
"dom4": "^2.1.3",
|
||||
"dom4": "^2.1.4",
|
||||
"highcharts": "^6.1.2",
|
||||
"jquery": "^3.3.1",
|
||||
"leaflet-freedraw": "^2.9.0",
|
||||
"leaflet": "^1.3.4",
|
||||
"jquery": "^3.4.0",
|
||||
"leaflet": "^1.4.0",
|
||||
"leaflet-freedraw": "^2.10.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"rails-ujs": "^5.2.2",
|
||||
"ramda": "^0.25.0",
|
||||
"react_ujs": "^2.4.4",
|
||||
"react-dom": "^16.8.4",
|
||||
"rails-ujs": "^5.2.3",
|
||||
"ramda": "=0.24.1",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-scroll-to-component": "^1.0.2",
|
||||
"react-sortable-hoc": "^1.7.1",
|
||||
"react": "^16.8.4",
|
||||
"react_ujs": "^2.5.0",
|
||||
"select2": "^4.0.6-rc.1",
|
||||
"turbolinks": "^5.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^10.0.1",
|
||||
"eclint": "^2.8.0",
|
||||
"eslint": "^5.9.0",
|
||||
"eslint-config-prettier": "^3.3.0",
|
||||
"eslint-plugin-prettier": "^3.0.0",
|
||||
"eclint": "^2.8.1",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-prettier": "^4.1.0",
|
||||
"eslint-plugin-prettier": "^3.0.1",
|
||||
"eslint-plugin-react": "^7.12.4",
|
||||
"eslint-plugin-react-hooks": "^1.5.1",
|
||||
"prettier": "^1.15.3",
|
||||
"webpack-dev-server": "^3.1.9"
|
||||
"eslint-plugin-react-hooks": "^1.6.0",
|
||||
"prettier": "^1.17.0",
|
||||
"webpack-dev-server": "^3.3.1"
|
||||
},
|
||||
"scripts": {
|
||||
"lint:ec": "eclint check $({ git ls-files ; find vendor -type f ; echo 'db/schema.rb' ; } | sort | uniq -u)",
|
||||
|
|
|
@ -45,7 +45,7 @@ describe Champs::CarteController, type: :controller do
|
|||
expect(assigns(:error)).to eq(nil)
|
||||
expect(champ.reload.value).to eq(nil)
|
||||
expect(champ.reload.geo_areas).to eq([])
|
||||
expect(response.body).to include("DS.drawMapData(\".carte-1\", {\"position\":{\"lon\":\"2.428462\",\"lat\":\"46.538192\",\"zoom\":\"13\"},\"selection\":null,\"quartiersPrioritaires\":[],\"cadastres\":[],\"parcellesAgricoles\":[]});")
|
||||
expect(response.body).to include("DS.fire('carte:update', {\"selector\":\".carte-1\",\"data\":{\"position\":{\"lon\":\"2.428462\",\"lat\":\"46.538192\",\"zoom\":\"13\"},\"selection\":null,\"quartiersPrioritaires\":[],\"cadastres\":[],\"parcellesAgricoles\":[]}});")
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -20,74 +20,105 @@ describe Champs::SiretController, type: :controller do
|
|||
end
|
||||
let(:siret) { '' }
|
||||
|
||||
context 'when user is connected' do
|
||||
context 'when the user is signed in' do
|
||||
render_views
|
||||
before { sign_in user }
|
||||
|
||||
context 'when siret empty' do
|
||||
before {
|
||||
get :show, params: params, format: 'js'
|
||||
}
|
||||
context 'when the SIRET is empty' do
|
||||
subject! { get :show, params: params, format: 'js' }
|
||||
|
||||
it 'empty info message' do
|
||||
it 'clears the etablissement and SIRET on the model' do
|
||||
champ.reload
|
||||
expect(champ.etablissement).to be_nil
|
||||
expect(champ.value).to be_empty
|
||||
end
|
||||
|
||||
it 'clears any information or error message' do
|
||||
expect(response.body).to include('.siret-info-1')
|
||||
expect(response.body).to include('innerHTML = ""')
|
||||
champ.reload
|
||||
expect(champ.etablissement).to be_nil
|
||||
expect(champ.value).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when siret invalid' do
|
||||
context 'when the SIRET is invalid' do
|
||||
let(:siret) { '1234' }
|
||||
before {
|
||||
get :show, params: params, format: 'js'
|
||||
}
|
||||
|
||||
it 'invalid error' do
|
||||
subject! { get :show, params: params, format: 'js' }
|
||||
|
||||
it 'clears the etablissement and SIRET on the model' do
|
||||
champ.reload
|
||||
expect(champ.etablissement).to be_nil
|
||||
expect(champ.value).to be_empty
|
||||
end
|
||||
|
||||
it 'displays a “SIRET is invalid” error message' do
|
||||
expect(response.body).to include('Le numéro de SIRET doit comporter exactement 14 chiffres.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the API is unavailable' do
|
||||
let(:siret) { '82161143100015' }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:find_etablissement_with_siret).and_raise(RestClient::RequestFailed)
|
||||
end
|
||||
|
||||
subject! { get :show, params: params, format: 'js' }
|
||||
|
||||
it 'clears the etablissement and SIRET on the model' do
|
||||
champ.reload
|
||||
expect(champ.etablissement).to be_nil
|
||||
expect(champ.value).to be_empty
|
||||
end
|
||||
|
||||
it 'displays a “API is unavailable” error message' do
|
||||
expect(response.body).to include(I18n.t('errors.messages.siret_network_error'))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when siret not found' do
|
||||
let(:siret) { '0' * 14 }
|
||||
before {
|
||||
expect(subject).to receive(:find_etablisement_with_siret).and_return(false)
|
||||
get :show, params: params, format: 'js'
|
||||
}
|
||||
context 'when the SIRET is valid but unknown' do
|
||||
let(:siret) { '00000000000000' }
|
||||
|
||||
it 'not found error' do
|
||||
before do
|
||||
allow(controller).to receive(:find_etablissement_with_siret).and_return(false)
|
||||
end
|
||||
|
||||
subject! { get :show, params: params, format: 'js' }
|
||||
|
||||
it 'clears the etablissement and SIRET on the model' do
|
||||
champ.reload
|
||||
expect(champ.etablissement).to be_nil
|
||||
expect(champ.value).to be_empty
|
||||
end
|
||||
|
||||
it 'displays a “SIRET not found” error message' do
|
||||
expect(response.body).to include('Nous n’avons pas trouvé d’établissement correspondant à ce numéro de SIRET.')
|
||||
champ.reload
|
||||
expect(champ.etablissement).to be_nil
|
||||
expect(champ.value).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when siret found' do
|
||||
context 'when the SIRET informations are retrieved successfully' do
|
||||
let(:siret) { etablissement.siret }
|
||||
let(:etablissement) { build(:etablissement) }
|
||||
before {
|
||||
expect(subject).to receive(:find_etablisement_with_siret).and_return(etablissement)
|
||||
get :show, params: params, format: 'js'
|
||||
}
|
||||
|
||||
it 'etablissement info message' do
|
||||
expect(response.body).to include(etablissement.entreprise_raison_sociale)
|
||||
before do
|
||||
allow(controller).to receive(:find_etablissement_with_siret).and_return(etablissement)
|
||||
end
|
||||
|
||||
subject! { get :show, params: params, format: 'js' }
|
||||
|
||||
it 'populates the etablissement and SIRET on the model' do
|
||||
champ.reload
|
||||
expect(champ.value).to eq(etablissement.siret)
|
||||
expect(champ.etablissement.siret).to eq(etablissement.siret)
|
||||
end
|
||||
|
||||
it 'displays the name of the company' do
|
||||
expect(response.body).to include(etablissement.entreprise_raison_sociale)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not connected' do
|
||||
before {
|
||||
get :show, params: { position: '1' }, format: 'js'
|
||||
}
|
||||
context 'when user is not signed in' do
|
||||
subject! { get :show, params: { position: '1' }, format: 'js' }
|
||||
|
||||
it { expect(response.code).to eq('401') }
|
||||
end
|
||||
|
|
|
@ -278,6 +278,13 @@ describe Users::DossiersController, type: :controller do
|
|||
context 'with a valid SIRET' do
|
||||
let(:params_siret) { '440 117 620 01530' }
|
||||
|
||||
context 'When API-Entreprise is down' do
|
||||
let(:api_etablissement_status) { 502 }
|
||||
let(:api_body_status) { File.read('spec/fixtures/files/api_entreprise/exercices_unavailable.json') }
|
||||
|
||||
it_behaves_like 'the request fails with an error', I18n.t('errors.messages.siret_network_error')
|
||||
end
|
||||
|
||||
context 'when API-Entreprise doesn’t know this SIRET' do
|
||||
let(:api_etablissement_status) { 404 }
|
||||
let(:api_body_status) { '' }
|
||||
|
|
|
@ -167,8 +167,8 @@ feature 'The user' do
|
|||
expect(page).to have_text('analyse antivirus en cours')
|
||||
|
||||
# Mark file as scanned and clean
|
||||
virus_scan = VirusScan.last
|
||||
virus_scan.update(scanned_at: Time.zone.now, status: VirusScan.statuses.fetch(:safe))
|
||||
attachment = ActiveStorage::Attachment.last
|
||||
attachment.blob.update(metadata: attachment.blob.metadata.merge(scanned_at: Time.zone.now, virus_scan_result: ActiveStorage::VirusScanner::SAFE))
|
||||
within '.piece-justificative' do
|
||||
click_on 'rafraichir'
|
||||
end
|
||||
|
|
|
@ -17,28 +17,24 @@ describe 'Dossier details:' do
|
|||
end
|
||||
|
||||
describe "the user can see the mean time they are expected to wait" do
|
||||
context "when the dossier is in construction" do
|
||||
before do
|
||||
other_dossier = create(:dossier, :accepte, :for_individual, procedure: procedure, en_construction_at: 10.days.ago, en_instruction_at: Time.zone.now)
|
||||
visit dossier_path(dossier)
|
||||
end
|
||||
let(:other_dossier) { create(:dossier, :accepte, :for_individual, procedure: procedure, en_construction_at: 10.days.ago, en_instruction_at: 9.days.ago, processed_at: Time.zone.now) }
|
||||
|
||||
it { expect(page).to have_text("Habituellement, les dossiers de cette démarche sont vérifiés dans un délai de 10 jours.") }
|
||||
context "when the dossier is in construction" do
|
||||
it "displays the estimated wait duration" do
|
||||
other_dossier
|
||||
visit dossier_path(dossier)
|
||||
expect(page).to have_text("Habituellement, les dossiers de cette démarche sont traités dans un délai de 10 jours.")
|
||||
end
|
||||
end
|
||||
|
||||
context "when the dossier is in instruction" do
|
||||
let(:dossier) { create(:dossier, :en_instruction, :for_individual, :with_commentaires, user: user, procedure: procedure) }
|
||||
|
||||
before do
|
||||
Timecop.freeze(Time.zone.local(2012, 12, 20))
|
||||
|
||||
other_dossier = create(:dossier, :accepte, :for_individual, procedure: procedure, en_instruction_at: 60.days.ago, processed_at: Time.zone.now)
|
||||
it "displays the estimated wait duration" do
|
||||
other_dossier
|
||||
visit dossier_path(dossier)
|
||||
expect(page).to have_text("Habituellement, les dossiers de cette démarche sont traités dans un délai de 10 jours.")
|
||||
end
|
||||
|
||||
after { Timecop.return }
|
||||
|
||||
it { expect(page).to have_text("Habituellement, les dossiers de cette démarche sont traités dans un délai de 2 mois.") }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
7
spec/fixtures/files/api_entreprise/entreprises_unavailable.json
vendored
Normal file
7
spec/fixtures/files/api_entreprise/entreprises_unavailable.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"errors": [
|
||||
"Erreur interne du serveur",
|
||||
"Le siret ou siren indiqué n'existe pas, n'est pas connu ou ne comporte aucune information pour cet appel"
|
||||
],
|
||||
"gateway_error": true
|
||||
}
|
|
@ -1,12 +1,11 @@
|
|||
RSpec.describe AntiVirusJob, type: :job do
|
||||
RSpec.describe VirusScannerJob, type: :job do
|
||||
let(:champ) do
|
||||
champ = create(:champ, :piece_justificative)
|
||||
champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain")
|
||||
champ
|
||||
end
|
||||
let(:virus_scan) { create(:virus_scan, status: VirusScan.statuses.fetch(:pending), champ: champ, blob_key: champ.piece_justificative_file.blob.key) }
|
||||
|
||||
subject { AntiVirusJob.new.perform(virus_scan) }
|
||||
subject { VirusScannerJob.new.perform(champ.piece_justificative_file.blob) }
|
||||
|
||||
context "when no virus is found" do
|
||||
let(:virus_found?) { true }
|
||||
|
@ -16,7 +15,7 @@ RSpec.describe AntiVirusJob, type: :job do
|
|||
subject
|
||||
end
|
||||
|
||||
it { expect(virus_scan.reload.status).to eq(VirusScan.statuses.fetch(:safe)) }
|
||||
it { expect(champ.piece_justificative_file.virus_scanner.safe?).to be_truthy }
|
||||
end
|
||||
|
||||
context "when a virus is found" do
|
||||
|
@ -27,6 +26,6 @@ RSpec.describe AntiVirusJob, type: :job do
|
|||
subject
|
||||
end
|
||||
|
||||
it { expect(virus_scan.reload.status).to eq(VirusScan.statuses.fetch(:infected)) }
|
||||
it { expect(champ.piece_justificative_file.virus_scanner.infected?).to be_truthy }
|
||||
end
|
||||
end
|
|
@ -2,21 +2,35 @@ require 'spec_helper'
|
|||
|
||||
describe ApiEntreprise::API do
|
||||
let(:procedure_id) { 12 }
|
||||
|
||||
describe '.entreprise' do
|
||||
subject { described_class.entreprise(siren, procedure_id) }
|
||||
|
||||
before do
|
||||
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/entreprises\/#{siren}?.*token=/)
|
||||
.to_return(status: status, body: body)
|
||||
end
|
||||
|
||||
context 'when the service is unavailable' do
|
||||
let(:siren) { '111111111' }
|
||||
let(:status) { 502 }
|
||||
let(:body) { File.read('spec/fixtures/files/api_entreprise/entreprises_unavailable.json') }
|
||||
|
||||
it 'raises RestClient::RequestFailed' do
|
||||
expect { subject }.to raise_error(RestClient::RequestFailed)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when siren does not exist' do
|
||||
let(:siren) { '111111111' }
|
||||
let(:status) { 404 }
|
||||
let(:body) { '' }
|
||||
let(:body) { File.read('spec/fixtures/files/api_entreprise/entreprises_not_found.json') }
|
||||
|
||||
it 'raises RestClient::ResourceNotFound' do
|
||||
expect { subject }.to raise_error(RestClient::ResourceNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when siret exist' do
|
||||
let(:siren) { '418166096' }
|
||||
let(:status) { 200 }
|
||||
|
|
|
@ -11,7 +11,7 @@ describe ApiEntreprise::EntrepriseAdapter do
|
|||
.to_return(body: body, status: status)
|
||||
end
|
||||
|
||||
context "when SIRET is OK" do
|
||||
context "when the SIRET is valid" do
|
||||
let(:body) { File.read('spec/fixtures/files/api_entreprise/entreprises.json') }
|
||||
let(:status) { 200 }
|
||||
|
||||
|
@ -70,12 +70,21 @@ describe ApiEntreprise::EntrepriseAdapter do
|
|||
end
|
||||
end
|
||||
|
||||
context "when SIRET is KO" do
|
||||
context "when the SIRET is unknown" do
|
||||
let(:body) { File.read('spec/fixtures/files/api_entreprise/entreprises_not_found.json') }
|
||||
let(:status) { 206 }
|
||||
let(:status) { 404 }
|
||||
|
||||
it '#to_params class est une Hash ?' do
|
||||
expect(subject).to eq({})
|
||||
end
|
||||
end
|
||||
|
||||
context "when the service is unavailable" do
|
||||
let(:body) { File.read('spec/fixtures/files/api_entreprise/entreprises_unavailable.json') }
|
||||
let(:status) { 502 }
|
||||
|
||||
it 'raises an exception' do
|
||||
expect { subject }.to raise_error(RestClient::RequestFailed)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,25 +10,15 @@ describe ApiEntreprise::ExercicesAdapter do
|
|||
.to_return(body: File.read('spec/fixtures/files/api_entreprise/exercices.json', status: 200))
|
||||
end
|
||||
|
||||
it '#to_params class est un Hash ?' do
|
||||
expect(subject).to be_an_instance_of(Hash)
|
||||
end
|
||||
it { is_expected.to be_an_instance_of(Hash) }
|
||||
|
||||
it 'have 3 exercices' do
|
||||
it 'contains several exercices attributes' do
|
||||
expect(subject[:exercices_attributes].size).to eq(3)
|
||||
end
|
||||
|
||||
context 'Attributs Exercices' do
|
||||
it 'L\'exercice contient bien un ca' do
|
||||
it 'contains informations in each exercices_attributes' do
|
||||
expect(subject[:exercices_attributes][0][:ca]).to eq('21009417')
|
||||
end
|
||||
|
||||
it 'L\'exercice contient bien une date de fin d\'exercice' do
|
||||
expect(subject[:exercices_attributes][0][:date_fin_exercice]).to eq("2013-12-31T00:00:00+01:00")
|
||||
end
|
||||
|
||||
it 'L\'exercice contient bien une date_fin_exercice_timestamp' do
|
||||
expect(subject[:exercices_attributes][0][:date_fin_exercice_timestamp]).to eq(1388444400)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,7 +17,7 @@ describe ApiEntreprise::RNAAdapter do
|
|||
context 'when siret is not valid' do
|
||||
let(:siret) { '234567' }
|
||||
let(:body) { '' }
|
||||
let(:status) { '404' }
|
||||
let(:status) { 404 }
|
||||
|
||||
it { is_expected.to eq({}) }
|
||||
end
|
||||
|
|
|
@ -383,20 +383,19 @@ describe Champ do
|
|||
let(:type_de_champ) { create(:type_de_champ_piece_justificative) }
|
||||
|
||||
context 'and there is a blob' do
|
||||
before { champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain") }
|
||||
before do
|
||||
champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain")
|
||||
champ.save
|
||||
end
|
||||
|
||||
it { expect { champ.save }.to change(VirusScan, :count).by(1) }
|
||||
it { expect(champ.piece_justificative_file.virus_scanner.analyzed?).to be_truthy }
|
||||
end
|
||||
|
||||
context 'and there is no blob' do
|
||||
it { expect { champ.save }.to_not change(VirusScan, :count) }
|
||||
end
|
||||
end
|
||||
before { champ.save }
|
||||
|
||||
context 'when type_champ is not type_de_champ_piece_justificative' do
|
||||
let(:type_de_champ) { create(:type_de_champ_textarea) }
|
||||
|
||||
it { expect { champ.save }.to_not change(VirusScan, :count) }
|
||||
it { expect(champ.piece_justificative_file.virus_scanner).to be_nil }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,23 +1,24 @@
|
|||
describe Champs::PieceJustificativeChamp do
|
||||
describe '#for_api' do
|
||||
let(:champ_pj) { create(:champ_piece_justificative) }
|
||||
let(:metadata) { champ_pj.piece_justificative_file.blob.metadata }
|
||||
|
||||
before { champ_pj.virus_scan.update(status: status) }
|
||||
before { champ_pj.piece_justificative_file.blob.update(metadata: metadata.merge(virus_scan_result: status)) }
|
||||
|
||||
subject { champ_pj.for_api }
|
||||
|
||||
context 'when file is safe' do
|
||||
let(:status) { 'safe' }
|
||||
let(:status) { ActiveStorage::VirusScanner::SAFE }
|
||||
it { is_expected.to include("/rails/active_storage/blobs/") }
|
||||
end
|
||||
|
||||
context 'when file is not scanned' do
|
||||
let(:status) { 'pending' }
|
||||
let(:status) { ActiveStorage::VirusScanner::PENDING }
|
||||
it { is_expected.to include("/rails/active_storage/blobs/") }
|
||||
end
|
||||
|
||||
context 'when file is infected' do
|
||||
let(:status) { 'infected' }
|
||||
let(:status) { ActiveStorage::VirusScanner::INFECTED }
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -727,59 +727,17 @@ describe Procedure do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#usual_verification_time' do
|
||||
describe '#usual_traitement_time' do
|
||||
let(:procedure) { create(:procedure) }
|
||||
|
||||
def create_dossier(construction_date:, instruction_date:)
|
||||
dossier = create(:dossier, :en_instruction, procedure: procedure)
|
||||
dossier.update!(en_construction_at: construction_date, en_instruction_at: instruction_date)
|
||||
end
|
||||
|
||||
before do
|
||||
delays.each do |delay|
|
||||
create_dossier(construction_date: 1.week.ago - delay, instruction_date: 1.week.ago)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are several dossiers in the time frame' do
|
||||
let(:delays) { [1.day, 2.days, 2.days, 2.days, 2.days, 3.days, 3.days, 3.days, 3.days, 12.days] }
|
||||
|
||||
it 'returns a time representative of the dossier verification delay' do
|
||||
expect(procedure.usual_verification_time).to be_between(3.days, 4.days)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are very old dossiers' do
|
||||
let(:delays) { [2.days, 2.days] }
|
||||
let!(:old_dossier) { create_dossier(construction_date: 3.months.ago, instruction_date: 2.months.ago) }
|
||||
|
||||
it 'ignores dossiers older than 1 month' do
|
||||
expect(procedure.usual_verification_time).to be_within(1.hour).of(2.days)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is only one dossier in the time frame' do
|
||||
let(:delays) { [1.day] }
|
||||
it { expect(procedure.usual_verification_time).to be_within(1.hour).of(1.day) }
|
||||
end
|
||||
|
||||
context 'where there are no dossiers' do
|
||||
let(:delays) { [] }
|
||||
it { expect(procedure.usual_verification_time).to be_nil }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#usual_instruction_time' do
|
||||
let(:procedure) { create(:procedure) }
|
||||
|
||||
def create_dossier(instruction_date:, processed_date:)
|
||||
def create_dossier(construction_date:, instruction_date:, processed_date:)
|
||||
dossier = create(:dossier, :accepte, procedure: procedure)
|
||||
dossier.update!(en_instruction_at: instruction_date, processed_at: processed_date)
|
||||
dossier.update!(en_construction_at: construction_date, en_instruction_at: instruction_date, processed_at: processed_date)
|
||||
end
|
||||
|
||||
before do
|
||||
delays.each do |delay|
|
||||
create_dossier(instruction_date: 1.week.ago - delay, processed_date: 1.week.ago)
|
||||
create_dossier(construction_date: 1.week.ago - delay, instruction_date: 1.week.ago - delay + 12.hours, processed_date: 1.week.ago)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -787,36 +745,36 @@ describe Procedure do
|
|||
let(:delays) { [1.day, 2.days, 2.days, 2.days, 2.days, 3.days, 3.days, 3.days, 3.days, 12.days] }
|
||||
|
||||
it 'returns a time representative of the dossier instruction delay' do
|
||||
expect(procedure.usual_instruction_time).to be_between(3.days, 4.days)
|
||||
expect(procedure.usual_traitement_time).to be_between(3.days, 4.days)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are very old dossiers' do
|
||||
let(:delays) { [2.days, 2.days] }
|
||||
let!(:old_dossier) { create_dossier(instruction_date: 3.months.ago, processed_date: 2.months.ago) }
|
||||
let!(:old_dossier) { create_dossier(construction_date: 3.months.ago, instruction_date: 2.months.ago, processed_date: 2.months.ago) }
|
||||
|
||||
it 'ignores dossiers older than 1 month' do
|
||||
expect(procedure.usual_instruction_time).to be_within(1.hour).of(2.days)
|
||||
expect(procedure.usual_traitement_time).to be_within(1.hour).of(2.days)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a dossier with bad data' do
|
||||
let(:delays) { [2.days, 2.days] }
|
||||
let!(:bad_dossier) { create_dossier(instruction_date: nil, processed_date: 10.days.ago) }
|
||||
let!(:bad_dossier) { create_dossier(construction_date: nil, instruction_date: nil, processed_date: 10.days.ago) }
|
||||
|
||||
it 'ignores bad dossiers' do
|
||||
expect(procedure.usual_instruction_time).to be_within(1.hour).of(2.days)
|
||||
expect(procedure.usual_traitement_time).to be_within(1.hour).of(2.days)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is only one processed dossier' do
|
||||
let(:delays) { [1.day] }
|
||||
it { expect(procedure.usual_instruction_time).to be_within(1.hour).of(1.day) }
|
||||
it { expect(procedure.usual_traitement_time).to be_within(1.hour).of(1.day) }
|
||||
end
|
||||
|
||||
context 'where there is no processed dossier' do
|
||||
let(:delays) { [] }
|
||||
it { expect(procedure.usual_instruction_time).to be_nil }
|
||||
it { expect(procedure.usual_traitement_time).to be_nil }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ describe ChampSerializer do
|
|||
|
||||
before do
|
||||
champ.piece_justificative_file.attach({ filename: __FILE__, io: File.open(__FILE__) })
|
||||
champ.reload.virus_scan.safe!
|
||||
champ.piece_justificative_file.virus_scanner.analyze_later
|
||||
end
|
||||
after { champ.piece_justificative_file.purge }
|
||||
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe 'shared/champs/piece_justificative/_pj_link.html.haml', type: :view do
|
||||
let(:champ) { create(:champ, :piece_justificative, :with_piece_justificative_file) }
|
||||
let(:virus_scan) { nil }
|
||||
describe 'shared/piece_jointe/_pj_link.html.haml', type: :view do
|
||||
let(:champ) { create(:champ_piece_justificative) }
|
||||
let(:virus_scan_result) { nil }
|
||||
|
||||
before do
|
||||
if virus_scan
|
||||
champ.update(virus_scan: virus_scan)
|
||||
end
|
||||
champ.piece_justificative_file.blob.update(metadata: champ.piece_justificative_file.blob.metadata.merge(virus_scan_result: virus_scan_result))
|
||||
end
|
||||
|
||||
subject { render 'shared/champs/piece_justificative/pj_link', champ: champ, user_can_upload: false }
|
||||
subject { render 'shared/piece_jointe/pj_link', object: champ, pj: champ.piece_justificative_file, user_can_upload: false }
|
||||
|
||||
context 'when there is no anti-virus scan' do
|
||||
let(:virus_scan) { nil }
|
||||
let(:virus_scan_result) { nil }
|
||||
|
||||
it 'allows to download the file' do
|
||||
expect(subject).to have_link(champ.piece_justificative_file.filename.to_s)
|
||||
|
@ -22,7 +20,7 @@ describe 'shared/champs/piece_justificative/_pj_link.html.haml', type: :view do
|
|||
end
|
||||
|
||||
context 'when the anti-virus scan is pending' do
|
||||
let(:virus_scan) { create(:virus_scan, :pending) }
|
||||
let(:virus_scan_result) { ActiveStorage::VirusScanner::PENDING }
|
||||
|
||||
it 'displays the filename, but doesn’t allow to download the file' do
|
||||
expect(subject).to have_text(champ.piece_justificative_file.filename.to_s)
|
||||
|
@ -32,7 +30,7 @@ describe 'shared/champs/piece_justificative/_pj_link.html.haml', type: :view do
|
|||
end
|
||||
|
||||
context 'when the file is scanned and safe' do
|
||||
let(:virus_scan) { create(:virus_scan, :safe) }
|
||||
let(:virus_scan_result) { ActiveStorage::VirusScanner::SAFE }
|
||||
|
||||
it 'allows to download the file' do
|
||||
expect(subject).to have_link(champ.piece_justificative_file.filename.to_s)
|
||||
|
@ -40,7 +38,7 @@ describe 'shared/champs/piece_justificative/_pj_link.html.haml', type: :view do
|
|||
end
|
||||
|
||||
context 'when the file is scanned and infected' do
|
||||
let(:virus_scan) { create(:virus_scan, :infected) }
|
||||
let(:virus_scan_result) { ActiveStorage::VirusScanner::INFECTED }
|
||||
|
||||
it 'displays the filename, but doesn’t allow to download the file' do
|
||||
expect(subject).to have_text(champ.piece_justificative_file.filename.to_s)
|
|
@ -1,4 +1,6 @@
|
|||
describe 'users/dossiers/show/_status_overview.html.haml', type: :view do
|
||||
before { allow(dossier.procedure).to receive(:usual_traitement_time).and_return(1.day) }
|
||||
|
||||
subject! { render 'users/dossiers/show/status_overview.html.haml', dossier: dossier }
|
||||
|
||||
matcher :have_timeline_item do |selector|
|
||||
|
@ -46,6 +48,7 @@ describe 'users/dossiers/show/_status_overview.html.haml', type: :view do
|
|||
end
|
||||
|
||||
it { is_expected.to have_selector('.status-explanation .en-construction') }
|
||||
it { is_expected.to have_text('Habituellement, les dossiers de cette démarche sont traités dans un délai de 1 jour') }
|
||||
end
|
||||
|
||||
context 'when en instruction' do
|
||||
|
@ -59,6 +62,7 @@ describe 'users/dossiers/show/_status_overview.html.haml', type: :view do
|
|||
end
|
||||
|
||||
it { is_expected.to have_selector('.status-explanation .en-instruction') }
|
||||
it { is_expected.to have_text('Habituellement, les dossiers de cette démarche sont traités dans un délai de 1 jour') }
|
||||
end
|
||||
|
||||
context 'when accepté' do
|
||||
|
|
Loading…
Reference in a new issue