lhc 12.0.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.
- checksums.yaml +7 -0
- data/.gitignore +37 -0
- data/.rubocop.localch.yml +325 -0
- data/.rubocop.yml +61 -0
- data/.ruby-version +1 -0
- data/Gemfile +13 -0
- data/Gemfile.activesupport4 +4 -0
- data/Gemfile.activesupport5 +4 -0
- data/Gemfile.activesupport6 +4 -0
- data/LICENSE +674 -0
- data/README.md +984 -0
- data/Rakefile +25 -0
- data/cider-ci.yml +6 -0
- data/cider-ci/bin/bundle +51 -0
- data/cider-ci/bin/ruby_install +8 -0
- data/cider-ci/bin/ruby_version +25 -0
- data/cider-ci/jobs/rspec-activesupport-4.yml +28 -0
- data/cider-ci/jobs/rspec-activesupport-5.yml +27 -0
- data/cider-ci/jobs/rspec-activesupport-6.yml +28 -0
- data/cider-ci/jobs/rubocop.yml +18 -0
- data/cider-ci/task_components/bundle.yml +22 -0
- data/cider-ci/task_components/rspec.yml +36 -0
- data/cider-ci/task_components/rubocop.yml +29 -0
- data/cider-ci/task_components/ruby.yml +15 -0
- data/friday.yml +3 -0
- data/lhc.gemspec +39 -0
- data/lib/core_ext/hash/deep_transform_values.rb +48 -0
- data/lib/lhc.rb +136 -0
- data/lib/lhc/concerns/lhc/basic_methods_concern.rb +42 -0
- data/lib/lhc/concerns/lhc/configuration_concern.rb +20 -0
- data/lib/lhc/concerns/lhc/fix_invalid_encoding_concern.rb +42 -0
- data/lib/lhc/concerns/lhc/formats_concern.rb +25 -0
- data/lib/lhc/concerns/lhc/request/user_agent_concern.rb +25 -0
- data/lib/lhc/config.rb +47 -0
- data/lib/lhc/endpoint.rb +119 -0
- data/lib/lhc/error.rb +80 -0
- data/lib/lhc/errors/client_error.rb +73 -0
- data/lib/lhc/errors/parser_error.rb +4 -0
- data/lib/lhc/errors/server_error.rb +28 -0
- data/lib/lhc/errors/timeout.rb +4 -0
- data/lib/lhc/errors/unknown_error.rb +4 -0
- data/lib/lhc/format.rb +18 -0
- data/lib/lhc/formats.rb +8 -0
- data/lib/lhc/formats/form.rb +45 -0
- data/lib/lhc/formats/json.rb +55 -0
- data/lib/lhc/formats/multipart.rb +45 -0
- data/lib/lhc/formats/plain.rb +42 -0
- data/lib/lhc/interceptor.rb +32 -0
- data/lib/lhc/interceptors.rb +26 -0
- data/lib/lhc/interceptors/auth.rb +98 -0
- data/lib/lhc/interceptors/caching.rb +127 -0
- data/lib/lhc/interceptors/default_timeout.rb +16 -0
- data/lib/lhc/interceptors/logging.rb +37 -0
- data/lib/lhc/interceptors/monitoring.rb +63 -0
- data/lib/lhc/interceptors/prometheus.rb +51 -0
- data/lib/lhc/interceptors/retry.rb +41 -0
- data/lib/lhc/interceptors/rollbar.rb +36 -0
- data/lib/lhc/interceptors/throttle.rb +81 -0
- data/lib/lhc/interceptors/zipkin.rb +110 -0
- data/lib/lhc/railtie.rb +10 -0
- data/lib/lhc/request.rb +157 -0
- data/lib/lhc/response.rb +60 -0
- data/lib/lhc/response/data.rb +28 -0
- data/lib/lhc/response/data/base.rb +22 -0
- data/lib/lhc/response/data/collection.rb +16 -0
- data/lib/lhc/response/data/item.rb +29 -0
- data/lib/lhc/rspec.rb +12 -0
- data/lib/lhc/test/cache_helper.rb +3 -0
- data/lib/lhc/version.rb +5 -0
- data/script/ci/build.sh +19 -0
- data/spec/basic_methods/delete_spec.rb +34 -0
- data/spec/basic_methods/get_spec.rb +49 -0
- data/spec/basic_methods/post_spec.rb +42 -0
- data/spec/basic_methods/put_spec.rb +48 -0
- data/spec/basic_methods/request_spec.rb +19 -0
- data/spec/basic_methods/request_without_rails_spec.rb +29 -0
- data/spec/config/endpoints_spec.rb +63 -0
- data/spec/config/placeholders_spec.rb +32 -0
- data/spec/core_ext/hash/deep_transform_values_spec.rb +24 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +8 -0
- data/spec/dummy/app/assets/config/manifest.js +3 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +7 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/helpers/application_helper.rb +4 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +5 -0
- data/spec/dummy/bin/rails +6 -0
- data/spec/dummy/bin/rake +6 -0
- data/spec/dummy/config.ru +6 -0
- data/spec/dummy/config/application.rb +16 -0
- data/spec/dummy/config/boot.rb +7 -0
- data/spec/dummy/config/environment.rb +7 -0
- data/spec/dummy/config/environments/development.rb +36 -0
- data/spec/dummy/config/environments/production.rb +77 -0
- data/spec/dummy/config/environments/test.rb +41 -0
- data/spec/dummy/config/initializers/assets.rb +10 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +9 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
- data/spec/dummy/config/initializers/inflections.rb +18 -0
- data/spec/dummy/config/initializers/mime_types.rb +6 -0
- data/spec/dummy/config/initializers/session_store.rb +5 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +11 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/endpoint/compile_spec.rb +35 -0
- data/spec/endpoint/match_spec.rb +41 -0
- data/spec/endpoint/placeholders_spec.rb +30 -0
- data/spec/endpoint/remove_interpolated_params_spec.rb +17 -0
- data/spec/endpoint/values_as_params_spec.rb +31 -0
- data/spec/error/dup_spec.rb +12 -0
- data/spec/error/find_spec.rb +57 -0
- data/spec/error/response_spec.rb +17 -0
- data/spec/error/timeout_spec.rb +14 -0
- data/spec/error/to_s_spec.rb +80 -0
- data/spec/formats/form_spec.rb +27 -0
- data/spec/formats/json_spec.rb +66 -0
- data/spec/formats/multipart_spec.rb +26 -0
- data/spec/formats/plain_spec.rb +29 -0
- data/spec/interceptors/after_request_spec.rb +20 -0
- data/spec/interceptors/after_response_spec.rb +39 -0
- data/spec/interceptors/auth/basic_auth_spec.rb +17 -0
- data/spec/interceptors/auth/bearer_spec.rb +19 -0
- data/spec/interceptors/auth/reauthentication_configuration_spec.rb +61 -0
- data/spec/interceptors/auth/reauthentication_spec.rb +44 -0
- data/spec/interceptors/before_request_spec.rb +21 -0
- data/spec/interceptors/before_response_spec.rb +20 -0
- data/spec/interceptors/caching/hydra_spec.rb +26 -0
- data/spec/interceptors/caching/main_spec.rb +73 -0
- data/spec/interceptors/caching/methods_spec.rb +42 -0
- data/spec/interceptors/caching/options_spec.rb +89 -0
- data/spec/interceptors/caching/parameters_spec.rb +24 -0
- data/spec/interceptors/caching/response_status_spec.rb +29 -0
- data/spec/interceptors/caching/to_cache_spec.rb +16 -0
- data/spec/interceptors/default_interceptors_spec.rb +15 -0
- data/spec/interceptors/default_timeout/main_spec.rb +34 -0
- data/spec/interceptors/define_spec.rb +29 -0
- data/spec/interceptors/dup_spec.rb +19 -0
- data/spec/interceptors/logging/main_spec.rb +37 -0
- data/spec/interceptors/monitoring/main_spec.rb +97 -0
- data/spec/interceptors/prometheus_spec.rb +54 -0
- data/spec/interceptors/response_competition_spec.rb +41 -0
- data/spec/interceptors/retry/main_spec.rb +73 -0
- data/spec/interceptors/return_response_spec.rb +38 -0
- data/spec/interceptors/rollbar/invalid_encoding_spec.rb +43 -0
- data/spec/interceptors/rollbar/main_spec.rb +57 -0
- data/spec/interceptors/throttle/main_spec.rb +106 -0
- data/spec/interceptors/throttle/reset_track_spec.rb +53 -0
- data/spec/interceptors/zipkin/distributed_tracing_spec.rb +135 -0
- data/spec/rails_helper.rb +6 -0
- data/spec/request/body_spec.rb +39 -0
- data/spec/request/encoding_spec.rb +37 -0
- data/spec/request/error_handling_spec.rb +88 -0
- data/spec/request/headers_spec.rb +12 -0
- data/spec/request/ignore_errors_spec.rb +73 -0
- data/spec/request/option_dup_spec.rb +13 -0
- data/spec/request/parallel_requests_spec.rb +59 -0
- data/spec/request/params_encoding_spec.rb +26 -0
- data/spec/request/request_without_rails_spec.rb +15 -0
- data/spec/request/url_patterns_spec.rb +54 -0
- data/spec/request/user_agent_spec.rb +26 -0
- data/spec/request/user_agent_without_rails_spec.rb +27 -0
- data/spec/response/body_spec.rb +16 -0
- data/spec/response/code_spec.rb +16 -0
- data/spec/response/data_accessor_spec.rb +29 -0
- data/spec/response/data_spec.rb +61 -0
- data/spec/response/effective_url_spec.rb +16 -0
- data/spec/response/headers_spec.rb +18 -0
- data/spec/response/options_spec.rb +18 -0
- data/spec/response/success_spec.rb +13 -0
- data/spec/response/time_spec.rb +21 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/support/fixtures/json/feedback.json +11 -0
- data/spec/support/fixtures/json/feedbacks.json +164 -0
- data/spec/support/fixtures/json/localina_content_ad.json +23 -0
- data/spec/support/load_json.rb +5 -0
- data/spec/support/reset_config.rb +7 -0
- data/spec/support/zipkin_mock.rb +113 -0
- data/spec/timeouts/no_signal_spec.rb +13 -0
- data/spec/timeouts/timings_spec.rb +55 -0
- metadata +534 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class LHC::ServerError < LHC::Error
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
class LHC::InternalServerError < LHC::ServerError
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class LHC::NotImplemented < LHC::ServerError
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class LHC::BadGateway < LHC::ServerError
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class LHC::ServiceUnavailable < LHC::ServerError
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class LHC::GatewayTimeout < LHC::ServerError
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class LHC::HttpVersionNotSupported < LHC::ServerError
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class LHC::InsufficientStorage < LHC::ServerError
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class LHC::NotExtended < LHC::ServerError
|
|
28
|
+
end
|
data/lib/lhc/format.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class LHC::Format
|
|
4
|
+
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def no_content_type_header!(options)
|
|
8
|
+
return if (options[:headers].keys & [:'Content-Type', 'Content-Type']).blank?
|
|
9
|
+
|
|
10
|
+
raise 'Content-Type header is not allowed for formatted requests!\nSee https://github.com/local-ch/lhc#formats for more information.'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def no_accept_header!(options)
|
|
14
|
+
return if (options[:headers].keys & [:Accept, 'Accept']).blank?
|
|
15
|
+
|
|
16
|
+
raise 'Accept header is not allowed for formatted requests!\nSee https://github.com/local-ch/lhc#formats for more information.'
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/lhc/formats.rb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LHC::Formats
|
|
4
|
+
class Form < LHC::Format
|
|
5
|
+
include LHC::BasicMethodsConcern
|
|
6
|
+
|
|
7
|
+
def self.request(options)
|
|
8
|
+
options[:format] = new
|
|
9
|
+
super(options)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def format_options(options)
|
|
13
|
+
options[:headers] ||= {}
|
|
14
|
+
no_content_type_header!(options)
|
|
15
|
+
options[:headers]['Content-Type'] = 'application/x-www-form-urlencoded'
|
|
16
|
+
options
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def as_json(input)
|
|
20
|
+
parse(input)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def as_open_struct(input)
|
|
24
|
+
parse(input)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def to_body(input)
|
|
28
|
+
input
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_s
|
|
32
|
+
'form'
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def to_sym
|
|
36
|
+
to_s.to_sym
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def parse(input)
|
|
42
|
+
input
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LHC::Formats
|
|
4
|
+
class JSON < LHC::Format
|
|
5
|
+
include LHC::BasicMethodsConcern
|
|
6
|
+
|
|
7
|
+
def self.request(options)
|
|
8
|
+
options[:format] = new
|
|
9
|
+
super(options)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def format_options(options)
|
|
13
|
+
options[:headers] ||= {}
|
|
14
|
+
no_content_type_header!(options)
|
|
15
|
+
no_accept_header!(options)
|
|
16
|
+
|
|
17
|
+
options[:headers]['Content-Type'] = 'application/json; charset=utf-8'
|
|
18
|
+
options[:headers]['Accept'] = 'application/json,application/vnd.api+json'
|
|
19
|
+
options[:headers]['Accept-Charset'] = 'utf-8'
|
|
20
|
+
options
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def as_json(input)
|
|
24
|
+
parse(input, Hash)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def as_open_struct(input)
|
|
28
|
+
parse(input, OpenStruct)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_body(input)
|
|
32
|
+
if input.is_a?(String)
|
|
33
|
+
input
|
|
34
|
+
else
|
|
35
|
+
input.to_json
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def to_s
|
|
40
|
+
'json'
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def to_sym
|
|
44
|
+
to_s.to_sym
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def parse(input, object_class)
|
|
50
|
+
::JSON.parse(input, object_class: object_class)
|
|
51
|
+
rescue ::JSON::ParserError => e
|
|
52
|
+
raise LHC::ParserError.new(e.message, input)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LHC::Formats
|
|
4
|
+
class Multipart < LHC::Format
|
|
5
|
+
include LHC::BasicMethodsConcern
|
|
6
|
+
|
|
7
|
+
def self.request(options)
|
|
8
|
+
options[:format] = new
|
|
9
|
+
super(options)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def format_options(options)
|
|
13
|
+
options[:headers] ||= {}
|
|
14
|
+
no_content_type_header!(options)
|
|
15
|
+
options[:headers]['Content-Type'] = 'multipart/form-data'
|
|
16
|
+
options
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def as_json(input)
|
|
20
|
+
parse(input)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def as_open_struct(input)
|
|
24
|
+
parse(input)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def to_body(input)
|
|
28
|
+
input
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_s
|
|
32
|
+
'multipart'
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def to_sym
|
|
36
|
+
to_s.to_sym
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def parse(input)
|
|
42
|
+
input
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LHC::Formats
|
|
4
|
+
class Plain < LHC::Format
|
|
5
|
+
include LHC::BasicMethodsConcern
|
|
6
|
+
|
|
7
|
+
def self.request(options)
|
|
8
|
+
options[:format] = new
|
|
9
|
+
super(options)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def format_options(options)
|
|
13
|
+
options
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def as_json(input)
|
|
17
|
+
parse(input)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def as_open_struct(input)
|
|
21
|
+
parse(input)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_body(input)
|
|
25
|
+
input
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def to_s
|
|
29
|
+
'plain'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def to_sym
|
|
33
|
+
to_s.to_sym
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def parse(input)
|
|
39
|
+
input
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class LHC::Interceptor
|
|
4
|
+
|
|
5
|
+
attr_reader :request
|
|
6
|
+
|
|
7
|
+
def initialize(request)
|
|
8
|
+
@request = request
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def response
|
|
12
|
+
@request.response
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def before_raw_request; end
|
|
16
|
+
|
|
17
|
+
def before_request; end
|
|
18
|
+
|
|
19
|
+
def after_request; end
|
|
20
|
+
|
|
21
|
+
def before_response; end
|
|
22
|
+
|
|
23
|
+
def after_response; end
|
|
24
|
+
|
|
25
|
+
# Prevent Interceptors from beeing duplicated!
|
|
26
|
+
# Their classes have flag-character.
|
|
27
|
+
# When duplicated you can't check for their class name anymore:
|
|
28
|
+
# e.g. options.deep_dup[:interceptors].include?(LHC::Caching) # false
|
|
29
|
+
def self.dup
|
|
30
|
+
self
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Handles interceptions during the lifecycle of a request
|
|
4
|
+
# Represents all active interceptors for a request/response.
|
|
5
|
+
class LHC::Interceptors
|
|
6
|
+
|
|
7
|
+
attr_accessor :all
|
|
8
|
+
|
|
9
|
+
# Intitalizes and determines if global or local interceptors are used
|
|
10
|
+
def initialize(request)
|
|
11
|
+
self.all = (request.options[:interceptors] || LHC.config.interceptors).map do |interceptor|
|
|
12
|
+
interceptor.new(request)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Forwards messages to interceptors and handles provided responses.
|
|
17
|
+
def intercept(name)
|
|
18
|
+
all.each do |interceptor|
|
|
19
|
+
result = interceptor.send(name)
|
|
20
|
+
if result.is_a? LHC::Response
|
|
21
|
+
raise 'Response already set from another interceptor' if @response
|
|
22
|
+
@response = interceptor.request.response = result
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class LHC::Auth < LHC::Interceptor
|
|
4
|
+
include ActiveSupport::Configurable
|
|
5
|
+
config_accessor :refresh_client_token
|
|
6
|
+
|
|
7
|
+
def before_request
|
|
8
|
+
authenticate!
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def after_response
|
|
12
|
+
return unless configuration_correct?
|
|
13
|
+
return unless reauthenticate?
|
|
14
|
+
reauthenticate!
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def authenticate!
|
|
20
|
+
if auth_options[:bearer]
|
|
21
|
+
bearer_authentication!
|
|
22
|
+
elsif auth_options[:basic]
|
|
23
|
+
basic_authentication!
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def basic_authentication!
|
|
28
|
+
auth = auth_options[:basic]
|
|
29
|
+
credentials = "#{auth[:username]}:#{auth[:password]}"
|
|
30
|
+
set_authorization_header("Basic #{Base64.encode64(credentials).chomp}")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def bearer_authentication!
|
|
34
|
+
token = auth_options[:bearer]
|
|
35
|
+
token = token.call if token.is_a?(Proc)
|
|
36
|
+
set_bearer_authorization_header(token)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# rubocop:disable Style/AccessorMethodName
|
|
40
|
+
def set_authorization_header(value)
|
|
41
|
+
request.headers['Authorization'] = value
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def set_bearer_authorization_header(token)
|
|
45
|
+
set_authorization_header("Bearer #{token}")
|
|
46
|
+
end
|
|
47
|
+
# rubocop:enable Style/AccessorMethodName
|
|
48
|
+
|
|
49
|
+
def reauthenticate!
|
|
50
|
+
# refresh token and update header
|
|
51
|
+
token = refresh_client_token_option.call
|
|
52
|
+
set_bearer_authorization_header(token)
|
|
53
|
+
# trigger LHC::Retry and ensure we do not trigger reauthenticate!
|
|
54
|
+
# again should it fail another time
|
|
55
|
+
new_options = request.options.dup
|
|
56
|
+
new_options = new_options.merge(retry: { max: 1 })
|
|
57
|
+
new_options = new_options.merge(auth: { reauthenticated: true })
|
|
58
|
+
request.options = new_options
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def reauthenticate?
|
|
62
|
+
!response.success? &&
|
|
63
|
+
!auth_options[:reauthenticated] &&
|
|
64
|
+
bearer_header_present? &&
|
|
65
|
+
LHC::Error.find(response) == LHC::Unauthorized
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def bearer_header_present?
|
|
69
|
+
@has_bearer_header ||= request.headers['Authorization'] =~ /^Bearer .+$/i
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def refresh_client_token_option
|
|
73
|
+
@refresh_client_token_option ||= auth_options[:refresh_client_token] || refresh_client_token
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def all_interceptor_classes
|
|
77
|
+
@all_interceptors ||= LHC::Interceptors.new(request).all.map(&:class)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def auth_options
|
|
81
|
+
@auth_options ||= request.options[:auth].dup || {}
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def configuration_correct?
|
|
85
|
+
# warn user about configs, only if refresh_client_token_option is set at all
|
|
86
|
+
refresh_client_token_option && refresh_client_token? && retry_interceptor?
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def refresh_client_token?
|
|
90
|
+
return true if refresh_client_token_option.is_a?(Proc)
|
|
91
|
+
warn("[WARNING] The given refresh_client_token must be a Proc for reauthentication.")
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def retry_interceptor?
|
|
95
|
+
return true if all_interceptor_classes.include?(LHC::Retry) && all_interceptor_classes.index(LHC::Retry) > all_interceptor_classes.index(self.class)
|
|
96
|
+
warn("[WARNING] Your interceptors must include LHC::Retry after LHC::Auth.")
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class LHC::Caching < LHC::Interceptor
|
|
4
|
+
include ActiveSupport::Configurable
|
|
5
|
+
|
|
6
|
+
config_accessor :cache, :logger
|
|
7
|
+
|
|
8
|
+
CACHE_VERSION = '1'
|
|
9
|
+
|
|
10
|
+
# Options forwarded to the cache
|
|
11
|
+
FORWARDED_OPTIONS = [:expires_in, :race_condition_ttl]
|
|
12
|
+
|
|
13
|
+
def before_request
|
|
14
|
+
return unless cache?(request)
|
|
15
|
+
deprecation_warning(request.options)
|
|
16
|
+
options = options(request.options)
|
|
17
|
+
key = key(request, options[:key])
|
|
18
|
+
response_data = cache_for(options).fetch(key)
|
|
19
|
+
return unless response_data
|
|
20
|
+
logger&.info "Served from cache: #{key}"
|
|
21
|
+
from_cache(request, response_data)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def after_response
|
|
25
|
+
return unless response.success?
|
|
26
|
+
request = response.request
|
|
27
|
+
return unless cache?(request)
|
|
28
|
+
options = options(request.options)
|
|
29
|
+
cache_for(options).write(
|
|
30
|
+
key(request, options[:key]),
|
|
31
|
+
to_cache(response),
|
|
32
|
+
cache_options(options)
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
# return the cache for the given options
|
|
39
|
+
def cache_for(options)
|
|
40
|
+
options.fetch(:use, cache)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# do we even need to bother with this interceptor?
|
|
44
|
+
# based on the options, this method will
|
|
45
|
+
# return false if this interceptor cannot work
|
|
46
|
+
def cache?(request)
|
|
47
|
+
return false unless request.options[:cache]
|
|
48
|
+
options = options(request.options)
|
|
49
|
+
cache_for(options) &&
|
|
50
|
+
cached_method?(request.method, options[:methods])
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# returns the request_options
|
|
54
|
+
# will map deprecated options to the new format
|
|
55
|
+
def options(request_options)
|
|
56
|
+
options = (request_options[:cache] == true) ? {} : request_options[:cache].dup
|
|
57
|
+
map_deprecated_options!(request_options, options)
|
|
58
|
+
options
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# maps `cache_key` -> `key`, `cache_expires_in` -> `expires_in` and so on
|
|
62
|
+
def map_deprecated_options!(request_options, options)
|
|
63
|
+
deprecated_keys(request_options).each do |deprecated_key|
|
|
64
|
+
new_key = deprecated_key.to_s.gsub(/^cache_/, '').to_sym
|
|
65
|
+
options[new_key] = request_options[deprecated_key]
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# converts json we read from the cache to an LHC::Response object
|
|
70
|
+
def from_cache(request, data)
|
|
71
|
+
raw = Typhoeus::Response.new(data)
|
|
72
|
+
response = LHC::Response.new(raw, request, from_cache: true)
|
|
73
|
+
request.response = response
|
|
74
|
+
response
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# converts a LHC::Response object to json, we store in the cache
|
|
78
|
+
def to_cache(response)
|
|
79
|
+
data = {}
|
|
80
|
+
data[:body] = response.body
|
|
81
|
+
data[:code] = response.code
|
|
82
|
+
# convert into a actual hash because the typhoeus headers object breaks marshaling
|
|
83
|
+
data[:headers] = response.headers ? Hash[response.headers] : response.headers
|
|
84
|
+
# return_code is quite important as Typhoeus relies on it in order to determin 'success?'
|
|
85
|
+
data[:return_code] = response.options[:return_code]
|
|
86
|
+
# in a test scenario typhoeus uses mocks and not return_code to determine 'success?'
|
|
87
|
+
data[:mock] = response.mock
|
|
88
|
+
data
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def key(request, key)
|
|
92
|
+
unless key
|
|
93
|
+
key = "#{request.method.upcase} #{request.url}"
|
|
94
|
+
key += "?#{request.params.to_query}" unless request.params.blank?
|
|
95
|
+
end
|
|
96
|
+
"LHC_CACHE(v#{CACHE_VERSION}): #{key}"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Checks if the provided method should be cached
|
|
100
|
+
# in regards of the provided options.
|
|
101
|
+
def cached_method?(method, cached_methods)
|
|
102
|
+
(cached_methods || [:get]).include?(method)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# extracts the options that should be forwarded to
|
|
106
|
+
# the cache
|
|
107
|
+
def cache_options(input = {})
|
|
108
|
+
input.each_with_object({}) do |(key, value), result|
|
|
109
|
+
result[key] = value if key.in? FORWARDED_OPTIONS
|
|
110
|
+
result
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# grabs the deprecated keys from the request options
|
|
115
|
+
def deprecated_keys(request_options)
|
|
116
|
+
request_options.keys.select { |k| k =~ /^cache_.*/ }.sort
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# emits a deprecation warning if necessary
|
|
120
|
+
def deprecation_warning(request_options)
|
|
121
|
+
unless deprecated_keys(request_options).empty?
|
|
122
|
+
ActiveSupport::Deprecation.warn(
|
|
123
|
+
"Cache options have changed! #{deprecated_keys(request_options).join(', ')} are deprecated and will be removed in future versions."
|
|
124
|
+
)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|