Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(france-travail): add rendez-vous-partenaire api webhooks for participation v2 #2497

Open
wants to merge 24 commits into
base: staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,9 @@ CARNET_DE_BORD_URL=https://demo.carnetdebord.inclusion.beta.gouv.fr
CARNET_DE_BORD_API_SECRET=secret_api_token

DEPARTMENTS_WHERE_PARCOURS_DISABLED=44

# France Travail Recette
FRANCE_TRAVAIL_AUTH_URL=https://entreprise-r.ft-qvr.fr
FRANCE_TRAVAIL_API_URL=https://api-r.ft-qvr.io
FRANCE_TRAVAIL_CLIENT_ID=client_id
FRANCE_TRAVAIL_CLIENT_SECRET=client_secret
4 changes: 3 additions & 1 deletion .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ AGENT_CONNECT_CLIENT_SECRET=client_secret

# France Travail
FRANCE_TRAVAIL_AUTH_URL=https://somefakeauthurl.fr
FRANCE_TRAVAIL_RDV_API_URL=https://francetravailfakerdvurl.fr
FRANCE_TRAVAIL_API_URL=https://francetravailfakerdvurl.fr
FRANCE_TRAVAIL_CLIENT_ID=client_id
FRANCE_TRAVAIL_CLIENT_SECRET=client_secret

AGENT_SIGNATURE_KEY=bc995863-5c80-43a3-a31d-0da216e814a4
33 changes: 33 additions & 0 deletions app/clients/france_travail_client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class FranceTravailClient
def self.create_participation(payload:, headers:)
Faraday.post(
"#{ENV['FRANCE_TRAVAIL_API_URL']}/partenaire/rendez-vous-partenaire/v1/rendez-vous",
payload.to_json,
headers
)
end

def self.update_participation(payload:, headers:)
Faraday.put(
"#{ENV['FRANCE_TRAVAIL_API_URL']}/partenaire/rendez-vous-partenaire/v1/rendez-vous",
payload.to_json,
headers
)
end

def self.delete_participation(france_travail_id:, headers:)
Faraday.delete(
"#{ENV['FRANCE_TRAVAIL_API_URL']}/partenaire/rendez-vous-partenaire/v1/rendez-vous/#{france_travail_id}",
Holist marked this conversation as resolved.
Show resolved Hide resolved
{},
headers
)
end

Holist marked this conversation as resolved.
Show resolved Hide resolved
def self.retrieve_user_token(payload:, headers:)
Faraday.post(
"#{ENV['FRANCE_TRAVAIL_API_URL']}/partenaire/rechercher-usager/v1/usagers/recherche",
payload.to_json,
headers
)
end
end
13 changes: 13 additions & 0 deletions app/clients/france_travail_headers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class FranceTravailHeaders
def self.for_user(user)
FranceTravailApi::BuildUserAuthenticatedHeaders.call(user: user).headers
end

def self.for_client
access_token = FranceTravailApi::RetrieveAccessToken.call.access_token
{
"Authorization" => "Bearer #{access_token}",
"Content-Type" => "application/json"
}
end
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C'est très cool d'avoir présenté ça comme ça dans cette classe.
Malheureusement comme l'API de nos services est pas ouf, quand tu vas appeler ces méthodes depuis les services d'origine, il ne va rien se passer quand les services appelés par ces méthodes vont fail (ce qui risque d'arriver souvent malheureusement vu les API capricieuses de FT 😢 ).
Aujourd'hui le seul moyen d'appeler un service correctement depuis un autre service c'est la méthode call_service!, je pense qu'on pourrait faire mieux. Mais du coup je pense que soit on trouve un moyen ici de gérer ça autrement que via la méthode call_service!, soit au déplace l'appel de ces services dans les services directement.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bien vu merci pour ce rappel au sujet de call_service! J'ai mis ca en place comme tu le suggére ici : 5539acf

end
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module OutgoingWebhooks
module FranceTravail
class LockedAndOrderedJobBase < ApplicationJob
include LockedAndOrderedJobs

def self.job_timestamp(timestamp:, **)
timestamp
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module OutgoingWebhooks
module FranceTravail
class ProcessParticipationJob < LockedAndOrderedJobBase
def self.lock_key(participation_id:, **)
"#{base_lock_key}:#{participation_id}"
end

def perform(participation_id:, timestamp:, event:)
call_service!(FranceTravailApi::ProcessParticipation, participation_id: participation_id, timestamp: timestamp,
event: event)
end
end
end
end
10 changes: 0 additions & 10 deletions app/jobs/outgoing_webhooks/send_france_travail_webhook_job.rb

This file was deleted.

90 changes: 90 additions & 0 deletions app/models/concerns/participation/france_travail_payload.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
module Participation::FranceTravailPayload
extend ActiveSupport::Concern

# rubocop:disable Metrics/AbcSize
def to_ft_payload
{
id: france_travail_id,
adresse: lieu&.address,
date: starts_at.to_datetime,
duree: duration_in_min,
information: motif.instruction_for_rdv,
initiateur: france_travail_initiateur,
libelleAdresse: organisation.name,
modaliteContact: france_travail_modalite,
motif: france_travail_motif,
organisme: {
code: france_travail_organisme_code,
emailContact: organisation.email,
idStructure: organisation.safir_code,
libelleStructure: organisation.name,
telephoneContact: organisation.phone_number
},
statut: france_travail_statut,
telephoneContactUsager: user.phone_number,
theme: motif.name,
typeReception: france_travail_type_reception,
interlocuteur: {
email: agents.first.email,
nom: agents.first.last_name,
prenom: agents.first.first_name
}
}
end
# rubocop:enable Metrics/AbcSize

# Liste des modalités FT (on ne prend en compte que le physique et le telephone): PHYSIQUE, TELEPHONE, VISIO
def france_travail_modalite
by_phone? ? "TELEPHONE" : "PHYSIQUE"
end

# Liste des initiateurs FT : USAGER, PARTENAIRE
def france_travail_initiateur
created_by_user? ? "USAGER" : "PARTENAIRE"
end

# Liste des motifs FT : AUT, ACC, ORI
def france_travail_motif
case motif.motif_category&.motif_category_type
when "rsa_orientation"
"ORI"
when "rsa_accompagnement"
"ACC"
else
"AUT"
end
end

# Liste des codes organismes FT : IND, FT, CD, DCD, ML, CE
def france_travail_organisme_code
Holist marked this conversation as resolved.
Show resolved Hide resolved
case organisation.organisation_type
when "conseil_departemental"
"CD"
when "france_travail"
"FT"
when "delegataire_rsa"
"DCD"
else
"IND"
end
end

# Liste des types de réception FT : COL, IND
def france_travail_type_reception
collectif? ? "COL" : "IND"
end

# Liste des statuts FT : PRIS, EFFECTUE, MODIFIE, ABSENT, ANNULE
def france_travail_statut
case status
when "seen"
"EFFECTUE"
when "excused", "revoked"
"ANNULE"
when "noshow"
"ABSENT"
else
"PRIS"
end
end
end
33 changes: 33 additions & 0 deletions app/models/concerns/participation/france_travail_webhooks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# For france travail the webhooks are specific, we have to adapt to FT specs as they could not
# implement a system integrating our webhooks, so we separated the two webhooks logic.
module Participation::FranceTravailWebhooks
extend ActiveSupport::Concern

included do
after_commit on: :create, if: -> { eligible_for_france_travail_webhook? } do
OutgoingWebhooks::FranceTravail::ProcessParticipationJob.perform_later(
participation_id: id, timestamp: created_at, event: :create
)
end

after_commit on: :update, if: -> { eligible_for_france_travail_webhook? } do
OutgoingWebhooks::FranceTravail::ProcessParticipationJob.perform_later(
participation_id: id, timestamp: updated_at, event: :update
)
end

around_destroy lambda { |participation, block|
if participation.eligible_for_france_travail_webhook?
OutgoingWebhooks::FranceTravail::ProcessParticipationJob.perform_later(
participation_id: participation.id, timestamp: Time.current, event: :delete
)
end

block.call
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je n'avais pas fait attention pendant ma première review mais on va avoir un problème ici: lorsque tu vas appeler Participation.find dans le service la participation aura probablement déjà disparu. C'est pour ça que dans l'implémentation précédente le payload était généré ici avant le job.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bien vu pour ca effectivement....
Comme on a vu ensemble le plus propre pour éviter d'avoir des arguments conditionnés au delete uniquement dans un Job (ProcessParticipationJob) qui faisait déjà beaucoup de choses c'est de partir sur une meilleure séparation des responsabilité (comme tu me l'avais suggéré initialement d'ailleurs ;) ) Je met en place des jobs spécifiques pour le create/l'update et le delete. C'est fait dans le dernier commit : 375c08c

end

def eligible_for_france_travail_webhook?
Holist marked this conversation as resolved.
Show resolved Hide resolved
organisation.france_travail? && user.birth_date? && user.nir?
end
end
91 changes: 0 additions & 91 deletions app/models/concerns/rdv/france_travail_webhooks.rb

This file was deleted.

5 changes: 4 additions & 1 deletion app/models/participation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ class Participation < ApplicationRecord
include Notificable
include HasCurrentCategoryConfiguration
include RdvParticipationStatus
include Participation::FranceTravailWebhooks
include Participation::FranceTravailPayload

belongs_to :rdv
belongs_to :follow_up
Expand All @@ -14,6 +16,7 @@ class Participation < ApplicationRecord

has_many :notifications, dependent: :destroy
has_many :follow_up_invitations, through: :follow_up, source: :invitations
has_many :agents, through: :rdv

has_one :organisation, through: :rdv

Expand All @@ -28,7 +31,7 @@ class Participation < ApplicationRecord

enum created_by: { agent: "agent", user: "user", prescripteur: "prescripteur" }, _prefix: :created_by

delegate :starts_at, :motif_name,
delegate :starts_at, :motif, :lieu, :collectif?, :by_phone?, :duration_in_min,
:rdv_solidarites_url, :rdv_solidarites_rdv_id, :instruction_for_rdv,
to: :rdv
delegate :department, :department_id, to: :organisation
Expand Down
1 change: 0 additions & 1 deletion app/models/rdv.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ class Rdv < ApplicationRecord
include Notificable
include RdvParticipationStatus
include WebhookDeliverable
include Rdv::FranceTravailWebhooks
include HasCurrentCategoryConfiguration

after_commit :notify_participations_to_users, on: :update, if: :should_notify_users?
Expand Down
3 changes: 0 additions & 3 deletions app/models/webhook_receipt.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,4 @@ class WebhookReceipt < ApplicationRecord
belongs_to :webhook_endpoint, optional: true

validates :resource_model, :resource_id, :timestamp, presence: true

# france travail webhooks are not linked to a webhook_endpoint
scope :france_travail, -> { where(webhook_endpoint_id: nil) }
end
Loading
Loading