api_particulier 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +23 -0
  3. data/LICENSE +21 -0
  4. data/README.md +66 -0
  5. data/lib/api_particulier/client.rb +76 -0
  6. data/lib/api_particulier/commons/auth/bearer_token.rb +21 -0
  7. data/lib/api_particulier/commons/auth/strategy.rb +13 -0
  8. data/lib/api_particulier/commons/client_base.rb +128 -0
  9. data/lib/api_particulier/commons/configuration.rb +101 -0
  10. data/lib/api_particulier/commons/errors.rb +84 -0
  11. data/lib/api_particulier/commons/middleware/authentication.rb +49 -0
  12. data/lib/api_particulier/commons/middleware/envelope.rb +31 -0
  13. data/lib/api_particulier/commons/middleware/error_handler.rb +106 -0
  14. data/lib/api_particulier/commons/middleware/logging.rb +70 -0
  15. data/lib/api_particulier/commons/middleware/rate_limit.rb +17 -0
  16. data/lib/api_particulier/commons/rate_limit.rb +54 -0
  17. data/lib/api_particulier/commons/response.rb +32 -0
  18. data/lib/api_particulier/commons/siren.rb +37 -0
  19. data/lib/api_particulier/commons/siret.rb +37 -0
  20. data/lib/api_particulier/commons/user_agent.rb +16 -0
  21. data/lib/api_particulier/commons/version.rb +7 -0
  22. data/lib/api_particulier/commons.rb +23 -0
  23. data/lib/api_particulier/resources/ants.rb +28 -0
  24. data/lib/api_particulier/resources/cnous.rb +65 -0
  25. data/lib/api_particulier/resources/dsnj.rb +42 -0
  26. data/lib/api_particulier/resources/dss.rb +238 -0
  27. data/lib/api_particulier/resources/france_travail.rb +42 -0
  28. data/lib/api_particulier/resources/gip_mds.rb +42 -0
  29. data/lib/api_particulier/resources/men.rb +34 -0
  30. data/lib/api_particulier/resources/mesri.rb +56 -0
  31. data/lib/api_particulier/resources/sdh.rb +28 -0
  32. data/lib/api_particulier/version.rb +3 -0
  33. data/lib/api_particulier.rb +4 -0
  34. metadata +107 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 69af0dca4424823c2fa2cbaba141f4ccde38d45309ce3f4a878e3ae6105728aa
4
+ data.tar.gz: 95b4443df163a996ca72416060d212b69fc2efc4dc00981699aa3d486e285993
5
+ SHA512:
6
+ metadata.gz: d5e1f0af171ee5808171f3e0d13b5cae816d0932422ba7eb5b083b148c339e31f2d9f513f0e874d71246c92062a463a2c9faac8c6d4143b9385685e7817da2b9
7
+ data.tar.gz: 6c98f422268ae3cecf8f9e56296a1b3df56e5fa499e5b31f541b0e6c1598e55587d8ac5ad963275c87da12f9dd4689c5e7afd7de966a7ad8514e6601cb046ba9
data/CHANGELOG.md ADDED
@@ -0,0 +1,23 @@
1
+ # Changelog
2
+
3
+ All notable changes to `api_particulier` (Ruby) are documented here.
4
+ Format follows [Keep a Changelog](https://keepachangelog.com/) and the project
5
+ adheres to [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Added
10
+ - Initial release — conforms to `clients/SPECS.md` §1–§20.
11
+ - `production` / `staging` environments with `base_url` override.
12
+ - `BearerToken` auth strategy with a pluggable `Auth::Strategy` seam.
13
+ - Client-level `default_params` with per-call override for `recipient`
14
+ (Particulier does not require `context` / `object`).
15
+ - Local SIRET / SIREN validation before any HTTP call.
16
+ - `Response` value object (`data`, `links`, `meta`, `raw`, `http_status`,
17
+ `headers`, `rate_limit`).
18
+ - Full JSON:API exception hierarchy matching `clients/SPECS.md` §6.
19
+ - `RateLimit-*` header parsing and `retry_after` on `RateLimitError`.
20
+ - Opt-in retry middleware via `faraday-retry`.
21
+ - 9 resource modules scaffolded from the OpenAPI spec, grouped by provider.
22
+ - Logging middleware redacts query strings by default (PII protection).
23
+ - `examples/{basic,error_handling,retry}.rb`.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DINUM (Direction Interministérielle du Numérique)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # api_particulier
2
+
3
+ Ruby client for [API Particulier v3](https://particulier.api.gouv.fr). Conforms
4
+ to [`clients/SPECS.md`](../../SPECS.md).
5
+
6
+ ## Installation
7
+
8
+ ```ruby
9
+ # Gemfile
10
+ gem 'api_particulier'
11
+ ```
12
+
13
+ ## Configuration
14
+
15
+ ```ruby
16
+ client = ApiParticulier::Client.new(
17
+ token: ENV['API_PARTICULIER_TOKEN'],
18
+ environment: :staging, # or :production (default)
19
+ default_params: { recipient: '13002526500013' }
20
+ )
21
+ ```
22
+
23
+ ENV vars: `API_PARTICULIER_TOKEN`, `API_PARTICULIER_ENV`,
24
+ `API_PARTICULIER_BASE_URL`.
25
+
26
+ ## Quickstart
27
+
28
+ ```ruby
29
+ response = client.ants.extrait_immatriculation_vehicule(immatriculation: 'AA-123-BB')
30
+ response.data # => { "titulaire" => { ... } }
31
+ response.rate_limit.remaining
32
+ ```
33
+
34
+ Low-level escape hatch: `client.get(path, params: {...})`.
35
+
36
+ ## Error handling
37
+
38
+ See the entreprise README and `clients/SPECS.md` §6 — same hierarchy applies
39
+ under `ApiParticulier::Commons::*`.
40
+
41
+ ## Testing
42
+
43
+ No embedded mock mode. Stub with WebMock:
44
+
45
+ ```ruby
46
+ stub_request(:get, %r{https://staging\.particulier\.api\.gouv\.fr/v3/ants/.+})
47
+ .with(headers: { 'Authorization' => 'Bearer test' })
48
+ .to_return(status: 200,
49
+ headers: { 'Content-Type' => 'application/json' },
50
+ body: { data: {}, links: {}, meta: {} }.to_json)
51
+ ```
52
+
53
+ PII notice: the default logger redacts the query string on Particulier
54
+ requests, since query params carry personal data (names, DOB, INE).
55
+
56
+ ## Development
57
+
58
+ Shared code comes from `clients/ruby/commons/`; resources are scaffolded from
59
+ the OpenAPI spec:
60
+
61
+ ```sh
62
+ clients/ruby/bin/sync_commons
63
+ clients/ruby/bin/scaffold_resources --api particulier
64
+ ```
65
+
66
+ CI checks both are in sync.
@@ -0,0 +1,76 @@
1
+ require_relative 'commons'
2
+
3
+ # <scaffold:requires:begin>
4
+ require_relative 'resources/ants'
5
+ require_relative 'resources/cnous'
6
+ require_relative 'resources/dsnj'
7
+ require_relative 'resources/dss'
8
+ require_relative 'resources/france_travail'
9
+ require_relative 'resources/gip_mds'
10
+ require_relative 'resources/men'
11
+ require_relative 'resources/mesri'
12
+ require_relative 'resources/sdh'
13
+ # <scaffold:requires:end>
14
+
15
+ module ApiParticulier
16
+ BASE_URLS = {
17
+ Commons::Configuration::PRODUCTION => 'https://particulier.api.gouv.fr',
18
+ Commons::Configuration::STAGING => 'https://staging.particulier.api.gouv.fr'
19
+ }.freeze
20
+
21
+ class Client < Commons::ClientBase
22
+ REQUIRED_PARAMS = %i[recipient].freeze
23
+ SIRET_PARAMS = %i[recipient].freeze
24
+
25
+ def initialize(token: nil, environment: nil, default_params: {}, base_url: nil, auth_strategy: nil, **opts)
26
+ env_token = token || ENV.fetch('API_PARTICULIER_TOKEN', nil)
27
+ env_env = (environment || ENV.fetch('API_PARTICULIER_ENV', :production)).to_sym
28
+
29
+ config = Commons::Configuration.new(
30
+ base_urls: BASE_URLS,
31
+ token: env_token,
32
+ auth_strategy: auth_strategy,
33
+ environment: env_env,
34
+ base_url: base_url || ENV.fetch('API_PARTICULIER_BASE_URL', nil),
35
+ default_params: default_params,
36
+ user_agent: opts[:user_agent] || Commons::UserAgent.build(product: 'api-particulier-ruby', version: VERSION),
37
+ open_timeout: opts[:open_timeout] || Commons::Configuration::DEFAULT_OPEN_TIMEOUT,
38
+ read_timeout: opts[:read_timeout] || Commons::Configuration::DEFAULT_READ_TIMEOUT,
39
+ retry: opts[:retry],
40
+ logger: opts[:logger],
41
+ adapter: opts[:adapter]
42
+ )
43
+ super(config, product: :particulier)
44
+ end
45
+
46
+ # <scaffold:resources:begin>
47
+ def ants
48
+ @ants ||= Resources::Ants.new(self)
49
+ end
50
+ def cnous
51
+ @cnous ||= Resources::Cnous.new(self)
52
+ end
53
+ def dsnj
54
+ @dsnj ||= Resources::Dsnj.new(self)
55
+ end
56
+ def dss
57
+ @dss ||= Resources::Dss.new(self)
58
+ end
59
+ def france_travail
60
+ @france_travail ||= Resources::FranceTravail.new(self)
61
+ end
62
+ def gip_mds
63
+ @gip_mds ||= Resources::GipMds.new(self)
64
+ end
65
+ def men
66
+ @men ||= Resources::Men.new(self)
67
+ end
68
+ def mesri
69
+ @mesri ||= Resources::Mesri.new(self)
70
+ end
71
+ def sdh
72
+ @sdh ||= Resources::Sdh.new(self)
73
+ end
74
+ # <scaffold:resources:end>
75
+ end
76
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ # DO NOT EDIT — generated from clients/ruby/commons/ (source digest: 903f3b3aca1a59a6cb1a53ab98f72c365486fc1f).
3
+ # Regenerate via clients/ruby/bin/sync_commons.
4
+
5
+ require_relative 'strategy'
6
+
7
+ module ApiParticulier::Commons
8
+ module Auth
9
+ class BearerToken < Strategy
10
+ def initialize(token)
11
+ raise ArgumentError, 'token must be a non-empty string' if token.nil? || token.to_s.strip.empty?
12
+
13
+ @token = token.to_s
14
+ end
15
+
16
+ def apply(request)
17
+ request.headers['Authorization'] = "Bearer #{@token}"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ # DO NOT EDIT — generated from clients/ruby/commons/ (source digest: 903f3b3aca1a59a6cb1a53ab98f72c365486fc1f).
3
+ # Regenerate via clients/ruby/bin/sync_commons.
4
+
5
+ module ApiParticulier::Commons
6
+ module Auth
7
+ class Strategy
8
+ def apply(request)
9
+ raise NotImplementedError, "#{self.class} must implement #apply(request)"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+ # DO NOT EDIT — generated from clients/ruby/commons/ (source digest: 903f3b3aca1a59a6cb1a53ab98f72c365486fc1f).
3
+ # Regenerate via clients/ruby/bin/sync_commons.
4
+
5
+ require 'faraday'
6
+ begin
7
+ require 'faraday/retry'
8
+ rescue LoadError
9
+ # faraday-retry is optional; the :retry middleware is only used when the
10
+ # consumer opts in.
11
+ end
12
+
13
+ require_relative 'middleware/authentication'
14
+ require_relative 'middleware/logging'
15
+ require_relative 'middleware/rate_limit'
16
+ require_relative 'middleware/error_handler'
17
+ require_relative 'middleware/envelope'
18
+ require_relative 'response'
19
+ require_relative 'siret'
20
+ require_relative 'errors'
21
+
22
+ module ApiParticulier::Commons
23
+ class ClientBase
24
+ attr_reader :configuration
25
+
26
+ REQUIRED_PARAMS = %i[recipient context object].freeze
27
+ SIRET_PARAMS = %i[recipient].freeze
28
+
29
+ def initialize(configuration, product:)
30
+ @configuration = configuration
31
+ @product = product
32
+ @connection = build_connection
33
+ end
34
+
35
+ def get(path, params: {}, headers: {})
36
+ merged = merge_params(params)
37
+ validate_required!(merged)
38
+ validate_sirets!(merged)
39
+
40
+ response = @connection.get(path, clean(merged), headers)
41
+ build_response(response)
42
+ end
43
+
44
+ private
45
+
46
+ def merge_params(params)
47
+ defaults = @configuration.default_params.transform_keys(&:to_s)
48
+ defaults.merge((params || {}).transform_keys(&:to_s))
49
+ end
50
+
51
+ def required_params_for(_params)
52
+ self.class::REQUIRED_PARAMS
53
+ end
54
+
55
+ def siret_params_for(_params)
56
+ self.class::SIRET_PARAMS
57
+ end
58
+
59
+ def validate_required!(params)
60
+ required_params_for(params).each do |key|
61
+ next unless blank?(params[key.to_s])
62
+
63
+ raise MissingParameterError, "required parameter #{key.inspect} is missing"
64
+ end
65
+ end
66
+
67
+ def validate_sirets!(params)
68
+ siret_params_for(params).each do |key|
69
+ value = params[key.to_s]
70
+ next if value.nil?
71
+
72
+ Siret.validate!(value, parameter: key)
73
+ end
74
+ end
75
+
76
+ def blank?(value)
77
+ value.nil? || (value.respond_to?(:empty?) && value.empty?)
78
+ end
79
+
80
+ def clean(params)
81
+ params.reject { |_, v| v.nil? }
82
+ end
83
+
84
+ def build_response(response)
85
+ Response.new(
86
+ raw: response.body,
87
+ http_status: response.status,
88
+ headers: response.headers,
89
+ rate_limit: response.env[Middleware::RateLimitParser::ENV_KEY]
90
+ )
91
+ end
92
+
93
+ def build_connection
94
+ cfg = @configuration
95
+ Faraday.new(url: cfg.base_url) do |conn|
96
+ conn.options.open_timeout = cfg.open_timeout
97
+ conn.options.timeout = cfg.read_timeout
98
+
99
+ conn.headers['User-Agent'] = cfg.user_agent if cfg.user_agent
100
+ conn.headers['Accept'] = 'application/json'
101
+
102
+ if cfg.retry && defined?(Faraday::Retry)
103
+ conn.request :retry,
104
+ max: cfg.retry.fetch(:max, 2),
105
+ retry_statuses: cfg.retry.fetch(:on_status, [429, 502, 503]),
106
+ methods: %i[get],
107
+ interval: cfg.retry.fetch(:interval, 0.5),
108
+ backoff_factor: cfg.retry.fetch(:backoff_factor, 2),
109
+ exceptions: [
110
+ ApiParticulier::Commons::RateLimitError,
111
+ ApiParticulier::Commons::ProviderError,
112
+ ApiParticulier::Commons::ProviderUnavailableError,
113
+ ApiParticulier::Commons::TransportError
114
+ ]
115
+ end
116
+
117
+ conn.use Middleware::Authentication, auth_strategy: cfg.auth_strategy
118
+ conn.use Middleware::Logging, logger: cfg.logger, redact_query: @product == :particulier
119
+
120
+ conn.use Middleware::RateLimitParser
121
+ conn.use Middleware::ErrorHandler
122
+ conn.use Middleware::Envelope
123
+
124
+ conn.adapter(cfg.adapter || Faraday.default_adapter)
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+ # DO NOT EDIT — generated from clients/ruby/commons/ (source digest: 903f3b3aca1a59a6cb1a53ab98f72c365486fc1f).
3
+ # Regenerate via clients/ruby/bin/sync_commons.
4
+
5
+ require_relative 'auth/bearer_token'
6
+
7
+ module ApiParticulier::Commons
8
+ class Configuration
9
+ PRODUCTION = :production
10
+ STAGING = :staging
11
+ ENVIRONMENTS = [PRODUCTION, STAGING].freeze
12
+
13
+ DEFAULT_OPEN_TIMEOUT = 5
14
+ DEFAULT_READ_TIMEOUT = 30
15
+
16
+ attr_reader :base_url,
17
+ :environment,
18
+ :auth_strategy,
19
+ :default_params,
20
+ :open_timeout,
21
+ :read_timeout,
22
+ :retry,
23
+ :logger,
24
+ :user_agent,
25
+ :adapter
26
+
27
+ def initialize(
28
+ base_urls:,
29
+ token: nil,
30
+ auth_strategy: nil,
31
+ environment: PRODUCTION,
32
+ base_url: nil,
33
+ default_params: {},
34
+ open_timeout: DEFAULT_OPEN_TIMEOUT,
35
+ read_timeout: DEFAULT_READ_TIMEOUT,
36
+ retry: nil,
37
+ logger: nil,
38
+ user_agent: nil,
39
+ adapter: nil
40
+ )
41
+ @base_urls = base_urls
42
+ resolved_env = resolve_environment(environment)
43
+ @environment = resolved_env
44
+ @explicit_base_url = !base_url.nil?
45
+ @base_url = base_url || base_urls.fetch(resolved_env)
46
+ @auth_strategy = auth_strategy || build_bearer_strategy(token)
47
+ @default_params = default_params.freeze
48
+ @open_timeout = open_timeout
49
+ @read_timeout = read_timeout
50
+ @retry = binding.local_variable_get(:retry)
51
+ @logger = logger
52
+ @user_agent = user_agent
53
+ @adapter = adapter
54
+ freeze
55
+ end
56
+
57
+ def with(**overrides)
58
+ self.class.new(**current_attrs.merge(overrides))
59
+ end
60
+ alias copy with
61
+
62
+ def production?
63
+ environment == PRODUCTION
64
+ end
65
+
66
+ def staging?
67
+ environment == STAGING
68
+ end
69
+
70
+ private
71
+
72
+ def current_attrs
73
+ {
74
+ base_urls: @base_urls,
75
+ auth_strategy: auth_strategy,
76
+ environment: environment,
77
+ base_url: @explicit_base_url ? base_url : nil,
78
+ default_params: default_params,
79
+ open_timeout: open_timeout,
80
+ read_timeout: read_timeout,
81
+ retry: @retry,
82
+ logger: logger,
83
+ user_agent: user_agent,
84
+ adapter: adapter
85
+ }
86
+ end
87
+
88
+ def resolve_environment(value)
89
+ env = value.to_sym
90
+ return env if ENVIRONMENTS.include?(env)
91
+
92
+ raise ArgumentError, "environment must be one of #{ENVIRONMENTS.inspect}; got #{value.inspect}"
93
+ end
94
+
95
+ def build_bearer_strategy(token)
96
+ return nil if token.nil?
97
+
98
+ Auth::BearerToken.new(token)
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+ # DO NOT EDIT — generated from clients/ruby/commons/ (source digest: 903f3b3aca1a59a6cb1a53ab98f72c365486fc1f).
3
+ # Regenerate via clients/ruby/bin/sync_commons.
4
+
5
+ module ApiParticulier::Commons
6
+ class Error < StandardError
7
+ attr_reader :http_status, :errors, :method, :url
8
+
9
+ def initialize(message = nil, http_status: nil, errors: [], method: nil, url: nil)
10
+ super(message || default_message(http_status, errors))
11
+ @http_status = http_status
12
+ @errors = errors || []
13
+ @method = method
14
+ @url = url
15
+ end
16
+
17
+ def first_error
18
+ errors.first || {}
19
+ end
20
+
21
+ def first_error_code
22
+ first_error['code'] || first_error[:code]
23
+ end
24
+
25
+ def first_error_title
26
+ first_error['title'] || first_error[:title]
27
+ end
28
+
29
+ def first_error_detail
30
+ first_error['detail'] || first_error[:detail]
31
+ end
32
+
33
+ def first_error_source
34
+ first_error['source'] || first_error[:source]
35
+ end
36
+
37
+ def first_error_meta
38
+ first_error['meta'] || first_error[:meta] || {}
39
+ end
40
+
41
+ private
42
+
43
+ def default_message(http_status, errors)
44
+ first = (errors || []).first || {}
45
+ title = first['title'] || first[:title]
46
+ detail = first['detail'] || first[:detail]
47
+ parts = [http_status, title, detail].compact
48
+ parts.empty? ? self.class.name : parts.join(' — ')
49
+ end
50
+ end
51
+
52
+ class ClientError < Error; end
53
+ class AuthenticationError < ClientError; end
54
+ class AuthorizationError < ClientError; end
55
+ class NotFoundError < ClientError; end
56
+ class ConflictError < ClientError; end
57
+ class ValidationError < ClientError; end
58
+
59
+ class RateLimitError < ClientError
60
+ attr_reader :retry_after
61
+
62
+ def initialize(message = nil, retry_after: nil, **kwargs)
63
+ super(message, **kwargs)
64
+ @retry_after = retry_after
65
+ end
66
+ end
67
+
68
+ class ServerError < Error; end
69
+ class ProviderError < ServerError
70
+ attr_reader :retry_after
71
+
72
+ def initialize(message = nil, retry_after: nil, **kwargs)
73
+ super(message, **kwargs)
74
+ @retry_after = retry_after
75
+ end
76
+ end
77
+ class ProviderUnavailableError < ServerError; end
78
+
79
+ class TransportError < Error; end
80
+
81
+ class InvalidSiretError < ArgumentError; end
82
+ class InvalidSirenError < ArgumentError; end
83
+ class MissingParameterError < ArgumentError; end
84
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+ # DO NOT EDIT — generated from clients/ruby/commons/ (source digest: 903f3b3aca1a59a6cb1a53ab98f72c365486fc1f).
3
+ # Regenerate via clients/ruby/bin/sync_commons.
4
+
5
+ require 'faraday'
6
+
7
+ module ApiParticulier::Commons
8
+ module Middleware
9
+ class Authentication < Faraday::Middleware
10
+ def initialize(app, auth_strategy:)
11
+ super(app)
12
+ @auth_strategy = auth_strategy
13
+ end
14
+
15
+ def on_request(env)
16
+ return if @auth_strategy.nil?
17
+
18
+ request = RequestWrapper.new(env)
19
+ begin
20
+ @auth_strategy.apply(request)
21
+ rescue StandardError => e
22
+ raise ApiParticulier::Commons::AuthenticationError.new(
23
+ "auth strategy raised: #{e.message}",
24
+ method: env.method,
25
+ url: env.url.to_s
26
+ )
27
+ end
28
+ end
29
+
30
+ class RequestWrapper
31
+ def initialize(env)
32
+ @env = env
33
+ end
34
+
35
+ def headers
36
+ @env.request_headers
37
+ end
38
+
39
+ def method
40
+ @env.method
41
+ end
42
+
43
+ def url
44
+ @env.url
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ # DO NOT EDIT — generated from clients/ruby/commons/ (source digest: 903f3b3aca1a59a6cb1a53ab98f72c365486fc1f).
3
+ # Regenerate via clients/ruby/bin/sync_commons.
4
+
5
+ require 'faraday'
6
+ require 'json'
7
+
8
+ module ApiParticulier::Commons
9
+ module Middleware
10
+ class Envelope < Faraday::Middleware
11
+ def on_complete(env)
12
+ body = env.body
13
+ return if body.nil? || body.is_a?(Hash) || body.is_a?(Array)
14
+ return unless body.is_a?(String) && !body.empty?
15
+
16
+ parsed =
17
+ begin
18
+ JSON.parse(body)
19
+ rescue JSON::ParserError
20
+ raise ApiParticulier::Commons::TransportError.new(
21
+ "invalid JSON body: #{body[0, 200]}",
22
+ method: env.method,
23
+ url: env.url.to_s
24
+ )
25
+ end
26
+
27
+ env.body = parsed
28
+ end
29
+ end
30
+ end
31
+ end