Skip to content

Commit

Permalink
Merge pull request #1579 from etalab/features/api-particulier-fqf-mod…
Browse files Browse the repository at this point in the history
…ality

API Particulier x DataPass : handle Formulaire QF modality
  • Loading branch information
jbfeldis authored Dec 3, 2024
2 parents 8a2e9de + d0276e2 commit 30be26e
Show file tree
Hide file tree
Showing 44 changed files with 1,105 additions and 8 deletions.
4 changes: 4 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ Metrics/MethodLength:
RSpec/NestedGroups:
Enabled: false

RSpec/SpecFilePathFormat:
Exclude:
- "spec/**/*hubee*"

Naming/VariableNumber:
Enabled: false

Expand Down
9 changes: 7 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ gem 'ransack'
gem 'wicked'

gem 'rest-client'
gem 'faraday'
gem 'faraday-gzip'
gem 'faraday-net_http'
gem 'faraday-retry'
gem 'faraday-encoding'

group :development, :test do
gem 'awesome_print'
Expand All @@ -101,10 +106,10 @@ group :development do
# Can be configured to work on production as well see: https://github.com/MiniProfiler/rack-mini-profiler/blob/master/README.md
gem 'rack-mini-profiler', '~> 3.3'
gem 'rubocop', require: false
gem 'rubocop-rails'
gem 'rubocop-rspec'
gem 'rubocop-capybara'
gem 'rubocop-factory_bot'
gem 'rubocop-rails'
gem 'rubocop-rspec'
gem 'rubocop-rspec_rails'

gem 'better_errors'
Expand Down
13 changes: 13 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,18 @@ GEM
faraday-net_http (>= 2.0, < 3.4)
json
logger
faraday-encoding (0.0.6)
faraday
faraday-gzip (2.0.1)
faraday (>= 1.0)
zlib (~> 3.0)
faraday-net_http (3.3.0)
net-http
faraday-net_http_persistent (2.3.0)
faraday (~> 2.5)
net-http-persistent (>= 4.0.4, < 5)
faraday-retry (2.2.1)
faraday (~> 2.0)
ferrum (0.15)
addressable (~> 2.5)
concurrent-ruby (~> 1.1)
Expand Down Expand Up @@ -561,6 +568,7 @@ GEM
nokogiri (~> 1.8)
yajl-ruby (1.4.3)
zeitwerk (2.7.1)
zlib (3.1.1)

PLATFORMS
aarch64-linux
Expand All @@ -585,6 +593,11 @@ DEPENDENCIES
cuprite
draper
factory_bot_rails
faraday
faraday-encoding
faraday-gzip
faraday-net_http
faraday-retry
gaffe
good_job (~> 3.99)
guard-rspec
Expand Down
23 changes: 23 additions & 0 deletions app/clients/abstract_hubee_api_client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require 'faraday'

class AbstractHubEEAPIClient
protected

def http_connection(&block)
Faraday.new do |conn|
conn.request :retry, max: 5
conn.response :raise_error
conn.response :json
conn.options.timeout = 2
yield(conn) if block
end
end

def consumer_key
Rails.application.credentials.hubee_consumer_key
end

def consumer_secret
Rails.application.credentials.hubee_consumer_secret
end
end
23 changes: 23 additions & 0 deletions app/clients/abstract_insee_api_client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require 'faraday'

class AbstractINSEEAPIClient
protected

def http_connection(&block)
@http_connection ||= Faraday.new do |conn|
conn.request :retry, max: 5
conn.response :raise_error
conn.response :json
conn.options.timeout = 2
yield(conn) if block
end
end

def consumer_key
Rails.application.credentials.insee_consumer_key
end

def consumer_secret
Rails.application.credentials.insee_consumer_secret
end
end
33 changes: 33 additions & 0 deletions app/clients/formulaire_qf_api_client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# :nocov:
class FormulaireQFAPIClient
def create_collectivity(organization:, editor_id: nil)
params = {
siret: organization.siret,
code_cog: organization.code_commune_etablissement,
departement: organization.code_postal_etablissement[0..1],
name: organization.denomination,
status: 'active',
editor: editor_id
}

http_connection.post("#{host}/api/collectivites", params.to_json)
end

private

def host
Rails.application.credentials.formulaire_qf.host
end

def http_connection(&block)
Faraday.new do |conn|
conn.headers['Content-Type'] = 'application/json'
conn.request :authorization, 'Bearer', -> { secret }
yield(conn) if block
end
end

def secret
Rails.application.credentials.formulaire_qf.secret
end
end
29 changes: 29 additions & 0 deletions app/clients/hubee_api_authentication.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# :nocov:
class HubEEAPIAuthentication < AbstractHubEEAPIClient
def access_token
http_connection.post(
auth_url,
'grant_type=client_credentials&scope=ADMIN',
{
'Authorization' => "Basic #{encoded_client_id_and_secret}"
}
).body['access_token']
end

private

def auth_url
Rails.application.credentials.hubee_auth_url
end

def encoded_client_id_and_secret
Base64.strict_encode64("#{consumer_key}:#{consumer_secret}")
end

def http_connection(&block)
@http_connection ||= super do |conn|
conn.response :json
yield(conn) if block
end
end
end
131 changes: 131 additions & 0 deletions app/clients/hubee_api_client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# :nocov:
class HubEEAPIClient < AbstractHubEEAPIClient # rubocop:disable Metrics/ClassLength
class NotFound < StandardError; end
class AlreadyExists < StandardError; end

def find_or_create_organization(organization, email_demandeur = nil)
find_organization(organization)
rescue NotFound
create_organization(organization, email_demandeur)
end

def find_organization(organization)
http_connection.get("#{host}/referential/v1/organizations/SI-#{organization.siret}-#{organization.code_commune_etablissement}").body
rescue Faraday::ResourceNotFound
raise NotFound
end

def create_organization(organization, email)
http_connection.post(
"#{host}/referential/v1/organizations",
{
type: 'SI',
companyRegister: organization.siret,
branchCode: organization.code_commune_etablissement,
email:,
name: organization.denomination,
country: 'France',
postalCode: organization.code_postal_etablissement,
territory: organization.code_commune_etablissement,
status: 'Actif'
}.to_json,
'Content-Type' => 'application/json'
).body
rescue Faraday::BadRequestError => e
raise AlreadyExists if already_exists_error?(e)

raise
end

def create_subscription(authorization_request, organization_payload, process_code, editor_payload = {})
subscription_payload = find_or_create_inactive_subscription(authorization_request, organization_payload, process_code)
activate_subscription(authorization_request, subscription_payload, editor_payload)
subscription_payload
end

def find_subscription(_authorization_request, organization_payload, process_code)
request = http_connection { |conn| conn.request :gzip }.get(
"#{host}/referential/v1/subscriptions",
companyRegister: organization_payload['companyRegister'],
processCode: process_code
)
request.body.first
end

protected

def host
Rails.application.credentials.hubee_api_url
end

def already_exists_error?(faraday_error)
faraday_error.response[:body]['errors'].any? do |error|
error['message'].include?('already exists')
end
end

def http_connection(&block)
super do |conn|
conn.request :authorization, 'Bearer', -> { HubEEAPIAuthentication.new.access_token }
yield(conn) if block
end
end

private

def activate_subscription(authorization_request, subscription_payload, editor_payload = {}) # rubocop:disable Metrics/AbcSize
subscription_id = authorization_request.extra_infos['hubee_subscription_id']
return if subscription_id.blank?

payload = subscription_payload.with_indifferent_access.merge({
status: 'Actif',
activateDateTime: DateTime.now.iso8601,
accessMode: 'API',
notificationFrequency: 'Aucune'
}.with_indifferent_access)

payload.delete('id')
payload.delete('creationDateTime')
payload.merge!(editor_payload.with_indifferent_access)

http_connection.put(
"#{host}/referential/v1/subscriptions/#{subscription_id}",
payload.to_json,
'Content-Type' => 'application/json'
).body
end

def create_inactive_subscription(authorization_request, organization_payload, process_code) # rubocop:disable Metrics/AbcSize
http_connection.post(
"#{host}/referential/v1/subscriptions",
{
datapassId: authorization_request.external_id.to_i,
notificationFrequency: 'Aucune',
processCode: process_code,
subscriber: {
type: 'SI',
companyRegister: organization_payload['companyRegister'],
branchCode: organization_payload['branchCode']
},
email: authorization_request.demandeur.email,
status: 'Inactif',
localAdministrator: {
email: authorization_request.demandeur.email
},
validateDateTime: DateTime.now.iso8601,
updateDateTime: DateTime.now.iso8601
}.to_json,
'Content-Type' => 'application/json'
).body
rescue Faraday::BadRequestError => e
raise AlreadyExists if already_exists_error?(e)

raise
end

def find_or_create_inactive_subscription(authorization_request, organization_payload, process_code)
create_inactive_subscription(authorization_request, organization_payload, process_code)
rescue HubEEAPIClient::AlreadyExists
find_subscription(authorization_request, organization_payload, process_code)
end
end
19 changes: 19 additions & 0 deletions app/clients/insee_api_authentication.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

class INSEEAPIAuthentication < AbstractINSEEAPIClient
def access_token
http_connection.post(
'https://api.insee.fr/token',
'grant_type=client_credentials',
{
'Authorization' => "Basic #{encoded_client_id_and_secret}"
}
).body['access_token']
end

private

def encoded_client_id_and_secret
Base64.strict_encode64("#{consumer_key}:#{consumer_secret}")
end
end
15 changes: 15 additions & 0 deletions app/clients/insee_sirene_api_client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class INSEESireneAPIClient < AbstractINSEEAPIClient
def etablissement(siret:)
http_connection.get(
"https://api.insee.fr/entreprises/sirene/V3.11/siret/#{siret}"
).body
end

protected

def http_connection
super do |conn|
conn.request :authorization, 'Bearer', -> { INSEEAPIAuthentication.new.access_token }
end
end
end
4 changes: 3 additions & 1 deletion app/interactors/datapass_webhook/adapt_v2_to_v1.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class DatapassWebhook::AdaptV2ToV1 < ApplicationInteractor
def call
context.event = context.event.sub('authorization_request', 'enrollment')
context.authorization_request_data = generic_data.dup
context.data = build_data
end

Expand All @@ -21,7 +22,8 @@ def build_data
'previous_enrollment_id' => nil,
'scopes' => generic_data['scopes'].index_with { |_scope| true },
'team_members' => build_team_members,
'events' => []
'events' => [],
'service_provider' => context.data['service_provider']
}
}
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class DatapassWebhook::APIParticulier::CreateFormulaireQFCollectivity < ApplicationInteractor
delegate :authorization_request, to: :context
delegate :organization, to: :authorization_request, private: true

def call
FormulaireQFAPIClient.new.create_collectivity(organization:, editor_id:)
end

private

def editor_id
authorization_request.extra_infos.dig('service_provider', 'id')
end
end
Loading

0 comments on commit 30be26e

Please sign in to comment.