Merge pull request #4679 from betagouv/dev

2020-01-13-01
This commit is contained in:
Keirua 2020-01-13 16:01:51 +01:00 committed by GitHub
commit a9c02fded1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
98 changed files with 557 additions and 1309 deletions

View file

@ -64,7 +64,6 @@ gem 'rgeo-geojson'
gem 'sanitize-url'
gem 'sassc-rails' # Use SCSS for stylesheets
gem 'scenic'
gem 'select2-rails'
gem 'sentry-raven'
gem 'skylight'
gem 'smart_listing'

View file

@ -592,8 +592,6 @@ GEM
scss_lint (0.57.1)
rake (>= 0.9, < 13)
sass (~> 3.5, >= 3.5.5)
select2-rails (4.0.3)
thor (~> 0.14)
selectize-rails (0.12.6)
selenium-webdriver (3.141.0)
childprocess (~> 0.5)
@ -793,7 +791,6 @@ DEPENDENCIES
sassc-rails
scenic
scss_lint
select2-rails
sentry-raven
shoulda-matchers
simple_xlsx_reader

View file

@ -29,7 +29,6 @@
// = require search
// = require site_banner
// = require switch_menu
// = require autocomplete
// = require users
// = require attestation_template_edit

View file

@ -1,33 +0,0 @@
.algolia-autocomplete {
width: 100%;
}
.algolia-autocomplete .aa-input,
.algolia-autocomplete .aa-hint {
width: 100%;
}
.algolia-autocomplete .aa-hint {
color: #999999;
}
.algolia-autocomplete .aa-dropdown-menu {
width: 100%;
background-color: #FFFFFF;
border: 1px solid #999999;
border-top: none;
}
.algolia-autocomplete .aa-dropdown-menu .aa-suggestion {
cursor: pointer;
padding: 5px 4px;
}
.algolia-autocomplete .aa-dropdown-menu .aa-suggestion.aa-cursor {
background-color: #B2D7FF;
}
.algolia-autocomplete .aa-dropdown-menu .aa-suggestion em {
font-weight: bold;
font-style: normal;
}

View file

@ -242,6 +242,16 @@
}
}
.select2-selection--single {
min-height: 62px;
// scss-lint:disable SelectorFormat
.select2-selection__arrow {
display: none;
}
// scss-lint:enable
}
// scss-lint:disable SelectorFormat
.select2-selection__rendered {
padding: $default-padding;

View file

@ -63,6 +63,10 @@
width: 200px;
margin-right: 10px;
}
.select2-container {
margin-bottom: 0;
}
}
}

View file

@ -5,6 +5,5 @@
// = require ./fonts
// = require leaflet
// = require select2
// = require autocomplete
// = require_tree .
// = stub ./print.scss

View file

@ -1,24 +0,0 @@
class AddressController < ApplicationController
def suggestions
request = params[:request]
json = ApiAdresse::AddressAdapter.new(request).get_suggestions.map do |value|
{ label: value }
end.to_json
render json: json
end
def geocode
request = params[:request]
point = ApiAdresse::PointAdapter.new(request).geocode
if point.present?
lon = point.x.to_s
lat = point.y.to_s
end
render json: { lon: lon, lat: lat, zoom: '14', dossier_id: params[:dossier_id] }
end
end

View file

@ -0,0 +1,13 @@
class APIGeoTestController < ActionController::Base
def regions
render json: [{ nom: 'Martinique' }]
end
def departements
render json: [{ nom: 'Aisne', code: '02' }]
end
def communes
render json: [{ nom: 'Ambléon' }]
end
end

View file

@ -117,6 +117,7 @@ class ApplicationController < ActionController::Base
def setup_javascript_settings
gon.autosave = Rails.application.config.ds_autosave
gon.autocomplete = Rails.application.secrets.autocomplete
end
def setup_tracking

View file

@ -15,7 +15,7 @@ module Instructeurs
def attestation
if dossier.attestation.pdf.attached?
redirect_to url_for(dossier.attestation.pdf)
redirect_to dossier.attestation.pdf.service_url
end
end

View file

@ -10,7 +10,7 @@ module Instructeurs
.procedures
.with_attached_logo
.includes(:defaut_groupe_instructeur)
.order(closed_at: :desc, archived_at: :desc, unpublished_at: :desc, published_at: :desc, created_at: :desc)
.order(closed_at: :desc, unpublished_at: :desc, published_at: :desc, created_at: :desc)
dossiers = current_instructeur.dossiers.joins(:groupe_instructeur)
@dossiers_count_per_procedure = dossiers.all_state.group('groupe_instructeurs.procedure_id').reorder(nil).count

View file

@ -20,5 +20,18 @@ module Manager
head :ok
end
def delete
user = User.find(params[:id])
if !user.can_be_deleted?
fail "Impossible de supprimer cet utilisateur car il a des dossiers en instruction"
end
user.delete_and_keep_track_dossiers(current_administration)
logger.info("L'utilisateur #{user.id} est supprimé par #{current_administration.id}")
flash[:notice] = "L'utilisateur #{user.id} est supprimé"
redirect_to manager_users_path
end
end
end

View file

@ -49,7 +49,7 @@ module Users
def attestation
if dossier.attestation&.pdf&.attached?
redirect_to url_for(dossier.attestation.pdf)
redirect_to dossier.attestation.pdf.service_url
else
flash.notice = "L'attestation n'est plus disponible sur ce dossier."
redirect_to dossier_path(dossier)
@ -220,27 +220,30 @@ module Users
def new
erase_user_location!
if params[:brouillon]
procedure = Procedure.brouillon.find(params[:procedure_id])
else
procedure = Procedure.publiees.find(params[:procedure_id])
begin
if params[:brouillon]
procedure = Procedure.brouillon.find(params[:procedure_id])
else
procedure = Procedure.publiees.find(params[:procedure_id])
end
rescue ActiveRecord::RecordNotFound
flash.alert = t('errors.messages.procedure_not_found')
return redirect_to url_for dossiers_path
end
dossier = Dossier.create!(groupe_instructeur: procedure.defaut_groupe_instructeur, user: current_user, state: Dossier.states.fetch(:brouillon))
dossier = Dossier.new(
groupe_instructeur: procedure.defaut_groupe_instructeur,
user: current_user,
state: Dossier.states.fetch(:brouillon)
)
dossier.build_default_individual
dossier.save!
if dossier.procedure.for_individual
if current_user.france_connect_information.present?
dossier.update_with_france_connect(current_user.france_connect_information)
end
redirect_to identite_dossier_path(dossier)
else
redirect_to siret_dossier_path(id: dossier.id)
end
rescue ActiveRecord::RecordNotFound
flash.alert = t('errors.messages.procedure_not_found')
redirect_to url_for dossiers_path
end
def dossier_for_help

View file

@ -1012,6 +1012,11 @@ enum TypeDeChamp {
"""
civilite
"""
Communes
"""
communes
"""
Date
"""

View file

@ -7,7 +7,7 @@ module Types
field :content_type, String, null: false
def url
Rails.application.routes.url_helpers.url_for(object)
object.service_url
end
end
end

View file

@ -1,46 +1,158 @@
import $ from 'jquery';
import 'select2';
const optionTemplate = email =>
const { api_geo_url, api_adresse_url } = gon.autocomplete || {};
const language = {
errorLoading: function() {
return 'Les résultats ne peuvent pas être chargés.';
},
inputTooLong: function(args) {
var overChars = args.input.length - args.maximum;
return 'Supprimez ' + overChars + ' caractère' + (overChars > 1 ? 's' : '');
},
inputTooShort: function(args) {
var remainingChars = args.minimum - args.input.length;
return (
'Saisissez au moins ' +
remainingChars +
' caractère' +
(remainingChars > 1 ? 's' : '')
);
},
loadingMore: function() {
return 'Chargement de résultats supplémentaires…';
},
maximumSelected: function(args) {
return (
'Vous pouvez seulement sélectionner ' +
args.maximum +
' élément' +
(args.maximum > 1 ? 's' : '')
);
},
noResults: function() {
return 'Aucun résultat trouvé';
},
searching: function() {
return 'Recherche en cours…';
},
removeAllItems: function() {
return 'Supprimer tous les éléments';
}
};
const baseOptions = {
language,
width: '100%'
};
const baseAjaxOptions = {
delay: 250,
cache: true,
data({ term: nom }) {
return {
nom,
fields: 'nom,code'
};
},
processResults(data) {
return {
results: data.map(({ nom }) => ({ id: nom, text: nom }))
};
}
};
const regionsOptions = {
...baseOptions,
minimumInputLength: 2,
ajax: { url: `${api_geo_url}/regions`, ...baseAjaxOptions }
};
const communesOptions = {
...baseOptions,
minimumInputLength: 2,
ajax: { url: `${api_geo_url}/communes`, ...baseAjaxOptions }
};
const etranger99 = { id: '99 - Étranger', text: '99 - Étranger' };
const departementsOptions = {
...baseOptions,
minimumInputLength: 2,
ajax: {
...baseAjaxOptions,
url: `${api_geo_url}/departements`,
processResults(data) {
return {
results: data
.map(({ nom, code }) => ({
id: `${code} - ${nom}`,
text: `${code} - ${nom}`
}))
.concat([etranger99])
};
}
}
};
const adresseOptions = {
...baseOptions,
minimumInputLength: 2,
ajax: {
...baseAjaxOptions,
url: `${api_adresse_url}/search`,
data({ term: q }) {
return {
q,
limit: 5
};
},
processResults(data) {
return {
results: data.features.map(({ properties: { label }, geometry }) => ({
id: label,
text: label,
geometry
}))
};
}
}
};
const templateOption = ({ text }) =>
$(
'<span class="custom-select2-option"><span class="icon person"></span>' +
email.text +
'</span>'
`<span class="custom-select2-option"><span class="icon person"></span>${text}</span>`
);
addEventListener('ds:page:update', () => {
$('select.select2').select2({
language: 'fr',
width: '100%'
});
$('select.select2').select2(baseOptions);
$('select.select2.departements').select2(departementsOptions);
$('select.select2.regions').select2(regionsOptions);
$('select.select2.communes').select2(communesOptions);
$('select.select2.adresse').select2(adresseOptions);
$('.columns-form select.select2-limited').select2({
language: 'fr',
width: '300px',
placeholder: 'Sélectionnez des colonnes',
maximumSelectionLength: '5',
width: '300px'
maximumSelectionLength: '5'
});
$('.recipients-form select.select2-limited').select2({
language: 'fr',
language,
width: '300px',
placeholder: 'Sélectionnez des instructeurs',
maximumSelectionLength: '30',
width: '300px'
maximumSelectionLength: '30'
});
$('select.select2-limited.select-instructeurs').select2({
language: 'fr',
language,
dropdownParent: $('.instructeur-wrapper'),
placeholder: 'Saisir ladresse email de linstructeur',
tags: true,
tokenSeparators: [',', ' '],
templateResult: optionTemplate,
templateSelection: function(email) {
return $(
'<span class="custom-select2-option"><span class="icon person"></span>' +
email.text +
'</span>'
);
}
templateResult: templateOption,
templateSelection: templateOption
});
});

View file

@ -8,7 +8,6 @@ import '../shared/page-update-event';
import '../shared/activestorage/ujs';
import '../shared/rails-ujs-fix';
import '../shared/safari-11-file-xhr-workaround';
import '../shared/autocomplete';
import '../shared/remote-input';
import '../shared/franceconnect';

View file

@ -13,7 +13,6 @@ import '../shared/activestorage/ujs';
import '../shared/activestorage/attachment-checker';
import '../shared/rails-ujs-fix';
import '../shared/safari-11-file-xhr-workaround';
import '../shared/autocomplete';
import '../shared/remote-input';
import '../shared/franceconnect';
import '../shared/toggle-target';

View file

@ -1,51 +0,0 @@
import autocomplete from 'autocomplete.js';
import { getJSON, fire } from '@utils';
const sources = [
{
type: 'address',
url: '/address/suggestions'
}
];
const options = {
autoselect: true,
minLength: 1
};
function selector(type) {
return `[data-autocomplete=${type}]`;
}
function source(url) {
return {
source(query, callback) {
getJSON(url, { request: query }).then(callback);
},
templates: {
suggestion({ label, mine }) {
const mineClass = `path-mine-${mine ? 'true' : 'false'}`;
const openTag = `<div class="aa-suggestion ${mineClass}">`;
return autocomplete.escapeHighlightedString(label, openTag, '</div>');
}
},
debounce: 300
};
}
addEventListener('ds:page:update', function() {
for (let { type, url } of sources) {
for (let element of document.querySelectorAll(selector(type))) {
element.removeAttribute('data-autocomplete');
autocompleteInitializeElement(element, url);
}
}
});
function autocompleteInitializeElement(element, url) {
const select = autocomplete(element, options, [source(url)]);
select.on('autocomplete:selected', ({ target }, suggestion) => {
fire(target, 'autocomplete:select', suggestion);
select.autocomplete.setVal(suggestion.label);
});
}

View file

@ -1,5 +1,6 @@
/* globals FreeDraw L */
import { fire, getJSON, delegate } from '@utils';
import { fire, delegate } from '@utils';
import $ from 'jquery';
import polygonArea from './polygon_area';
@ -113,14 +114,6 @@ function drawParcellesAgricoles(map, { parcellesAgricoles }, editable = false) {
);
}
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');
@ -246,10 +239,12 @@ delegate('click', '.toolbar .new-area', event => {
}
});
delegate('autocomplete:select', '.toolbar [data-address]', event => {
$(document).on('select2:select', 'select[data-address]', event => {
const map = getCurrentMap(event.target);
const { geometry } = event.params.data;
if (map) {
geocodeAddress(map, event.detail.label);
if (map && geometry && geometry.type === 'Point') {
const [lon, lat] = geometry.coordinates;
map.setView(new L.LatLng(lat, lon), 14);
}
});

View file

@ -1,33 +0,0 @@
class ApiAdresse::Adapter
private
def initialize(address, limit, blank_return)
@address = address
@limit = limit
@blank_return = blank_return
end
def features
@features ||= get_features
end
def get_features
response = ApiAdresse::API.call(@address, @limit)
result = JSON.parse(response)
result['features']
rescue RestClient::Exception, JSON::ParserError, TypeError
@blank_return
end
def handle_result
if features.present?
process_features
else
@blank_return
end
end
def process_features
raise NoMethodError
end
end

View file

@ -1,17 +0,0 @@
class ApiAdresse::AddressAdapter < ApiAdresse::Adapter
def initialize(address)
super(address, 5, [])
end
def get_suggestions
handle_result
end
private
def process_features
features.map do |feature|
feature['properties']['label']
end
end
end

View file

@ -1,12 +0,0 @@
class ApiAdresse::API
def self.call(address, limit = 1)
search_url = [API_ADRESSE_URL, "search"].join("/")
RestClient::Request.execute(method: :get,
url: search_url,
timeout: 5,
headers: { params: { q: address, limit: limit } })
rescue RestClient::ServiceUnavailable
nil
end
end

View file

@ -1,15 +0,0 @@
class ApiAdresse::PointAdapter < ApiAdresse::Adapter
def initialize(address)
super(address, 1, nil)
end
def geocode
handle_result
end
private
def process_features
RGeo::GeoJSON.decode(features[0]['geometry'], json_parser: :json)
end
end

View file

@ -1,57 +0,0 @@
class ApiGeo::API
TIMEOUT = 15
CACHE_DURATION = 1.day
def self.regions
url = [API_GEO_URL, "regions"].join("/")
call(url, { fields: :nom })
end
def self.departements
url = [API_GEO_URL, "departements"].join("/")
call(url, { fields: :nom })
end
def self.pays
parse(File.open('app/lib/api_geo/pays.json').read)
end
def self.search_rpg(geojson)
url = [API_GEO_SANDBOX_URL, "rpg", "parcelles", "search"].join("/")
call(url, geojson, :post)
end
private
def self.parse(body)
JSON.parse(body, symbolize_names: true)
end
def self.call(url, body, method = :get)
# The cache engine is stored, because as of Typhoeus 1.3.1 the cache engine instance
# is included in the computed `cache_key`.
# (Which means that when the cache instance changes, the cache is invalidated.)
@typhoeus_cache ||= Typhoeus::Cache::SuccessfulRequestsRailsCache.new
response = Typhoeus::Request.new(
url,
method: method,
params: method == :get ? body : nil,
body: method == :post ? body : nil,
timeout: TIMEOUT,
accept_encoding: 'gzip',
headers: {
'Accept' => 'application/json',
'Accept-Encoding' => 'gzip, deflate'
}.merge(method == :post ? { 'Content-Type' => 'application/json' } : {}),
cache: @typhoeus_cache,
cache_ttl: CACHE_DURATION
).run
if response.success?
parse(response.body)
else
nil
end
end
end

View file

@ -1,27 +0,0 @@
class ApiGeo::RPGAdapter
def initialize(coordinates)
@coordinates = GeojsonService.to_json_polygon_for_rpg(coordinates)
end
def data_source
@data_source ||= ApiGeo::API.search_rpg(@coordinates)
end
def results
data_source[:features].map do |feature|
feature[:properties]
.stringify_keys
.transform_keys(&:underscore)
.symbolize_keys
.slice(
:culture,
:code_culture,
:surface,
:bio
).merge({
geometry: feature[:geometry],
geo_reference_id: feature[:properties][:id]
})
end
end
end

View file

@ -14,7 +14,7 @@ class Administrateur < ApplicationRecord
before_validation -> { sanitize_email(:email) }
scope :inactive, -> { joins(:user).where(users: { last_sign_in_at: nil }) }
scope :with_publiees_ou_closes, -> { joins(:procedures).where(procedures: { aasm_state: [:publiee, :archivee, :close, :depubliee] }) }
scope :with_publiees_ou_closes, -> { joins(:procedures).where(procedures: { aasm_state: [:publiee, :close, :depubliee] }) }
# validate :password_complexity, if: Proc.new { |a| Devise.password_length.include?(a.password.try(:size)) }
@ -68,6 +68,6 @@ class Administrateur < ApplicationRecord
end
def can_be_deleted?
dossiers.state_instruction_commencee.none? && procedures.none?
dossiers.state_instruction_commencee.none? && procedures.all? { |p| p.administrateurs.count > 1 }
end
end

View file

@ -0,0 +1,2 @@
class Champs::CommuneChamp < Champs::TextChamp
end

View file

@ -1,5 +1,2 @@
class Champs::DepartementChamp < Champs::TextChamp
def self.departements
ApiGeo::API.departements.map { |liste| "#{liste[:code]} - #{liste[:nom]}" }.push('99 - Étranger')
end
end

View file

@ -1,6 +1,8 @@
class Champs::PaysChamp < Champs::TextChamp
PAYS = JSON.parse(Rails.root.join('app', 'lib', 'api_geo', 'pays.json').read, symbolize_names: true)
def self.pays
ApiGeo::API.pays.pluck(:nom)
PAYS.pluck(:nom)
end
def self.disabled_options

View file

@ -48,7 +48,7 @@ class Champs::PieceJustificativeChamp < Champ
def for_api
if piece_justificative_file.attached? && (piece_justificative_file.virus_scanner.safe? || piece_justificative_file.virus_scanner.pending?)
Rails.application.routes.url_helpers.url_for(piece_justificative_file)
piece_justificative_file.service_url
end
end
end

View file

@ -1,5 +1,2 @@
class Champs::RegionChamp < Champs::TextChamp
def self.regions
ApiGeo::API.regions.sort_by { |e| e[:nom] }.pluck(:nom)
end
end

View file

@ -0,0 +1,12 @@
module BlobSignedIdConcern
extend ActiveSupport::Concern
included do
# We override signed_id to add `expires_in` option to generated hash.
# This is a measure to ensure that we never under any circumstance
# expose permanent attachment url
def signed_id
ActiveStorage.verifier.generate(id, purpose: :blob_id, expires_in: ActiveStorage::Service.url_expires_in)
end
end
end

View file

@ -2,7 +2,7 @@
# (rather than on blob creation).
# This will help to avoid cloberring metadata accidentally (as metadata
# are more stable on attachment creation than on blob creation).
module BlobVirusScanner
module BlobVirusScannerConcern
extend ActiveSupport::Concern
included do

View file

@ -21,7 +21,7 @@ class Dossier < ApplicationRecord
DRAFT_EXPIRATION = 1.month + 5.days
has_one :etablissement, dependent: :destroy
has_one :individual, dependent: :destroy
has_one :individual, validate: false, dependent: :destroy
has_one :attestation, dependent: :destroy
has_one_attached :justificatif_motivation
@ -207,9 +207,7 @@ class Dossier < ApplicationRecord
delegate :france_connect_information, to: :user
before_validation :update_state_dates, if: -> { state_changed? }
before_save :build_default_champs, if: Proc.new { groupe_instructeur_id_was.nil? }
before_save :build_default_individual, if: Proc.new { procedure.for_individual? }
before_save :update_search_terms
after_save :send_dossier_received
@ -217,6 +215,7 @@ class Dossier < ApplicationRecord
after_create :send_draft_notification_email
validates :user, presence: true
validates :individual, presence: true, if: -> { procedure.for_individual? }
def update_search_terms
self.search_terms = [
@ -239,8 +238,12 @@ class Dossier < ApplicationRecord
end
def build_default_individual
if Individual.where(dossier_id: self.id).count == 0
build_individual
if procedure.for_individual? && individual.blank?
self.individual = if france_connect_information.present?
Individual.from_france_connect(france_connect_information)
else
Individual.new
end
end
end
@ -579,10 +582,6 @@ class Dossier < ApplicationRecord
!PiecesJustificativesService.liste_pieces_justificatives(self).empty? && PiecesJustificativesService.pieces_justificatives_total_size(self) < Dossier::TAILLE_MAX_ZIP
end
def update_with_france_connect(fc_information)
self.individual = Individual.create_from_france_connect(fc_information)
end
def linked_dossiers
Dossier.where(id: champs.filter(&:dossier_link?).map(&:value).compact)
end

View file

@ -9,8 +9,8 @@ class Individual < ApplicationRecord
GENDER_MALE = 'M.'
GENDER_FEMALE = 'Mme'
def self.create_from_france_connect(fc_information)
create!(
def self.from_france_connect(fc_information)
new(
nom: fc_information.family_name,
prenom: fc_information.given_name,
gender: fc_information.gender == 'female' ? GENDER_FEMALE : GENDER_MALE

View file

@ -1,7 +1,7 @@
require Rails.root.join('lib', 'percentile')
class Procedure < ApplicationRecord
self.ignored_columns = ['logo', 'logo_secure_token', 'test_started_at']
self.ignored_columns = ['archived_at']
include ProcedureStatsConcern
@ -46,8 +46,8 @@ class Procedure < ApplicationRecord
default_scope { where(hidden_at: nil) }
scope :brouillons, -> { where(aasm_state: :brouillon) }
scope :publiees, -> { where(aasm_state: :publiee) }
scope :closes, -> { where(aasm_state: [:archivee, :close, :depubliee]) }
scope :publiees_ou_closes, -> { where(aasm_state: [:publiee, :close, :archivee, :depubliee]) }
scope :closes, -> { where(aasm_state: [:close, :depubliee]) }
scope :publiees_ou_closes, -> { where(aasm_state: [:publiee, :close, :depubliee]) }
scope :by_libelle, -> { order(libelle: :asc) }
scope :created_during, -> (range) { where(created_at: range) }
scope :cloned_from_library, -> { where(cloned_from_library: true) }
@ -233,9 +233,8 @@ class Procedure < ApplicationRecord
end
def validate_for_publication
old_attributes = self.slice(:aasm_state, :archived_at, :closed_at)
old_attributes = self.slice(:aasm_state, :closed_at)
self.aasm_state = :publiee
self.archived_at = nil
self.closed_at = nil
is_valid = validate
@ -279,14 +278,6 @@ class Procedure < ApplicationRecord
update(csv_export_queued: false, xlsx_export_queued: false, ods_export_queued: false)
end
def closed_at
read_attribute(:closed_at).presence || archived_at
end
def close?
aasm_state == 'close' || aasm_state == 'archivee'
end
def locked?
publiee? || close? || depubliee?
end
@ -383,7 +374,6 @@ class Procedure < ApplicationRecord
}, &method(:clone_attachments))
procedure.path = SecureRandom.uuid
procedure.aasm_state = :brouillon
procedure.archived_at = nil
procedure.closed_at = nil
procedure.unpublished_at = nil
procedure.published_at = nil
@ -622,7 +612,7 @@ class Procedure < ApplicationRecord
end
def before_publish
update!(archived_at: nil, closed_at: nil, unpublished_at: nil)
update!(closed_at: nil, unpublished_at: nil)
end
def after_publish
@ -631,7 +621,7 @@ class Procedure < ApplicationRecord
def after_close
now = Time.zone.now
update!(archived_at: now, closed_at: now)
update!(closed_at: now)
purge_export_files
end

View file

@ -19,6 +19,7 @@ class TypeDeChamp < ApplicationRecord
pays: 'pays',
regions: 'regions',
departements: 'departements',
communes: 'communes',
engagement: 'engagement',
header_section: 'header_section',
explication: 'explication',

View file

@ -0,0 +1,2 @@
class TypesDeChamp::CommuneTypeDeChamp < TypesDeChamp::TypeDeChampBase
end

View file

@ -96,6 +96,23 @@ class User < ApplicationRecord
last_sign_in_at.present?
end
def can_be_deleted?
dossiers.state_instruction_commencee.empty?
end
def delete_and_keep_track_dossiers(administration)
if !can_be_deleted?
raise "Cannot delete this user because instruction has started for some dossiers"
end
if can_be_deleted?
dossiers.each do |dossier|
dossier.delete_and_keep_track(administration)
end
destroy
end
end
private
def link_invites!

View file

@ -32,8 +32,7 @@ class ProcedureSerializer < ActiveModel::Serializer
end
def state
state = object.aasm_state
state == 'close' ? 'archivee' : state
object.aasm_state
end
def geographic_information

View file

@ -36,7 +36,7 @@ as well as a link to its edit page.
<% if page.resource.invitation_expired? %>
<%= link_to "renvoyer l'invitation", reinvite_manager_administrateur_path(page.resource), method: :post, class: "button" %>
<% end %>
<%= button_to "supprimer", delete_manager_administrateur_path(page.resource), method: :delete, disabled: !page.resource.can_be_deleted?, class: "button", data: { confirm: "Confirmez-vous la suppression de l'administrateur ?" }, title: page.resource.can_be_deleted? ? "Supprimer" : "Cet administrateur a des dossiers ou des procédures et ne peut être supprimé" %>
<%= button_to "supprimer", delete_manager_administrateur_path(page.resource), method: :delete, disabled: !page.resource.can_be_deleted?, class: "button", data: { confirm: "Confirmez-vous la suppression de l'administrateur ?" }, title: page.resource.can_be_deleted? ? "Supprimer" : "Cet administrateur a des dossiers ou des procédures dont il est le seul admin et ne peut être supprimé" %>
</div>
</header>

View file

@ -25,13 +25,9 @@ as well as a link to its edit page.
</h1>
<div>
<%= link_to(
t("administrate.actions.edit_resource", name: page.page_title),
[:edit, namespace, page.resource],
class: "button",
) if valid_action?(:edit) && show_action?(:edit, page.resource) %>
</div>
<%= button_to "supprimer", delete_manager_user_path(page.resource), method: :delete, disabled: !page.resource.can_be_deleted?, class: "button", data: { confirm: "Confirmez-vous la suppression de l'utilisateur ?" }, title: page.resource.can_be_deleted? ? "Supprimer" : "Cet utilisateur a des dossiers dont l'instruction a commencé et ne peut être supprimé" %>
</div>
<div>
<% if !user.confirmed? %>
<%= link_to('Renvoyer lemail de confirmation', [:resend_confirmation_instructions, namespace, page.resource], method: :post, class: 'button') %>

View file

@ -1,4 +1,4 @@
= form.text_field :value,
data: { address: true, autocomplete: 'address' },
placeholder: champ.libelle,
required: champ.mandatory?
= form.select :value, [champ.value].compact,
{ include_blank: true },
required: champ.mandatory?,
class: 'select2 adresse'

View file

@ -1,6 +1,6 @@
.toolbar
%button.button.primary.new-area Ajouter une zone
%input.address{ data: { address: true, autocomplete: 'address' }, placeholder: 'Saisissez une adresse ou positionner la carte' }
%select.select2.adresse{ data: { address: true }, placeholder: 'Saisissez une adresse ou positionner la carte' }
.carte.edit{ data: { geo: geo_data(champ) }, class: "carte-#{form.index}" }

View file

@ -0,0 +1,4 @@
= form.select :value, [champ.value].compact,
{ include_blank: true },
required: champ.mandatory?,
class: 'select2 communes'

View file

@ -1,4 +1,4 @@
= form.select :value,
Champs::DepartementChamp.departements,
include_blank: true,
required: champ.mandatory?
= form.select :value, [champ.value].compact,
{ include_blank: true },
required: champ.mandatory?,
class: 'select2 departements'

View file

@ -1,5 +1,5 @@
= form.select :value,
Champs::PaysChamp.pays,
disabled: Champs::PaysChamp.disabled_options,
include_blank: true,
required: champ.mandatory?
{ disabled: Champs::PaysChamp.disabled_options, include_blank: true },
required: champ.mandatory?,
class: 'select2 pays'

View file

@ -1,4 +1,4 @@
= form.select :value,
Champs::RegionChamp.regions,
include_blank: true,
required: champ.mandatory?
= form.select :value, [champ.value].compact,
{ include_blank: true },
required: champ.mandatory?,
class: 'select2 regions'

View file

@ -105,3 +105,7 @@ LOGRAGE_ENABLED="disabled"
# Service externe d'horodatage des changements de statut des dossiers (effectué quotidiennement)
UNIVERSIGN_API_URL=""
UNIVERSIGN_USERPWD=""
# API Geo / Adresse
API_ADRESSE_URL="https://api-adresse.data.gouv.fr"
API_GEO_URL="https://geo.api.gouv.fr"

View file

@ -6,7 +6,10 @@ ActiveStorage::Service.url_expires_in = 1.hour
# Rails 6 adds support for `.on_load(:active_storage_attachment)`, which is
# cleaner (as it allows to enqueue the virus scan on attachment creation, rather
# than on blob creation).
ActiveSupport.on_load(:active_storage_blob) { include BlobVirusScanner }
ActiveSupport.on_load(:active_storage_blob) do
include BlobSignedIdConcern
include BlobVirusScannerConcern
end
# When an OpenStack service is initialized it makes a request to fetch
# `publicURL` to use for all operations. We intercept the method that reads

View file

@ -5,6 +5,7 @@ Rails.application.config.assets.version = '1.0'
# Add additional assets to the asset load path
Rails.application.config.assets.paths << Rails.root.join('node_modules', 'trix', 'dist')
Rails.application.config.assets.paths << Rails.root.join('node_modules', 'select2', 'dist', 'css')
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.

View file

@ -13,7 +13,7 @@ Rails.application.config.content_security_policy do |policy|
# c'est trop compliqué pour être rectifié immédiatement (et sans valeur ajoutée:
# c'est hardcodé dans les vues, donc pas injectable).
policy.style_src :self, "*.crisp.chat", "crisp.chat", :unsafe_inline
policy.connect_src :self, "wss://*.crisp.chat", "*.crisp.chat", "*.demarches-simplifiees.fr", "in-automate.sendinblue.com", "app.franceconnect.gouv.fr", "sentry.io"
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"
# 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
policy.default_src :self, :data, :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"

View file

@ -1,9 +1,6 @@
# API URLs
API_ADRESSE_URL = ENV.fetch("API_ADRESSE_URL", "https://api-adresse.data.gouv.fr")
API_CARTO_URL = ENV.fetch("API_CARTO_URL", "https://sandbox.geo.api.gouv.fr/apicarto")
API_ENTREPRISE_URL = ENV.fetch("API_ENTREPRISE_URL", "https://entreprise.api.gouv.fr/v2")
API_GEO_URL = ENV.fetch("API_GEO_URL", "https://geo.api.gouv.fr")
API_GEO_SANDBOX_URL = ENV.fetch("API_GEO_SANDBOX_URL", "https://sandbox.geo.api.gouv.fr")
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")
SENDINBLUE_API_URL = ENV.fetch("SENDINBLUE_API_URL", "https://in-automate.sendinblue.com/api/v2")

View file

@ -24,6 +24,7 @@ fr:
pays: 'Pays'
regions: 'Régions'
departements: 'Départements'
communes: 'Communes'
engagement: 'Engagement'
header_section: 'Titre de section'
explication: 'Explication'

View file

@ -25,6 +25,7 @@ Rails.application.routes.draw do
end
resources :users, only: [:index, :show] do
delete 'delete', on: :member
post 'resend_confirmation_instructions', on: :member
put 'enable_feature', on: :member
end
@ -208,13 +209,6 @@ Rails.application.routes.draw do
resources :instructeurs, only: [:index, :create, :destroy]
end
#
# Addresses
#
get 'address/suggestions' => 'address#suggestions'
get 'address/geocode' => 'address#geocode'
resources :invites, only: [:show] do
collection do
post 'dossier/:dossier_id', to: 'invites#create', as: :dossier
@ -393,6 +387,14 @@ Rails.application.routes.draw do
end
end
if Rails.env.test?
scope 'test/api_geo' do
get 'regions' => 'api_geo_test#regions'
get 'communes' => 'api_geo_test#communes'
get 'departements' => 'api_geo_test#departements'
end
end
#
# Legacy routes
#

View file

@ -15,7 +15,7 @@ defaults: &defaults
basic_auth:
username: <%= ENV['BASIC_AUTH_USERNAME'] %>
password: <%= ENV['BASIC_AUTH_PASSWORD'] %>
france_connect_particulier:
france_connect_particulier: &france_connect_particulier
identifier: <%= ENV['FC_PARTICULIER_ID'] %>
secret: <%= ENV['FC_PARTICULIER_SECRET'] %>
redirect_uri: https://<%= ENV['APP_HOST'] %>/france_connect/particulier/callback
@ -68,11 +68,18 @@ defaults: &defaults
client_key: <%= ENV['CRISP_CLIENT_KEY'] %>
universign:
userpwd: <%= ENV['UNIVERSIGN_USERPWD'] %>
autocomplete:
api_geo_url: <%= ENV['API_GEO_URL'] %>
api_adresse_url: <%= ENV['API_ADRESSE_URL'] %>
development:
<<: *defaults
france_connect_particulier:
<<: *france_connect_particulier
redirect_uri: http://<%= ENV['APP_HOST'] %>/france_connect/particulier/callback
test:
<<: *defaults
@ -94,6 +101,8 @@ test:
logout_endpoint: https://bidon.com/endpoint
universign:
userpwd: 'fake:fake'
autocomplete:
api_geo_url: /test/api_geo
# Do not keep production secrets in the repository,
# instead read values from the environment.

View file

@ -0,0 +1,6 @@
class AddProceduresPathClosedAtHiddenAtIndex < ActiveRecord::Migration[5.2]
def change
add_index :procedures, [:path, :closed_at, :hidden_at], unique: true
remove_index :procedures, [:path, :archived_at, :hidden_at]
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2019_12_11_113341) do
ActiveRecord::Schema.define(version: 2019_12_18_103727) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -505,7 +505,7 @@ ActiveRecord::Schema.define(version: 2019_12_11_113341) do
t.index ["declarative_with_state"], name: "index_procedures_on_declarative_with_state"
t.index ["hidden_at"], name: "index_procedures_on_hidden_at"
t.index ["parent_procedure_id"], name: "index_procedures_on_parent_procedure_id"
t.index ["path", "archived_at", "hidden_at"], name: "index_procedures_on_path_and_archived_at_and_hidden_at", unique: true
t.index ["path", "closed_at", "hidden_at"], name: "index_procedures_on_path_and_closed_at_and_hidden_at", unique: true
t.index ["service_id"], name: "index_procedures_on_service_id"
end

View file

@ -15,13 +15,7 @@ namespace :support do
user = User.find_by!(email: user_email)
administration = Administration.find_by!(email: administration_email)
if user.dossiers.state_instruction_commencee.any?
fail "Cannot delete this user because instruction has started for some dossiers"
end
user.dossiers.each do |dossier|
dossier.delete_and_keep_track(administration)
end
user.delete_and_keep_track_dossiers(administration)
user.destroy
end

View file

@ -10,7 +10,6 @@
"@rails/webpacker": "4.0.7",
"@sentry/browser": "^5.7.1",
"@turf/area": "^6.0.1",
"autocomplete.js": "^0.37.0",
"babel-plugin-macros": "^2.6.1",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"chartkick": "^3.1.3",

View file

@ -1,46 +0,0 @@
require 'spec_helper'
describe AddressController, type: :controller do
describe '#GET suggestions' do
subject { get :suggestions, params: { request: request } }
before do
subject
end
context 'when request return result', vcr: { cassette_name: 'api_adresse_search_paris_2' } do
let (:request) { 'Paris' }
it { expect(response.status).to eq 200 }
it { expect(response.body).to eq '[{"label":"Paris"},{"label":"Paris 63120 Courpière"},{"label":"PARIS (Vaillac) 46240 Cœur de Causse"},{"label":"Paris 40500 Saint-Sever"},{"label":"Paris Buton 37140 Bourgueil"}]' }
end
context 'when request return nothing', vcr: { cassette_name: 'api_adresse_search_nothing_2' } do
let (:request) { 'je recherche pas grand chose' }
it { expect(response.status).to eq 200 }
it { expect(response.body).to eq "[]" }
end
end
describe '#GET geocode' do
let(:dossier_id) { "1" }
subject { get :geocode, params: { request: request, dossier_id: dossier_id } }
before do
subject
end
context 'when request return result', vcr: { cassette_name: 'api_adresse_search_paris' } do
let(:request) { 'Paris' }
it { expect(response.body).to eq ({ lon: '2.3469', lat: '48.8589', zoom: '14', dossier_id: dossier_id }).to_json }
end
context 'when request return nothing', vcr: { cassette_name: 'api_adresse_search_nothing' } do
let(:request) { 'je recherche pas grand chose' }
it { expect(response.body).to eq ({ lon: nil, lat: nil, zoom: '14', dossier_id: dossier_id }).to_json }
end
end
end

View file

@ -15,7 +15,7 @@ describe API::V2::GraphqlController do
end
let(:dossier1) { create(:dossier, :en_construction, :for_individual, procedure: procedure, en_construction_at: 1.day.ago) }
let(:dossier2) { create(:dossier, :en_construction, :for_individual, procedure: procedure, en_construction_at: 3.days.ago) }
let!(:dossier_brouillon) { create(:dossier, :for_individual, procedure: procedure) }
let(:dossier_brouillon) { create(:dossier, :for_individual, procedure: procedure) }
let(:dossiers) { [dossier2, dossier1, dossier] }
let(:instructeur) { create(:instructeur, followed_dossiers: dossiers) }
@ -169,71 +169,70 @@ describe API::V2::GraphqlController do
end
context "dossier" do
let(:query) do
"{
dossier(number: #{dossier.id}) {
id
number
state
dateDerniereModification
datePassageEnConstruction
datePassageEnInstruction
dateTraitement
motivation
motivationAttachment {
url
}
usager {
context "with individual" do
let(:query) do
"{
dossier(number: #{dossier.id}) {
id
email
}
demandeur {
id
... on PersonnePhysique {
nom
prenom
civilite
dateDeNaissance
}
}
instructeurs {
id
email
}
messages {
email
body
attachment {
filename
checksum
byteSize
contentType
number
state
dateDerniereModification
datePassageEnConstruction
datePassageEnInstruction
dateTraitement
motivation
motivationAttachment {
url
}
}
avis {
expert {
usager {
id
email
}
question
reponse
dateQuestion
dateReponse
attachment {
url
filename
demandeur {
id
... on PersonnePhysique {
nom
prenom
civilite
dateDeNaissance
}
}
instructeurs {
id
email
}
messages {
email
body
attachment {
filename
checksum
byteSize
contentType
}
}
avis {
expert {
email
}
question
reponse
dateQuestion
dateReponse
attachment {
url
filename
}
}
champs {
id
label
stringValue
}
}
champs {
id
label
stringValue
}
}
}"
end
}"
end
context "with individual" do
it "should be returned" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossier: {
@ -270,8 +269,7 @@ describe API::V2::GraphqlController do
filename: commentaire.piece_jointe.filename.to_s,
contentType: commentaire.piece_jointe.content_type,
checksum: commentaire.piece_jointe.checksum,
byteSize: commentaire.piece_jointe.byte_size,
url: Rails.application.routes.url_helpers.url_for(commentaire.piece_jointe)
byteSize: commentaire.piece_jointe.byte_size
},
email: commentaire.email
}

View file

@ -17,9 +17,9 @@ describe Instructeurs::DossiersController, type: :controller do
context 'when a dossier has an attestation' do
let(:dossier) { create(:dossier, :accepte, attestation: create(:attestation, :with_pdf), procedure: procedure) }
it 'redirects to attestation pdf' do
it 'redirects to a service tmp_url' do
get :attestation, params: { procedure_id: procedure.id, dossier_id: dossier.id }
expect(response).to redirect_to(dossier.attestation.pdf_url.gsub('http://localhost:3000', ''))
expect(response.location).to match '/rails/active_storage/disk/'
end
end
end

View file

@ -0,0 +1,17 @@
describe Manager::UsersController, type: :controller do
let(:administration) { create(:administration) }
describe '#delete' do
let!(:user) { create(:user) }
before { sign_in administration }
subject { delete :delete, params: { id: user.id } }
it 'deletes the user' do
subject
expect(User.find_by(id: user.id)).to be_nil
end
end
end

View file

@ -14,7 +14,7 @@ describe NewAdministrateur::MailTemplatesController, type: :controller do
it { expect(response).to have_http_status(:ok) }
it 'displays the procedure logo' do
expect(response.body).to have_css("img[src*='#{procedure.logo_url}']")
expect(response.body).to have_css("img[src*='/rails/active_storage/blobs/']")
end
it 'displays the action buttons' do

View file

@ -145,7 +145,7 @@ describe Users::DossiersController, type: :controller do
it 'redirects to attestation pdf' do
get :attestation, params: { id: dossier.id }
expect(response).to redirect_to(dossier.attestation.pdf_url.gsub('http://localhost:3000', ''))
expect(response.location).to match '/rails/active_storage/disk/'
end
end
end

View file

@ -124,6 +124,11 @@ FactoryBot.define do
value { '971 - Guadeloupe' }
end
factory :champ_communes, class: 'Champs::CommuneChamp' do
type_de_champ { create(:type_de_champ_communes) }
value { 'Paris' }
end
factory :champ_engagement, class: 'Champs::EngagementChamp' do
type_de_champ { create(:type_de_champ_engagement) }
value { 'true' }

View file

@ -15,9 +15,12 @@ FactoryBot.define do
procedure = create(:procedure, :published, :with_type_de_champ, :with_type_de_champ_private)
end
# Assign the procedure to the dossier through the groupe_instructeur
if dossier.groupe_instructeur.nil?
dossier.groupe_instructeur = procedure.defaut_groupe_instructeur
end
dossier.build_default_individual
end
trait :with_entreprise do

View file

@ -71,6 +71,9 @@ FactoryBot.define do
factory :type_de_champ_departements do
type_champ { TypeDeChamp.type_champs.fetch(:departements) }
end
factory :type_de_champ_communes do
type_champ { TypeDeChamp.type_champs.fetch(:communes) }
end
factory :type_de_champ_engagement do
type_champ { TypeDeChamp.type_champs.fetch(:engagement) }
end

View file

@ -10,10 +10,7 @@ feature 'The user' do
# TODO: check
# the order
# there are no extraneous input
scenario 'fill a dossier', js: true do
allow(Champs::RegionChamp).to receive(:regions).and_return(['region1', 'region2']).at_least(:once)
allow(Champs::DepartementChamp).to receive(:departements).and_return(['dep1', 'dep2']).at_least(:once)
scenario 'fill a dossier', js: true, vcr: { cassette_name: 'api_geo_departements_regions_et_communes' } do
log_in(user, procedure)
fill_individual
@ -33,8 +30,16 @@ feature 'The user' do
select('val1', from: form_id_for('multiple_drop_down_list'))
select('val3', from: form_id_for('multiple_drop_down_list'))
select('AUSTRALIE', from: 'pays')
select('region2', from: 'regions')
select('dep2', from: 'departements')
select_champ_geo('regions', 'Ma', 'Martinique')
select('Martinique', from: 'regions')
select_champ_geo('departements', 'Ai', '02 - Aisne')
select('02 - Aisne', from: 'departements')
select_champ_geo('communes', 'Am', 'Ambléon')
select('Ambléon', from: 'communes')
check('engagement')
fill_in('dossier_link', with: '123')
find('.editable-champ-piece_justificative input[type=file]').attach_file(Rails.root + 'spec/fixtures/files/file.pdf')
@ -57,8 +62,9 @@ feature 'The user' do
expect(champ_value_for('simple_drop_down_list')).to eq('val2')
expect(JSON.parse(champ_value_for('multiple_drop_down_list'))).to match(['val1', 'val3'])
expect(champ_value_for('pays')).to eq('AUSTRALIE')
expect(champ_value_for('regions')).to eq('region2')
expect(champ_value_for('departements')).to eq('dep2')
expect(champ_value_for('regions')).to eq('Martinique')
expect(champ_value_for('departements')).to eq('02 - Aisne')
expect(champ_value_for('communes')).to eq('Ambléon')
expect(champ_value_for('engagement')).to eq('on')
expect(champ_value_for('dossier_link')).to eq('123')
expect(champ_value_for('piece_justificative')).to be_nil # antivirus hasn't approved the file yet
@ -78,8 +84,9 @@ feature 'The user' do
expect(page).to have_selected_value('simple_drop_down_list', selected: 'val2')
expect(page).to have_selected_value('multiple_drop_down_list', selected: ['val1', 'val3'])
expect(page).to have_selected_value('pays', selected: 'AUSTRALIE')
expect(page).to have_selected_value('regions', selected: 'region2')
expect(page).to have_selected_value('departement', selected: 'dep2')
expect(page).to have_selected_value('regions', selected: 'Martinique')
expect(page).to have_selected_value('departements', selected: '02 - Aisne')
expect(page).to have_selected_value('communes', selected: 'Ambléon')
expect(page).to have_checked_field('engagement')
expect(page).to have_field('dossier_link', with: '123')
expect(page).to have_text('file.pdf')
@ -270,4 +277,12 @@ feature 'The user' do
expect(page).to have_selected_value("#{field}_4i", selected: date.strftime('%H'))
expect(page).to have_selected_value("#{field}_5i", selected: date.strftime('%M'))
end
def select_champ_geo(champ, fill_with, value)
find(".editable-champ-#{champ} .select2-container").click
id = find('.select2-container--open [role=listbox]')[:id]
find("[aria-controls=#{id}]").fill_in with: fill_with
expect(page).to have_content(value)
find('li', text: value).click
end
end

View file

@ -77,7 +77,7 @@ feature 'Creating a new dossier:' do
.to_return(status: 404, body: '')
end
scenario 'the user can enter the SIRET of its etablissement and create a new draft', vcr: { cassette_name: 'api_adresse_search_paris_3' } do
scenario 'the user can enter the SIRET of its etablissement and create a new draft' do
visit commencer_path(path: procedure.path)
click_on 'Commencer la démarche'

View file

@ -1,47 +0,0 @@
---
http_interactions:
- request:
method: get
uri: https://api-adresse.data.gouv.fr/search?limit=1&q=50%20av%20des%20champs%20elysees
body:
encoding: US-ASCII
string: ''
headers:
Accept:
- "*/*"
Accept-Encoding:
- gzip, deflate
User-Agent:
- rest-client/2.0.0 (darwin15.6.0 x86_64) ruby/2.3.1p112
response:
status:
code: 200
message: OK
headers:
Server:
- nginx/1.11.3
Date:
- Fri, 16 Dec 2016 16:22:23 GMT
Content-Type:
- application/json; charset=utf-8
Content-Length:
- '628'
Connection:
- keep-alive
Access-Control-Allow-Origin:
- "*"
Access-Control-Allow-Headers:
- X-Requested-With
body:
encoding: UTF-8
string: '{"limit": 1, "attribution": "BAN", "version": "draft", "licence": "ODbL
1.0", "query": "50 av des champs elysees", "type": "FeatureCollection", "features":
[{"geometry": {"type": "Point", "coordinates": [2.306888, 48.870374]}, "properties":
{"citycode": "75108", "postcode": "75008", "name": "50 Avenue des Champs \u00c9lys\u00e9es",
"id": "ADRNIVX_0000000270748251", "type": "housenumber", "context": "75, \u00cele-de-France",
"score": 0.7561038961038961, "label": "50 Avenue des Champs \u00c9lys\u00e9es
75008 Paris", "city": "Paris", "housenumber": "50", "street": "Avenue des
Champs \u00c9lys\u00e9es"}, "type": "Feature"}]}'
http_version:
recorded_at: Fri, 16 Dec 2016 16:22:23 GMT
recorded_with: VCR 3.0.3

View file

@ -1,42 +0,0 @@
---
http_interactions:
- request:
method: get
uri: https://api-adresse.data.gouv.fr/search?limit=1&q=je%20recherche%20pas%20grand%20chose
body:
encoding: US-ASCII
string: ''
headers:
Accept:
- "*/*"
Accept-Encoding:
- gzip, deflate
User-Agent:
- rest-client/2.0.0 (darwin15.6.0 x86_64) ruby/2.3.1p112
response:
status:
code: 200
message: OK
headers:
Server:
- nginx/1.11.3
Date:
- Fri, 16 Dec 2016 14:17:40 GMT
Content-Type:
- application/json; charset=utf-8
Content-Length:
- '163'
Connection:
- keep-alive
Access-Control-Allow-Origin:
- "*"
Access-Control-Allow-Headers:
- X-Requested-With
body:
encoding: UTF-8
string: '{"limit": 1, "attribution": "BAN", "version": "draft", "licence": "ODbL
1.0", "query": "je recherche pas grand chose", "type": "FeatureCollection",
"features": []}'
http_version:
recorded_at: Fri, 16 Dec 2016 14:17:40 GMT
recorded_with: VCR 3.0.3

View file

@ -1,42 +0,0 @@
---
http_interactions:
- request:
method: get
uri: https://api-adresse.data.gouv.fr/search?limit=5&q=je%20recherche%20pas%20grand%20chose
body:
encoding: US-ASCII
string: ''
headers:
Accept:
- "*/*"
Accept-Encoding:
- gzip, deflate
User-Agent:
- rest-client/2.0.0 (darwin15.6.0 x86_64) ruby/2.3.1p112
response:
status:
code: 200
message: OK
headers:
Server:
- nginx/1.11.3
Date:
- Fri, 16 Dec 2016 16:45:53 GMT
Content-Type:
- application/json; charset=utf-8
Content-Length:
- '163'
Connection:
- keep-alive
Access-Control-Allow-Origin:
- "*"
Access-Control-Allow-Headers:
- X-Requested-With
body:
encoding: UTF-8
string: '{"limit": 5, "attribution": "BAN", "version": "draft", "licence": "ODbL
1.0", "query": "je recherche pas grand chose", "type": "FeatureCollection",
"features": []}'
http_version:
recorded_at: Fri, 16 Dec 2016 16:45:53 GMT
recorded_with: VCR 3.0.3

View file

@ -1,45 +0,0 @@
---
http_interactions:
- request:
method: get
uri: https://api-adresse.data.gouv.fr/search?limit=1&q=Paris
body:
encoding: US-ASCII
string: ''
headers:
Accept:
- "*/*"
Accept-Encoding:
- gzip, deflate
User-Agent:
- rest-client/2.0.0 (darwin15.6.0 x86_64) ruby/2.3.1p112
response:
status:
code: 200
message: OK
headers:
Server:
- nginx/1.11.3
Date:
- Fri, 16 Dec 2016 14:16:43 GMT
Content-Type:
- application/json; charset=utf-8
Content-Length:
- '457'
Connection:
- keep-alive
Access-Control-Allow-Origin:
- "*"
Access-Control-Allow-Headers:
- X-Requested-With
body:
encoding: UTF-8
string: '{"limit": 1, "attribution": "BAN", "version": "draft", "licence": "ODbL
1.0", "query": "Paris", "type": "FeatureCollection", "features": [{"geometry":
{"type": "Point", "coordinates": [2.3469, 48.8589]}, "properties": {"adm_weight":
"6", "citycode": "75056", "name": "Paris", "city": "Paris", "postcode": "75000",
"context": "75, \u00cele-de-France", "score": 1.0, "label": "Paris", "id":
"75056", "type": "city", "population": "2244"}, "type": "Feature"}]}'
http_version:
recorded_at: Fri, 16 Dec 2016 14:16:43 GMT
recorded_with: VCR 3.0.3

View file

@ -1,63 +0,0 @@
---
http_interactions:
- request:
method: get
uri: https://api-adresse.data.gouv.fr/search?limit=5&q=Paris
body:
encoding: US-ASCII
string: ''
headers:
Accept:
- "*/*"
Accept-Encoding:
- gzip, deflate
User-Agent:
- rest-client/2.0.0 (darwin15.6.0 x86_64) ruby/2.3.1p112
response:
status:
code: 200
message: OK
headers:
Server:
- nginx/1.11.3
Date:
- Fri, 16 Dec 2016 16:43:34 GMT
Content-Type:
- application/json; charset=utf-8
Content-Length:
- '1887'
Connection:
- keep-alive
Access-Control-Allow-Origin:
- "*"
Access-Control-Allow-Headers:
- X-Requested-With
body:
encoding: UTF-8
string: '{"limit": 5, "attribution": "BAN", "version": "draft", "licence": "ODbL
1.0", "query": "Paris", "type": "FeatureCollection", "features": [{"geometry":
{"type": "Point", "coordinates": [2.3469, 48.8589]}, "properties": {"adm_weight":
"6", "citycode": "75056", "name": "Paris", "city": "Paris", "postcode": "75000",
"context": "75, \u00cele-de-France", "score": 1.0, "label": "Paris", "id":
"75056", "type": "city", "population": "2244"}, "type": "Feature"}, {"geometry":
{"type": "Point", "coordinates": [3.564293, 45.766413]}, "properties": {"citycode":
"63125", "postcode": "63120", "name": "Paris", "city": "Courpi\u00e8re", "context":
"63, Puy-de-D\u00f4me, Auvergne", "score": 0.8255363636363636, "label": "Paris
63120 Courpi\u00e8re", "id": "63125_B221_03549b", "type": "locality"}, "type":
"Feature"}, {"geometry": {"type": "Point", "coordinates": [1.550208, 44.673592]},
"properties": {"citycode": "46138", "postcode": "46240", "name": "PARIS (Vaillac)",
"city": "C\u0153ur de Causse", "context": "46, Lot, Midi-Pyr\u00e9n\u00e9es",
"score": 0.824090909090909, "label": "PARIS (Vaillac) 46240 C\u0153ur de Causse",
"id": "46138_XXXX_6ee4ec", "type": "street"}, "type": "Feature"}, {"geometry":
{"type": "Point", "coordinates": [-0.526884, 43.762253]}, "properties": {"citycode":
"40282", "postcode": "40500", "name": "Paris", "city": "Saint-Sever", "context":
"40, Landes, Aquitaine", "score": 0.8236181818181818, "label": "Paris 40500
Saint-Sever", "id": "40282_B237_2364e3", "type": "locality"}, "type": "Feature"},
{"geometry": {"type": "Point", "coordinates": [0.157613, 47.336685]}, "properties":
{"citycode": "37031", "postcode": "37140", "name": "Paris Buton", "city":
"Bourgueil", "context": "37, Indre-et-Loire, Centre Val-de-Loire", "score":
0.8235454545454545, "label": "Paris Buton 37140 Bourgueil", "id": "37031_X027_0a5e7a",
"type": "locality"}, "type": "Feature"}]}'
http_version:
recorded_at: Fri, 16 Dec 2016 16:43:34 GMT
recorded_with: VCR 3.0.3

View file

@ -1,93 +0,0 @@
---
http_interactions:
- request:
method: get
uri: https://api-adresse.data.gouv.fr/search?limit=1&q=50%20AV%20DES%20CHAMPS%20ELYSEES%20complement_adresse%2075008%20PARIS%208
body:
encoding: US-ASCII
string: ''
headers:
Accept:
- "*/*"
Accept-Encoding:
- gzip, deflate
User-Agent:
- rest-client/2.0.0 (darwin15.6.0 x86_64) ruby/2.3.1p112
response:
status:
code: 200
message: OK
headers:
Server:
- nginx/1.11.3
Date:
- Tue, 03 Jan 2017 13:00:54 GMT
Content-Type:
- application/json; charset=utf-8
Content-Length:
- '660'
Connection:
- keep-alive
Access-Control-Allow-Origin:
- "*"
Access-Control-Allow-Headers:
- X-Requested-With
body:
encoding: UTF-8
string: '{"limit": 1, "attribution": "BAN", "version": "draft", "licence": "ODbL
1.0", "query": "50 AV DES CHAMPS ELYSEES complement_adresse 75008 PARIS 8",
"type": "FeatureCollection", "features": [{"geometry": {"type": "Point", "coordinates":
[2.306888, 48.870374]}, "properties": {"citycode": "75108", "postcode": "75008",
"name": "50 Avenue des Champs \u00c9lys\u00e9es", "id": "ADRNIVX_0000000270748251",
"type": "housenumber", "context": "75, \u00cele-de-France", "score": 0.596517719568567,
"label": "50 Avenue des Champs \u00c9lys\u00e9es 75008 Paris", "city": "Paris",
"housenumber": "50", "street": "Avenue des Champs \u00c9lys\u00e9es"}, "type":
"Feature"}]}'
http_version:
recorded_at: Tue, 03 Jan 2017 13:00:54 GMT
- request:
method: get
uri: http://api-adresse.data.gouv.fr/search?limit=1&q=50%20AV%20DES%20CHAMPS%20ELYSEES%20complement_adresse%2075008%20PARIS%208
body:
encoding: US-ASCII
string: ''
headers:
Accept:
- "*/*"
Accept-Encoding:
- gzip, deflate
User-Agent:
- rest-client/2.0.0 (darwin15.6.0 x86_64) ruby/2.3.1p112
response:
status:
code: 200
message: OK
headers:
Server:
- nginx/1.11.3
Date:
- Tue, 03 Jan 2017 13:00:55 GMT
Content-Type:
- application/json; charset=utf-8
Content-Length:
- '660'
Connection:
- keep-alive
Access-Control-Allow-Origin:
- "*"
Access-Control-Allow-Headers:
- X-Requested-With
body:
encoding: UTF-8
string: '{"limit": 1, "attribution": "BAN", "version": "draft", "licence": "ODbL
1.0", "query": "50 AV DES CHAMPS ELYSEES complement_adresse 75008 PARIS 8",
"type": "FeatureCollection", "features": [{"geometry": {"type": "Point", "coordinates":
[2.306888, 48.870374]}, "properties": {"citycode": "75108", "postcode": "75008",
"name": "50 Avenue des Champs \u00c9lys\u00e9es", "id": "ADRNIVX_0000000270748251",
"type": "housenumber", "context": "75, \u00cele-de-France", "score": 0.596517719568567,
"label": "50 Avenue des Champs \u00c9lys\u00e9es 75008 Paris", "city": "Paris",
"housenumber": "50", "street": "Avenue des Champs \u00c9lys\u00e9es"}, "type":
"Feature"}]}'
http_version:
recorded_at: Tue, 03 Jan 2017 13:00:55 GMT
recorded_with: VCR 3.0.3

View file

@ -1,51 +0,0 @@
---
http_interactions:
- request:
method: get
uri: https://geo.api.gouv.fr/departements?fields=nom
body:
encoding: US-ASCII
string: ''
headers:
User-Agent:
- demarches-simplifiees.fr
Accept:
- application/json
Accept-Encoding:
- gzip, deflate
Expect:
- ''
response:
status:
code: 200
message: OK
headers:
Server:
- nginx/1.10.3 (Ubuntu)
Date:
- Tue, 23 Oct 2018 13:11:36 GMT
Content-Type:
- application/json; charset=utf-8
Transfer-Encoding:
- chunked
Connection:
- keep-alive
Vary:
- Accept-Encoding
X-Powered-By:
- Express
Access-Control-Allow-Origin:
- "*"
Etag:
- W/"cc1-jlb3C7xpXUEaq56Wojrp9rAkoH8"
Strict-Transport-Security:
- max-age=15552000
Content-Encoding:
- gzip
body:
encoding: ASCII-8BIT
string: !binary |-
W3sibm9tIjoiQWluIiwiY29kZSI6IjAxIn0seyJub20iOiJBaXNuZSIsImNvZGUiOiIwMiJ9LHsibm9tIjoiQWxsaWVyIiwiY29kZSI6IjAzIn0seyJub20iOiJBbHBlcy1kZS1IYXV0ZS1Qcm92ZW5jZSIsImNvZGUiOiIwNCJ9LHsibm9tIjoiSGF1dGVzLUFscGVzIiwiY29kZSI6IjA1In0seyJub20iOiJBbHBlcy1NYXJpdGltZXMiLCJjb2RlIjoiMDYifSx7Im5vbSI6IkFyZMOoY2hlIiwiY29kZSI6IjA3In0seyJub20iOiJBcmRlbm5lcyIsImNvZGUiOiIwOCJ9LHsibm9tIjoiQXJpw6hnZSIsImNvZGUiOiIwOSJ9LHsibm9tIjoiQXViZSIsImNvZGUiOiIxMCJ9LHsibm9tIjoiQXVkZSIsImNvZGUiOiIxMSJ9LHsibm9tIjoiQXZleXJvbiIsImNvZGUiOiIxMiJ9LHsibm9tIjoiQm91Y2hlcy1kdS1SaMO0bmUiLCJjb2RlIjoiMTMifSx7Im5vbSI6IkNhbHZhZG9zIiwiY29kZSI6IjE0In0seyJub20iOiJDYW50YWwiLCJjb2RlIjoiMTUifSx7Im5vbSI6IkNoYXJlbnRlIiwiY29kZSI6IjE2In0seyJub20iOiJDaGFyZW50ZS1NYXJpdGltZSIsImNvZGUiOiIxNyJ9LHsibm9tIjoiQ2hlciIsImNvZGUiOiIxOCJ9LHsibm9tIjoiQ29ycsOoemUiLCJjb2RlIjoiMTkifSx7Im5vbSI6IkPDtHRlLWQnT3IiLCJjb2RlIjoiMjEifSx7Im5vbSI6IkPDtHRlcy1kJ0FybW9yIiwiY29kZSI6IjIyIn0seyJub20iOiJDcmV1c2UiLCJjb2RlIjoiMjMifSx7Im5vbSI6IkRvcmRvZ25lIiwiY29kZSI6IjI0In0seyJub20iOiJEb3VicyIsImNvZGUiOiIyNSJ9LHsibm9tIjoiRHLDtG1lIiwiY29kZSI6IjI2In0seyJub20iOiJFdXJlIiwiY29kZSI6IjI3In0seyJub20iOiJFdXJlLWV0LUxvaXIiLCJjb2RlIjoiMjgifSx7Im5vbSI6IkZpbmlzdMOocmUiLCJjb2RlIjoiMjkifSx7Im5vbSI6IkNvcnNlLWR1LVN1ZCIsImNvZGUiOiIyQSJ9LHsibm9tIjoiSGF1dGUtQ29yc2UiLCJjb2RlIjoiMkIifSx7Im5vbSI6IkdhcmQiLCJjb2RlIjoiMzAifSx7Im5vbSI6IkhhdXRlLUdhcm9ubmUiLCJjb2RlIjoiMzEifSx7Im5vbSI6IkdlcnMiLCJjb2RlIjoiMzIifSx7Im5vbSI6Ikdpcm9uZGUiLCJjb2RlIjoiMzMifSx7Im5vbSI6IkjDqXJhdWx0IiwiY29kZSI6IjM0In0seyJub20iOiJJbGxlLWV0LVZpbGFpbmUiLCJjb2RlIjoiMzUifSx7Im5vbSI6IkluZHJlIiwiY29kZSI6IjM2In0seyJub20iOiJJbmRyZS1ldC1Mb2lyZSIsImNvZGUiOiIzNyJ9LHsibm9tIjoiSXPDqHJlIiwiY29kZSI6IjM4In0seyJub20iOiJKdXJhIiwiY29kZSI6IjM5In0seyJub20iOiJMYW5kZXMiLCJjb2RlIjoiNDAifSx7Im5vbSI6IkxvaXItZXQtQ2hlciIsImNvZGUiOiI0MSJ9LHsibm9tIjoiTG9pcmUiLCJjb2RlIjoiNDIifSx7Im5vbSI6IkhhdXRlLUxvaXJlIiwiY29kZSI6IjQzIn0seyJub20iOiJMb2lyZS1BdGxhbnRpcXVlIiwiY29kZSI6IjQ0In0seyJub20iOiJMb2lyZXQiLCJjb2RlIjoiNDUifSx7Im5vbSI6IkxvdCIsImNvZGUiOiI0NiJ9LHsibm9tIjoiTG90LWV0LUdhcm9ubmUiLCJjb2RlIjoiNDcifSx7Im5vbSI6IkxvesOocmUiLCJjb2RlIjoiNDgifSx7Im5vbSI6Ik1haW5lLWV0LUxvaXJlIiwiY29kZSI6IjQ5In0seyJub20iOiJNYW5jaGUiLCJjb2RlIjoiNTAifSx7Im5vbSI6Ik1hcm5lIiwiY29kZSI6IjUxIn0seyJub20iOiJIYXV0ZS1NYXJuZSIsImNvZGUiOiI1MiJ9LHsibm9tIjoiTWF5ZW5uZSIsImNvZGUiOiI1MyJ9LHsibm9tIjoiTWV1cnRoZS1ldC1Nb3NlbGxlIiwiY29kZSI6IjU0In0seyJub20iOiJNZXVzZSIsImNvZGUiOiI1NSJ9LHsibm9tIjoiTW9yYmloYW4iLCJjb2RlIjoiNTYifSx7Im5vbSI6Ik1vc2VsbGUiLCJjb2RlIjoiNTcifSx7Im5vbSI6Ik5pw6h2cmUiLCJjb2RlIjoiNTgifSx7Im5vbSI6Ik5vcmQiLCJjb2RlIjoiNTkifSx7Im5vbSI6Ik9pc2UiLCJjb2RlIjoiNjAifSx7Im5vbSI6Ik9ybmUiLCJjb2RlIjoiNjEifSx7Im5vbSI6IlBhcy1kZS1DYWxhaXMiLCJjb2RlIjoiNjIifSx7Im5vbSI6IlB1eS1kZS1Ew7RtZSIsImNvZGUiOiI2MyJ9LHsibm9tIjoiUHlyw6luw6llcy1BdGxhbnRpcXVlcyIsImNvZGUiOiI2NCJ9LHsibm9tIjoiSGF1dGVzLVB5csOpbsOpZXMiLCJjb2RlIjoiNjUifSx7Im5vbSI6IlB5csOpbsOpZXMtT3JpZW50YWxlcyIsImNvZGUiOiI2NiJ9LHsibm9tIjoiQmFzLVJoaW4iLCJjb2RlIjoiNjcifSx7Im5vbSI6IkhhdXQtUmhpbiIsImNvZGUiOiI2OCJ9LHsibm9tIjoiUmjDtG5lIiwiY29kZSI6IjY5In0seyJub20iOiJIYXV0ZS1TYcO0bmUiLCJjb2RlIjoiNzAifSx7Im5vbSI6IlNhw7RuZS1ldC1Mb2lyZSIsImNvZGUiOiI3MSJ9LHsibm9tIjoiU2FydGhlIiwiY29kZSI6IjcyIn0seyJub20iOiJTYXZvaWUiLCJjb2RlIjoiNzMifSx7Im5vbSI6IkhhdXRlLVNhdm9pZSIsImNvZGUiOiI3NCJ9LHsibm9tIjoiUGFyaXMiLCJjb2RlIjoiNzUifSx7Im5vbSI6IlNlaW5lLU1hcml0aW1lIiwiY29kZSI6Ijc2In0seyJub20iOiJTZWluZS1ldC1NYXJuZSIsImNvZGUiOiI3NyJ9LHsibm9tIjoiWXZlbGluZXMiLCJjb2RlIjoiNzgifSx7Im5vbSI6IkRldXgtU8OodnJlcyIsImNvZGUiOiI3OSJ9LHsibm9tIjoiU29tbWUiLCJjb2RlIjoiODAifSx7Im5vbSI6IlRhcm4iLCJjb2RlIjoiODEifSx7Im5vbSI6IlRhcm4tZXQtR2Fyb25uZSIsImNvZGUiOiI4MiJ9LHsibm9tIjoiVmFyIiwiY29kZSI6IjgzIn0seyJub20iOiJWYXVjbHVzZSIsImNvZGUiOiI4NCJ9LHsibm9tIjoiVmVuZMOpZSIsImNvZGUiOiI4NSJ9LHsibm9tIjoiVmllbm5lIiwiY29kZSI6Ijg2In0seyJub20iOiJIYXV0ZS1WaWVubmUiLCJjb2RlIjoiODcifSx7Im5vbSI6IlZvc2dlcyIsImNvZGUiOiI4OCJ9LHsibm9tIjoiWW9ubmUiLCJjb2RlIjoiODkifSx7Im5vbSI6IlRlcnJpdG9pcmUgZGUgQmVsZm9ydCIsImNvZGUiOiI5MCJ9LHsibm9tIjoiRXNzb25uZSIsImNvZGUiOiI5MSJ9LHsibm9tIjoiSGF1dHMtZGUtU2VpbmUiLCJjb2RlIjoiOTIifSx7Im5vbSI6IlNlaW5lLVNhaW50LURlbmlzIiwiY29kZSI6IjkzIn0seyJub20iOiJWYWwtZGUtTWFybmUiLCJjb2RlIjoiOTQifSx7Im5vbSI6IlZhbC1kJ09pc2UiLCJjb2RlIjoiOTUifSx7Im5vbSI6Ikd1YWRlbG91cGUiLCJjb2RlIjoiOTcxIn0seyJub20iOiJNYXJ0aW5pcXVlIiwiY29kZSI6Ijk3MiJ9LHsibm9tIjoiR3V5YW5lIiwiY29kZSI6Ijk3MyJ9LHsibm9tIjoiTGEgUsOpdW5pb24iLCJjb2RlIjoiOTc0In0seyJub20iOiJNYXlvdHRlIiwiY29kZSI6Ijk3NiJ9XQ==
http_version:
recorded_at: Tue, 23 Oct 2018 13:11:36 GMT
recorded_with: VCR 4.0.0

View file

@ -1,51 +0,0 @@
---
http_interactions:
- request:
method: get
uri: https://geo.api.gouv.fr/regions?fields=nom
body:
encoding: US-ASCII
string: ''
headers:
User-Agent:
- demarches-simplifiees.fr
Accept:
- application/json
Accept-Encoding:
- gzip, deflate
Expect:
- ''
response:
status:
code: 200
message: OK
headers:
Server:
- nginx/1.10.3 (Ubuntu)
Date:
- Tue, 23 Oct 2018 13:11:36 GMT
Content-Type:
- application/json; charset=utf-8
Transfer-Encoding:
- chunked
Connection:
- keep-alive
Vary:
- Accept-Encoding
X-Powered-By:
- Express
Access-Control-Allow-Origin:
- "*"
Etag:
- W/"28d-5OgIzgwL+0K2UaO0foKduqMMBrA"
Strict-Transport-Security:
- max-age=15552000
Content-Encoding:
- gzip
body:
encoding: ASCII-8BIT
string: !binary |-
W3sibm9tIjoiR3VhZGVsb3VwZSIsImNvZGUiOiIwMSJ9LHsibm9tIjoiTWFydGluaXF1ZSIsImNvZGUiOiIwMiJ9LHsibm9tIjoiR3V5YW5lIiwiY29kZSI6IjAzIn0seyJub20iOiJMYSBSw6l1bmlvbiIsImNvZGUiOiIwNCJ9LHsibm9tIjoiTWF5b3R0ZSIsImNvZGUiOiIwNiJ9LHsibm9tIjoiw45sZS1kZS1GcmFuY2UiLCJjb2RlIjoiMTEifSx7Im5vbSI6IkNlbnRyZS1WYWwgZGUgTG9pcmUiLCJjb2RlIjoiMjQifSx7Im5vbSI6IkJvdXJnb2duZS1GcmFuY2hlLUNvbXTDqSIsImNvZGUiOiIyNyJ9LHsibm9tIjoiTm9ybWFuZGllIiwiY29kZSI6IjI4In0seyJub20iOiJIYXV0cy1kZS1GcmFuY2UiLCJjb2RlIjoiMzIifSx7Im5vbSI6IkdyYW5kIEVzdCIsImNvZGUiOiI0NCJ9LHsibm9tIjoiUGF5cyBkZSBsYSBMb2lyZSIsImNvZGUiOiI1MiJ9LHsibm9tIjoiQnJldGFnbmUiLCJjb2RlIjoiNTMifSx7Im5vbSI6Ik5vdXZlbGxlLUFxdWl0YWluZSIsImNvZGUiOiI3NSJ9LHsibm9tIjoiT2NjaXRhbmllIiwiY29kZSI6Ijc2In0seyJub20iOiJBdXZlcmduZS1SaMO0bmUtQWxwZXMiLCJjb2RlIjoiODQifSx7Im5vbSI6IlByb3ZlbmNlLUFscGVzLUPDtHRlIGQnQXp1ciIsImNvZGUiOiI5MyJ9LHsibm9tIjoiQ29yc2UiLCJjb2RlIjoiOTQifV0=
http_version:
recorded_at: Tue, 23 Oct 2018 13:11:36 GMT
recorded_with: VCR 4.0.0

File diff suppressed because one or more lines are too long

View file

@ -1,44 +0,0 @@
require 'spec_helper'
describe ApiAdresse::AddressAdapter do
describe '#get_suggestions' do
let(:request) { 'Paris' }
let(:response) { File.open('spec/fixtures/files/api_adresse/search_results.json') }
let(:status) { 200 }
subject { described_class.new(request).get_suggestions }
before do
stub_request(:get, "https://api-adresse.data.gouv.fr/search?&q=#{request}&limit=5")
.to_return(status: status, body: response, headers: {})
end
context 'when address return a list of address' do
it { expect(subject.size).to eq 5 }
it { is_expected.to be_an_instance_of Array }
end
context 'when address return an empty list' do
let(:response) { File.open('spec/fixtures/files/api_adresse/search_no_results.json') }
it { expect(subject.size).to eq 0 }
it { is_expected.to be_an_instance_of Array }
end
context 'when BAN is unavailable' do
let(:status) { 503 }
let(:response) { '' }
it { expect(subject.size).to eq 0 }
it { is_expected.to be_an_instance_of Array }
end
context 'when request is empty' do
let(:response) { 'Missing query' }
let(:request) { '' }
it { expect(subject.size).to eq 0 }
it { is_expected.to be_an_instance_of Array }
end
end
end

View file

@ -1,31 +0,0 @@
require 'spec_helper'
describe ApiAdresse::PointAdapter do
let(:address) { '50 av des champs elysees' }
describe '.geocode', vcr: { cassette_name: 'api_adresse_octo' } do
it 'return a point' do
expect(described_class.new(address).geocode.class).to eq(RGeo::Cartesian::PointImpl)
end
context 'when RestClient::Exception' do
before do
allow(ApiAdresse::API).to receive(:call).and_raise(RestClient::Exception)
end
it 'return nil' do
expect(described_class.new(address).geocode).to be_nil
end
end
context 'when JSON::ParserError' do
before do
allow(JSON).to receive(:parse).and_raise(JSON::ParserError)
end
it 'return nil' do
expect(described_class.new(address).geocode).to be_nil
end
end
end
end

View file

@ -1,75 +0,0 @@
require 'spec_helper'
describe ApiGeo::API do
describe '.regions', vcr: { cassette_name: 'api_geo_regions' } do
subject { described_class.regions }
it { expect(subject.size).to eq 18 }
end
describe '.departements', vcr: { cassette_name: 'api_geo_departements' } do
subject { described_class.departements }
it { expect(subject.size).to eq 101 }
end
describe '.pays' do
subject { described_class.pays }
let(:pays) {
JSON.parse(File.open('app/lib/api_geo/pays.json').read, symbolize_names: true)
}
it { is_expected.to eq pays }
end
describe '.search_rpg', vcr: { cassette_name: 'api_geo_search_rpg' } do
let(:coordinates) do
[
[
2.3945903778076176,
46.53312237252731
],
[
2.394933700561524,
46.532590956418076
],
[
2.3948478698730473,
46.53170525134736
],
[
2.393732070922852,
46.530760483351195
],
[
2.3909854888916016,
46.5309376286023
],
[
2.391414642333985,
46.531232869403546
],
[
2.3913288116455083,
46.53253190986272
],
[
2.39278793334961,
46.53329951007484
],
[
2.3945903778076176,
46.53312237252731
]
]
end
let(:geo_json) {
GeojsonService.to_json_polygon_for_rpg(coordinates)
}
subject { described_class.search_rpg(geo_json) }
it { expect(subject[:features].size).to eq 3 }
end
end

View file

@ -1,62 +0,0 @@
require 'spec_helper'
describe ApiGeo::RPGAdapter do
subject { described_class.new(coordinates).results }
let(:coordinates) do
[
[
2.3945903778076176,
46.53312237252731
],
[
2.394933700561524,
46.532590956418076
],
[
2.3948478698730473,
46.53170525134736
],
[
2.393732070922852,
46.530760483351195
],
[
2.3909854888916016,
46.5309376286023
],
[
2.391414642333985,
46.531232869403546
],
[
2.3913288116455083,
46.53253190986272
],
[
2.39278793334961,
46.53329951007484
],
[
2.3945903778076176,
46.53312237252731
]
]
end
context 'coordinates are filled', vcr: { cassette_name: 'api_geo_search_rpg' } do
describe 'Attribut filter' do
it { expect(subject.size).to eq(3) }
it do
expect(subject.first.keys).to eq([
:culture,
:code_culture,
:surface,
:bio,
:geometry,
:geo_reference_id
])
end
end
end
end

View file

@ -21,6 +21,31 @@ describe Administrateur, type: :model do
end
end
describe "#can_be_deleted?" do
subject { administrateur.can_be_deleted? }
context 'when the administrateur has a dossier in instruction' do
let!(:dossier) { create(:dossier, :en_instruction) }
let(:administrateur) { dossier.procedure.administrateurs.first }
it { is_expected.to be false }
end
context "when the administrateur's procedures have other administrateurs" do
let!(:administrateur) { create(:administrateur) }
let!(:autre_administrateur) { create(:administrateur) }
let!(:procedure) { create(:procedure, administrateurs: [administrateur, autre_administrateur]) }
it { is_expected.to be true }
end
context "when the administrateur has no procedure" do
let!(:administrateur) { create(:administrateur) }
it { is_expected.to be true }
end
end
# describe '#password_complexity' do
# let(:email) { 'mail@beta.gouv.fr' }
# let(:passwords) { ['pass', '12pass23', 'démarches ', 'démarches-simple', 'démarches-simplifiées-pwd'] }

View file

@ -32,12 +32,6 @@ shared_examples 'champ_spec' do
end
end
describe '.departement', vcr: { cassette_name: 'api_geo_departements' } do
subject { Champs::DepartementChamp.departements }
it { expect(subject).to include '99 - Étranger' }
end
context "when type_champ=date" do
let(:type_de_champ) { create(:type_de_champ_date) }
let(:champ) { type_de_champ.champ.create }

View file

@ -9,12 +9,12 @@ describe Champs::PieceJustificativeChamp do
context 'when file is safe' do
let(:status) { ActiveStorage::VirusScanner::SAFE }
it { is_expected.to include("/rails/active_storage/blobs/") }
it { is_expected.to include("/rails/active_storage/disk/") }
end
context 'when file is not scanned' do
let(:status) { ActiveStorage::VirusScanner::PENDING }
it { is_expected.to include("/rails/active_storage/blobs/") }
it { is_expected.to include("/rails/active_storage/disk/") }
end
context 'when file is infected' do

View file

@ -5,6 +5,13 @@ describe Dossier do
let(:user) { create(:user) }
describe 'validations' do
let(:procedure) { create(:procedure, :for_individual) }
subject(:dossier) { create(:dossier, procedure: procedure) }
it { is_expected.to validate_presence_of(:individual) }
end
describe "without_followers scope" do
let!(:dossier) { create(:dossier, :followed, :with_entreprise, user: user) }
let!(:dossier2) { create(:dossier, :with_entreprise, user: user) }
@ -112,56 +119,44 @@ describe Dossier do
end
end
describe '#build_default_champs' do
context 'when dossier is linked to a procedure with type_de_champ_public and private' do
let(:dossier) { create(:dossier, user: user) }
describe '#create' do
let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private) }
let(:dossier) { create(:dossier, procedure: procedure, user: user) }
it 'build all champs needed' do
expect(dossier.champs.count).to eq(1)
it 'builds public and private champs' do
expect(dossier.champs.count).to eq(1)
expect(dossier.champs_private.count).to eq(1)
end
context 'when the dossier belongs to a procedure for individuals' do
let(:procedure) { create(:procedure, :with_type_de_champ, for_individual: true) }
it 'creates a default individual' do
expect(dossier.individual).to be_present
expect(dossier.individual.nom).to be_nil
expect(dossier.individual.prenom).to be_nil
expect(dossier.individual.gender).to be_nil
end
it 'build all champs_private needed' do
expect(dossier.champs_private.count).to eq(1)
context 'and the user signs-in using France Connect' do
let(:france_connect_information) { build(:france_connect_information) }
let(:user) { build(:user, france_connect_information: france_connect_information) }
it 'fills the individual with the informations from France Connect' do
expect(dossier.individual.nom).to eq('DUBOIS')
expect(dossier.individual.prenom).to eq('Angela Claire Louise')
expect(dossier.individual.gender).to eq(Individual::GENDER_FEMALE)
end
end
end
end
describe '#build_default_individual' do
context 'when dossier is linked to a procedure with for_individual attr false' do
let(:dossier) { create(:dossier, user: user) }
context 'when the dossier belongs to a procedure for moral personas' do
let(:procedure) { create(:procedure, :with_type_de_champ, for_individual: false) }
it 'have no object created' do
it 'doesnt create a individual' do
expect(dossier.individual).to be_nil
end
end
context 'when dossier is linked to a procedure with for_individual attr true' do
let(:dossier) { create(:dossier, user: user, procedure: (create :procedure, for_individual: true)) }
it 'have no object created' do
expect(dossier.individual).not_to be_nil
end
end
end
describe '#save' do
subject { build(:dossier, procedure: procedure, user: user) }
let!(:procedure) { create(:procedure) }
context 'when is linked to a procedure' do
it 'creates default champs' do
expect(subject).to receive(:build_default_champs)
subject.save
end
end
context 'when is not linked to a procedure' do
subject { create(:dossier, procedure: nil, user: user) }
it 'does not create default champs' do
expect(subject).not_to receive(:build_default_champs)
subject.update(state: Dossier.states.fetch(:en_construction))
end
end
end
end
@ -1019,18 +1014,6 @@ describe Dossier do
end
end
describe '#update_with_france_connect' do
let(:dossier) { create(:dossier, user: user) }
let(:user_info) { create(:france_connect_information) }
it {
dossier.update_with_france_connect(user_info)
expect(dossier.individual.gender).to eq 'Mme'
expect(dossier.individual.nom).to eq user_info.family_name
expect(dossier.individual.prenom).to eq user_info.given_name
}
end
describe '#for_procedure' do
let!(:procedure_1) { create(:procedure) }
let!(:procedure_2) { create(:procedure) }

View file

@ -209,4 +209,45 @@ describe User, type: :model do
it { is_expected.to be true }
end
end
describe '#can_be_deleted?' do
let(:user) { create(:user) }
subject { user.can_be_deleted? }
context 'when the user has a dossier in instruction' do
let!(:dossier) { create(:dossier, :en_instruction, user: user) }
it { is_expected.to be false }
end
context 'when the user has no dossier in instruction' do
it { is_expected.to be true }
end
end
describe '#delete_and_keep_track_dossiers' do
let(:administration) { create(:administration) }
let(:user) { create(:user) }
context 'with a dossier in instruction' do
let!(:dossier_en_instruction) { create(:dossier, :en_instruction, user: user) }
it 'raises' do
expect { user.delete_and_keep_track_dossiers(administration) }.to raise_error
end
end
context 'without a dossier in instruction' do
let!(:dossier_en_construction) { create(:dossier, :en_construction, user: user) }
let!(:dossier_brouillon) { create(:dossier, user: user) }
it "keep track of dossiers and delete user" do
user.delete_and_keep_track_dossiers(administration)
expect(DeletedDossier.find_by(dossier_id: dossier_en_construction)).to be_present
expect(DeletedDossier.find_by(dossier_id: dossier_brouillon)).to be_present
expect(User.find_by(id: user.id)).to be_nil
end
end
end
end

View file

@ -14,7 +14,7 @@ describe ChampSerializer do
end
after { champ.piece_justificative_file.purge }
it { is_expected.to include(value: url_for(champ.piece_justificative_file)) }
it { expect(subject[:value]).to match('/rails/active_storage/disk/') }
end
context 'when type champ is not piece justificative' do

View file

@ -79,13 +79,14 @@ describe DossierSerializer do
],
pieces_justificatives: [
{
"content_url" => champ_pj.for_api,
"content_url" => subject[:pieces_justificatives][0]["content_url"],
"created_at" => champ_pj.created_at.in_time_zone('UTC').iso8601(3),
"type_de_piece_justificative_id" => original_pj_id,
"user" => a_hash_including("id" => dossier.user.id)
}
]
)
expect(subject[:pieces_justificatives][0]["content_url"]).to match('/rails/active_storage/disk/')
end
it "does not expose the PJ as a champ" do

View file

@ -69,6 +69,7 @@ describe ProcedureExportService do
"pays",
"regions",
"departements",
"communes",
"engagement",
"dossier_link",
"piece_justificative",
@ -142,6 +143,7 @@ describe ProcedureExportService do
"pays",
"regions",
"departements",
"communes",
"engagement",
"dossier_link",
"piece_justificative",
@ -220,6 +222,7 @@ describe ProcedureExportService do
"pays",
"regions",
"departements",
"communes",
"engagement",
"dossier_link",
"piece_justificative",

View file

@ -150,6 +150,8 @@ RSpec.configure do |config|
Typhoeus::Expectation.clear
ActionMailer::Base.deliveries.clear
ActiveStorage::Current.host = 'http://test.host'
}
RSpec::Matchers.define :have_same_attributes_as do |expected, options|

View file

@ -103,7 +103,7 @@ describe 'instructeurs/dossiers/state_button.html.haml', type: :view do
it 'allows to download the justificatif' do
expect(rendered).to have_dropdown_item('Justificatif')
expect(rendered).to have_link(href: url_for(dossier.justificatif_motivation.attachment.blob))
expect(response).to have_css("a[href*='/rails/active_storage/blobs/']", text: dossier.justificatif_motivation.attachment.filename.to_s)
end
end
end

View file

@ -17,7 +17,7 @@ describe 'users/dossiers/brouillon.html.haml', type: :view do
end
it 'affiche un lien vers la notice' do
expect(rendered).to have_link("Guide de la démarche", href: url_for(procedure.notice))
expect(response).to have_css("a[href*='/rails/active_storage/blobs/']", text: "Guide de la démarche")
end
it 'affiche les boutons de validation' do

View file

@ -1521,13 +1521,6 @@ atob@^2.1.1:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
autocomplete.js@^0.37.0:
version "0.37.0"
resolved "https://registry.yarnpkg.com/autocomplete.js/-/autocomplete.js-0.37.0.tgz#bcfbfd7bcabe90e90fad4c2b1aaa931379a10c38"
integrity sha512-MxYfNb89sl7IRhNdEJv6z8dSfA7lVeU7Dk6m/+/ih0/tPLsbxIM7uPVsnWEUh4j7dFqJktlM7hCeU7jmx6VC8A==
dependencies:
immediate "^3.2.3"
autoprefixer@^9.4.9:
version "9.5.1"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.5.1.tgz#243b1267b67e7e947f28919d786b50d3bb0fb357"
@ -4362,11 +4355,6 @@ ignore@^4.0.6:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
immediate@^3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c"
integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=
import-cwd@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"