dhs 1.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/.github/workflows/rubocop.yml +27 -0
- data/.github/workflows/test.yml +27 -0
- data/.gitignore +39 -0
- data/.rubocop.yml +186 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE +674 -0
- data/README.md +2807 -0
- data/Rakefile +26 -0
- data/dhs.gemspec +44 -0
- data/docs/accessing-data.png +0 -0
- data/lib/dhs.rb +67 -0
- data/lib/dhs/collection.rb +84 -0
- data/lib/dhs/complex.rb +158 -0
- data/lib/dhs/concerns/autoload_records.rb +57 -0
- data/lib/dhs/concerns/collection/handle_nested.rb +43 -0
- data/lib/dhs/concerns/collection/internal_collection.rb +74 -0
- data/lib/dhs/concerns/configuration.rb +20 -0
- data/lib/dhs/concerns/data/becomes.rb +18 -0
- data/lib/dhs/concerns/data/equality.rb +14 -0
- data/lib/dhs/concerns/data/extend.rb +87 -0
- data/lib/dhs/concerns/data/json.rb +14 -0
- data/lib/dhs/concerns/data/to_hash.rb +14 -0
- data/lib/dhs/concerns/inspect.rb +70 -0
- data/lib/dhs/concerns/is_href.rb +15 -0
- data/lib/dhs/concerns/item/destroy.rb +38 -0
- data/lib/dhs/concerns/item/endpoint_lookup.rb +27 -0
- data/lib/dhs/concerns/item/save.rb +55 -0
- data/lib/dhs/concerns/item/update.rb +50 -0
- data/lib/dhs/concerns/item/validation.rb +61 -0
- data/lib/dhs/concerns/o_auth.rb +25 -0
- data/lib/dhs/concerns/option_blocks.rb +26 -0
- data/lib/dhs/concerns/proxy/accessors.rb +132 -0
- data/lib/dhs/concerns/proxy/create.rb +45 -0
- data/lib/dhs/concerns/proxy/link.rb +25 -0
- data/lib/dhs/concerns/proxy/problems.rb +27 -0
- data/lib/dhs/concerns/record/attribute_assignment.rb +25 -0
- data/lib/dhs/concerns/record/batch.rb +40 -0
- data/lib/dhs/concerns/record/chainable.rb +465 -0
- data/lib/dhs/concerns/record/configuration.rb +103 -0
- data/lib/dhs/concerns/record/create.rb +24 -0
- data/lib/dhs/concerns/record/custom_setters.rb +22 -0
- data/lib/dhs/concerns/record/destroy.rb +18 -0
- data/lib/dhs/concerns/record/endpoints.rb +108 -0
- data/lib/dhs/concerns/record/equality.rb +14 -0
- data/lib/dhs/concerns/record/find.rb +86 -0
- data/lib/dhs/concerns/record/find_by.rb +38 -0
- data/lib/dhs/concerns/record/first.rb +20 -0
- data/lib/dhs/concerns/record/href_for.rb +19 -0
- data/lib/dhs/concerns/record/last.rb +27 -0
- data/lib/dhs/concerns/record/mapping.rb +25 -0
- data/lib/dhs/concerns/record/merge.rb +26 -0
- data/lib/dhs/concerns/record/model.rb +23 -0
- data/lib/dhs/concerns/record/pagination.rb +49 -0
- data/lib/dhs/concerns/record/provider.rb +23 -0
- data/lib/dhs/concerns/record/relations.rb +26 -0
- data/lib/dhs/concerns/record/request.rb +581 -0
- data/lib/dhs/concerns/record/scope.rb +25 -0
- data/lib/dhs/concerns/record/tracing.rb +24 -0
- data/lib/dhs/concerns/record/update.rb +17 -0
- data/lib/dhs/config.rb +24 -0
- data/lib/dhs/data.rb +180 -0
- data/lib/dhs/endpoint.rb +12 -0
- data/lib/dhs/interceptors/auto_oauth/interceptor.rb +33 -0
- data/lib/dhs/interceptors/auto_oauth/thread_registry.rb +18 -0
- data/lib/dhs/interceptors/extended_rollbar/handler.rb +40 -0
- data/lib/dhs/interceptors/extended_rollbar/interceptor.rb +20 -0
- data/lib/dhs/interceptors/extended_rollbar/thread_registry.rb +19 -0
- data/lib/dhs/interceptors/request_cycle_cache/interceptor.rb +41 -0
- data/lib/dhs/interceptors/request_cycle_cache/thread_registry.rb +18 -0
- data/lib/dhs/item.rb +59 -0
- data/lib/dhs/pagination/base.rb +90 -0
- data/lib/dhs/pagination/link.rb +21 -0
- data/lib/dhs/pagination/offset.rb +22 -0
- data/lib/dhs/pagination/page.rb +18 -0
- data/lib/dhs/pagination/start.rb +22 -0
- data/lib/dhs/pagination/total_pages.rb +9 -0
- data/lib/dhs/problems/base.rb +113 -0
- data/lib/dhs/problems/errors.rb +69 -0
- data/lib/dhs/problems/nested/base.rb +54 -0
- data/lib/dhs/problems/nested/errors.rb +16 -0
- data/lib/dhs/problems/nested/warnings.rb +15 -0
- data/lib/dhs/problems/warnings.rb +24 -0
- data/lib/dhs/proxy.rb +69 -0
- data/lib/dhs/railtie.rb +34 -0
- data/lib/dhs/record.rb +112 -0
- data/lib/dhs/rspec.rb +10 -0
- data/lib/dhs/test/stubbable_records.rb +34 -0
- data/lib/dhs/unprocessable.rb +6 -0
- data/lib/dhs/version.rb +5 -0
- data/script/ci/build.sh +18 -0
- data/spec/auto_oauth_spec.rb +163 -0
- data/spec/autoloading_spec.rb +45 -0
- data/spec/collection/accessors_spec.rb +31 -0
- data/spec/collection/collection_items_spec.rb +44 -0
- data/spec/collection/configurable_spec.rb +43 -0
- data/spec/collection/delegate_spec.rb +21 -0
- data/spec/collection/enumerable_spec.rb +27 -0
- data/spec/collection/href_spec.rb +17 -0
- data/spec/collection/meta_data_spec.rb +58 -0
- data/spec/collection/respond_to_spec.rb +20 -0
- data/spec/collection/to_a_spec.rb +34 -0
- data/spec/collection/to_ary_spec.rb +40 -0
- data/spec/collection/without_object_items_spec.rb +27 -0
- data/spec/complex/reduce_spec.rb +202 -0
- data/spec/concerns/record/request_spec.rb +78 -0
- data/spec/data/collection_spec.rb +56 -0
- data/spec/data/equality_spec.rb +23 -0
- data/spec/data/inspect_spec.rb +88 -0
- data/spec/data/is_item_or_collection_spec.rb +40 -0
- data/spec/data/item_spec.rb +106 -0
- data/spec/data/merge_spec.rb +27 -0
- data/spec/data/parent_spec.rb +39 -0
- data/spec/data/raw_spec.rb +48 -0
- data/spec/data/respond_to_spec.rb +26 -0
- data/spec/data/root_spec.rb +25 -0
- data/spec/data/select_spec.rb +27 -0
- data/spec/data/to_ary_spec.rb +28 -0
- data/spec/data/to_json_spec.rb +68 -0
- data/spec/dummy/Rakefile +8 -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 +26 -0
- data/spec/dummy/app/controllers/automatic_authentication_controller.rb +29 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/controllers/error_handling_with_chains_controller.rb +36 -0
- data/spec/dummy/app/controllers/extended_rollbar_controller.rb +10 -0
- data/spec/dummy/app/controllers/option_blocks_controller.rb +15 -0
- data/spec/dummy/app/controllers/request_cycle_cache_controller.rb +27 -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/models/concerns/dummy_customer/some_concern.rb +9 -0
- data/spec/dummy/app/models/dummy_customer.rb +7 -0
- data/spec/dummy/app/models/dummy_record.rb +6 -0
- data/spec/dummy/app/models/dummy_record_with_auto_oauth_provider.rb +6 -0
- data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers1.rb +7 -0
- data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers2.rb +7 -0
- data/spec/dummy/app/models/dummy_record_with_multiple_providers_per_endpoint.rb +6 -0
- data/spec/dummy/app/models/dummy_record_with_oauth.rb +7 -0
- data/spec/dummy/app/models/dummy_user.rb +6 -0
- data/spec/dummy/app/models/providers/customer_system.rb +7 -0
- data/spec/dummy/app/models/providers/internal_services.rb +7 -0
- data/spec/dummy/app/views/error_handling_with_chains/error.html.erb +1 -0
- data/spec/dummy/app/views/error_handling_with_chains/show.html.erb +3 -0
- data/spec/dummy/app/views/form_for.html.erb +5 -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 +40 -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/dhs.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/rollbar.rb +9 -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 +27 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/lib/assets/.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/for_url_spec.rb +27 -0
- data/spec/extended_rollbar_spec.rb +67 -0
- data/spec/item/access_errors_spec.rb +31 -0
- data/spec/item/accessors_spec.rb +21 -0
- data/spec/item/add_error_spec.rb +21 -0
- data/spec/item/becomes_spec.rb +38 -0
- data/spec/item/blacklisted_keywords_spec.rb +28 -0
- data/spec/item/delegate_spec.rb +32 -0
- data/spec/item/destroy_spec.rb +113 -0
- data/spec/item/dig_spec.rb +29 -0
- data/spec/item/error_codes_spec.rb +55 -0
- data/spec/item/errors_spec.rb +324 -0
- data/spec/item/fetch_spec.rb +39 -0
- data/spec/item/getter_spec.rb +24 -0
- data/spec/item/internal_data_structure_spec.rb +37 -0
- data/spec/item/map_spec.rb +46 -0
- data/spec/item/nested_errors_spec.rb +27 -0
- data/spec/item/partial_update_spec.rb +168 -0
- data/spec/item/respond_to_spec.rb +31 -0
- data/spec/item/save_spec.rb +115 -0
- data/spec/item/setter_spec.rb +44 -0
- data/spec/item/translate_errors_spec.rb +257 -0
- data/spec/item/update_spec.rb +161 -0
- data/spec/item/validation_spec.rb +131 -0
- data/spec/item/warning_codes_spec.rb +55 -0
- data/spec/item/warnings_spec.rb +51 -0
- data/spec/option_blocks/ensure_reset_between_requests_spec.rb +23 -0
- data/spec/option_blocks/main_spec.rb +54 -0
- data/spec/pagination/link/current_page_spec.rb +19 -0
- data/spec/pagination/link/pages_left_spec.rb +36 -0
- data/spec/pagination/link/parallel_spec.rb +19 -0
- data/spec/pagination/link/total_spec.rb +45 -0
- data/spec/pagination/offset/pages_left_spec.rb +26 -0
- data/spec/pagination/parameters_spec.rb +59 -0
- data/spec/pagination/total_pages_spec.rb +51 -0
- data/spec/proxy/create_sub_resource_spec.rb +182 -0
- data/spec/proxy/load_spec.rb +75 -0
- data/spec/proxy/record_identification_spec.rb +35 -0
- data/spec/rails_helper.rb +13 -0
- data/spec/record/all_spec.rb +133 -0
- data/spec/record/attribute_assignment_spec.rb +28 -0
- data/spec/record/build_spec.rb +26 -0
- data/spec/record/cast_nested_data_spec.rb +80 -0
- data/spec/record/compact_spec.rb +93 -0
- data/spec/record/create_spec.rb +160 -0
- data/spec/record/creation_failed_spec.rb +55 -0
- data/spec/record/custom_setters_spec.rb +42 -0
- data/spec/record/definitions_spec.rb +29 -0
- data/spec/record/destroy_spec.rb +38 -0
- data/spec/record/dig_configuration_spec.rb +75 -0
- data/spec/record/dup_spec.rb +20 -0
- data/spec/record/endpoint_inheritance_spec.rb +65 -0
- data/spec/record/endpoint_options_spec.rb +51 -0
- data/spec/record/endpoint_priorities_spec.rb +24 -0
- data/spec/record/endpoints_spec.rb +96 -0
- data/spec/record/equality_spec.rb +27 -0
- data/spec/record/error_handling_integration_spec.rb +25 -0
- data/spec/record/error_handling_spec.rb +40 -0
- data/spec/record/expanded_spec.rb +69 -0
- data/spec/record/fetch_spec.rb +40 -0
- data/spec/record/find_by_chains_spec.rb +21 -0
- data/spec/record/find_by_spec.rb +76 -0
- data/spec/record/find_each_spec.rb +57 -0
- data/spec/record/find_in_batches_spec.rb +122 -0
- data/spec/record/find_in_parallel_spec.rb +67 -0
- data/spec/record/find_spec.rb +103 -0
- data/spec/record/first_spec.rb +39 -0
- data/spec/record/force_merge_spec.rb +55 -0
- data/spec/record/handle_includes_errors_spec.rb +33 -0
- data/spec/record/has_many_spec.rb +118 -0
- data/spec/record/has_one_spec.rb +114 -0
- data/spec/record/href_for_spec.rb +24 -0
- data/spec/record/ignore_errors_spec.rb +137 -0
- data/spec/record/immutable_chains_spec.rb +22 -0
- data/spec/record/includes_after_expansion_spec.rb +70 -0
- data/spec/record/includes_expanded_spec.rb +37 -0
- data/spec/record/includes_first_page_spec.rb +738 -0
- data/spec/record/includes_missing_spec.rb +57 -0
- data/spec/record/includes_spec.rb +690 -0
- data/spec/record/includes_warning_spec.rb +46 -0
- data/spec/record/item_key_spec.rb +81 -0
- data/spec/record/items_created_key_configuration_spec.rb +37 -0
- data/spec/record/last_spec.rb +64 -0
- data/spec/record/loading_twice_spec.rb +19 -0
- data/spec/record/mapping_spec.rb +103 -0
- data/spec/record/model_name_spec.rb +17 -0
- data/spec/record/new_spec.rb +106 -0
- data/spec/record/options_getter_spec.rb +25 -0
- data/spec/record/options_spec.rb +164 -0
- data/spec/record/paginatable_collection_spec.rb +360 -0
- data/spec/record/pagination_chain_spec.rb +101 -0
- data/spec/record/pagination_links_spec.rb +72 -0
- data/spec/record/pagination_spec.rb +71 -0
- data/spec/record/persisted_spec.rb +52 -0
- data/spec/record/provider_spec.rb +40 -0
- data/spec/record/references_spec.rb +95 -0
- data/spec/record/relation_caching_spec.rb +120 -0
- data/spec/record/reload_by_id_spec.rb +43 -0
- data/spec/record/reload_spec.rb +64 -0
- data/spec/record/request_spec.rb +90 -0
- data/spec/record/save_spec.rb +40 -0
- data/spec/record/scope_chains_spec.rb +39 -0
- data/spec/record/select_spec.rb +17 -0
- data/spec/record/to_ary_spec.rb +65 -0
- data/spec/record/to_hash_spec.rb +22 -0
- data/spec/record/to_json_spec.rb +22 -0
- data/spec/record/tracing_spec.rb +149 -0
- data/spec/record/update_spec.rb +61 -0
- data/spec/record/where_chains_spec.rb +57 -0
- data/spec/record/where_spec.rb +62 -0
- data/spec/record/where_values_hash_spec.rb +32 -0
- data/spec/request_cycle_cache_spec.rb +106 -0
- data/spec/require_dhs_spec.rb +9 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/stubs/all_spec.rb +69 -0
- data/spec/support/fixtures/json/feedback.json +11 -0
- data/spec/support/fixtures/json/feedbacks.json +174 -0
- data/spec/support/fixtures/json/localina_content_ad.json +23 -0
- data/spec/support/load_json.rb +5 -0
- data/spec/support/request_cycle_cache.rb +10 -0
- data/spec/support/reset.rb +67 -0
- data/spec/views/form_for_spec.rb +20 -0
- metadata +783 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support'
|
|
4
|
+
|
|
5
|
+
class DHS::Record
|
|
6
|
+
|
|
7
|
+
module HrefFor
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
def href_for(args = nil)
|
|
12
|
+
return unless [Integer, String].include?(args.class)
|
|
13
|
+
params = { id: args }
|
|
14
|
+
find_endpoint(params).compile(params)
|
|
15
|
+
end
|
|
16
|
+
alias url_for href_for
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support'
|
|
4
|
+
|
|
5
|
+
class DHS::Record
|
|
6
|
+
|
|
7
|
+
module Last
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
def last(options = nil)
|
|
12
|
+
options = trace!(options)
|
|
13
|
+
first_batch = find_by({}, options).parent
|
|
14
|
+
if first_batch.paginated?
|
|
15
|
+
pagination = first_batch._pagination
|
|
16
|
+
find_by({ pagination_key => pagination.class.page_to_offset(pagination.last_page, pagination.limit) }, options)
|
|
17
|
+
else
|
|
18
|
+
first_batch.last
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def last!(options = nil)
|
|
23
|
+
find_by!({}, trace!(options))
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support'
|
|
4
|
+
|
|
5
|
+
class DHS::Record
|
|
6
|
+
|
|
7
|
+
# Mapping allows to configure some accessors that access data using a provided proc
|
|
8
|
+
module Mapping
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
def mapping
|
|
13
|
+
@mapping ||= {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def mapping=(mapping)
|
|
17
|
+
@mapping = mapping
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def map(name, block)
|
|
21
|
+
mapping[name] = block
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support'
|
|
4
|
+
|
|
5
|
+
class DHS::Record
|
|
6
|
+
|
|
7
|
+
module Merge
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
def merge(other)
|
|
11
|
+
_record.new(_data.to_h.merge(other.to_h))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def merge!(other)
|
|
15
|
+
_data._raw.merge!(other.to_h)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def deep_merge(other)
|
|
19
|
+
_record.new(_data.to_h.deep_merge(other.to_h))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def deep_merge!(other)
|
|
23
|
+
_data._raw.deep_merge!(other.to_h)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support'
|
|
4
|
+
require 'active_model'
|
|
5
|
+
|
|
6
|
+
class DHS::Record
|
|
7
|
+
|
|
8
|
+
module Model
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
def to_model
|
|
12
|
+
self
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def persisted?
|
|
16
|
+
href.present?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
included do
|
|
20
|
+
extend ActiveModel::Naming
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support'
|
|
4
|
+
|
|
5
|
+
class DHS::Record
|
|
6
|
+
|
|
7
|
+
module Pagination
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
# Kaminari-Interface
|
|
10
|
+
delegate :current_page, :first_page, :last_page, :prev_page, :next_page, :limit_value, :total_pages, to: :_pagination
|
|
11
|
+
|
|
12
|
+
def paginated?(raw = nil)
|
|
13
|
+
self.class.paginated?(raw || _raw)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def _pagination
|
|
17
|
+
self.class.pagination(_data)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
module ClassMethods
|
|
21
|
+
def pagination_class
|
|
22
|
+
case pagination_strategy.to_sym
|
|
23
|
+
when :page
|
|
24
|
+
DHS::Pagination::Page
|
|
25
|
+
when :total_pages
|
|
26
|
+
DHS::Pagination::TotalPages
|
|
27
|
+
when :start
|
|
28
|
+
DHS::Pagination::Start
|
|
29
|
+
when :link
|
|
30
|
+
DHS::Pagination::Link
|
|
31
|
+
else
|
|
32
|
+
DHS::Pagination::Offset
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def pagination(data)
|
|
37
|
+
pagination_class.new(data)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Checks if given raw is paginated or not
|
|
41
|
+
def paginated?(raw)
|
|
42
|
+
raw.is_a?(Hash) && (
|
|
43
|
+
raw.dig(*total_key).present? ||
|
|
44
|
+
raw.dig(*limit_key(:body)).present?
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support'
|
|
4
|
+
require 'active_support/core_ext'
|
|
5
|
+
|
|
6
|
+
class DHS::Record
|
|
7
|
+
|
|
8
|
+
# A provider can define options used for that specific provider
|
|
9
|
+
module Provider
|
|
10
|
+
extend ActiveSupport::Concern
|
|
11
|
+
|
|
12
|
+
included do
|
|
13
|
+
class_attribute :provider_options unless defined? provider_options
|
|
14
|
+
self.provider_options = nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module ClassMethods
|
|
18
|
+
def provider(options = nil)
|
|
19
|
+
self.provider_options = options
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support'
|
|
4
|
+
require 'active_support/core_ext/class/attribute'
|
|
5
|
+
|
|
6
|
+
class DHS::Record
|
|
7
|
+
|
|
8
|
+
module Relations
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
included do
|
|
12
|
+
class_attribute :_relations
|
|
13
|
+
self._relations = {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module ClassMethods
|
|
17
|
+
def has_many(*options)
|
|
18
|
+
name = options[0]
|
|
19
|
+
options = options[1] || {}
|
|
20
|
+
_relations[name] = { record_class_name: options.fetch(:class_name, name.to_s.singularize.classify) }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
alias has_one has_many
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support'
|
|
4
|
+
require 'active_support/core_ext/object'
|
|
5
|
+
|
|
6
|
+
class DHS::Record
|
|
7
|
+
|
|
8
|
+
module Request
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
def request(options)
|
|
13
|
+
options ||= {}
|
|
14
|
+
options = deep_merge_with_option_blocks(options)
|
|
15
|
+
options = options.freeze
|
|
16
|
+
if options.is_a?(Array)
|
|
17
|
+
multiple_requests(
|
|
18
|
+
filter_empty_request_options(options)
|
|
19
|
+
)
|
|
20
|
+
else
|
|
21
|
+
single_request(options)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def deep_merge_with_option_blocks(options)
|
|
28
|
+
return options if DHS::OptionBlocks::CurrentOptionBlock.options.blank?
|
|
29
|
+
if options.is_a?(Hash)
|
|
30
|
+
options.deep_merge(DHS::OptionBlocks::CurrentOptionBlock.options)
|
|
31
|
+
elsif options.is_a?(Array)
|
|
32
|
+
options.map do |option|
|
|
33
|
+
return DHS::OptionBlocks::CurrentOptionBlock.options unless option
|
|
34
|
+
option.deep_merge(DHS::OptionBlocks::CurrentOptionBlock.options)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def single_request_load_and_merge_remaining_objects!(data, options, endpoint)
|
|
40
|
+
return if options[:all].blank? || !paginated
|
|
41
|
+
load_and_merge_remaining_objects!(
|
|
42
|
+
data: data,
|
|
43
|
+
options: process_options(options, endpoint),
|
|
44
|
+
load_not_paginated_collection: true
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def filter_empty_request_options(options)
|
|
49
|
+
options.map do |option|
|
|
50
|
+
option if !option || !option.key?(:url) || !option[:url].nil?
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Applies limit to the first request of an all request chain
|
|
55
|
+
# Tries to apply an high value for limit and reacts on the limit
|
|
56
|
+
# returned by the endpoint to make further requests
|
|
57
|
+
def apply_limit!(options)
|
|
58
|
+
return if !paginated || options[:all].blank?
|
|
59
|
+
options[:params] ||= {}
|
|
60
|
+
options[:params] = options[:params].merge(limit_key(:parameter) => options[:params][limit_key(:parameter)] || DHS::Pagination::Base::DEFAULT_LIMIT)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Convert URLs in options to endpoint templates
|
|
64
|
+
def convert_options_to_endpoints(options)
|
|
65
|
+
if options.is_a?(Array)
|
|
66
|
+
options.map { |request_options| convert_options_to_endpoint(request_options) }
|
|
67
|
+
else
|
|
68
|
+
convert_options_to_endpoint(options)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def convert_options_to_endpoint(options)
|
|
73
|
+
return if options.blank?
|
|
74
|
+
url = options[:url]
|
|
75
|
+
endpoint = DHS::Endpoint.for_url(url)
|
|
76
|
+
return unless endpoint
|
|
77
|
+
template = endpoint.url
|
|
78
|
+
new_options = options.deep_merge(
|
|
79
|
+
params: DHC::Endpoint.values_as_params(template, url).merge(values_from_get_params(url, options))
|
|
80
|
+
)
|
|
81
|
+
new_options[:url] = template
|
|
82
|
+
new_options
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Extracts values from url's get parameters
|
|
86
|
+
# and return them as a ruby hash
|
|
87
|
+
def values_from_get_params(url, options)
|
|
88
|
+
uri = parse_uri(url, options)
|
|
89
|
+
return {} if uri.query.blank?
|
|
90
|
+
params = Rack::Utils.parse_nested_query(uri.query).deep_symbolize_keys
|
|
91
|
+
params
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def parse_uri(url, options)
|
|
95
|
+
URI.parse(
|
|
96
|
+
if url.match(Addressable::Template::EXPRESSION)
|
|
97
|
+
compute_url(options[:params], url)
|
|
98
|
+
else
|
|
99
|
+
url
|
|
100
|
+
end
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def handle_includes(includes, data, references = {})
|
|
105
|
+
references ||= {}
|
|
106
|
+
if includes.is_a? Hash
|
|
107
|
+
includes.each { |included, sub_includes| handle_include(included, data, sub_includes, references[included]) }
|
|
108
|
+
elsif includes.is_a? Array
|
|
109
|
+
includes.each do |included|
|
|
110
|
+
handle_includes(included, data, references)
|
|
111
|
+
end
|
|
112
|
+
else
|
|
113
|
+
handle_include(includes, data, nil, references[includes])
|
|
114
|
+
end
|
|
115
|
+
data.clear_cache! if data.present? # as we just included new nested resources
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def handle_include(included, data, sub_includes = nil, reference = nil)
|
|
119
|
+
if data.blank? || skip_loading_includes?(data, included)
|
|
120
|
+
handle_skip_include(included, data, sub_includes, reference)
|
|
121
|
+
else
|
|
122
|
+
options = options_for_data(data, included)
|
|
123
|
+
options = extend_with_reference(options, reference)
|
|
124
|
+
addition = load_existing_includes(options, data, sub_includes, reference)
|
|
125
|
+
data.extend!(addition, included)
|
|
126
|
+
expand_addition!(data, included, reference) unless expanded_data?(addition)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def handle_skip_include(included, data, sub_includes = nil, reference = nil)
|
|
131
|
+
return if sub_includes.blank?
|
|
132
|
+
handle_includes(sub_includes, data[included], reference)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def options_for_data(data, included = nil)
|
|
136
|
+
return options_for_multiple(data, included) if data.collection?
|
|
137
|
+
return options_for_nested_items(data, included) if included && data[included].collection?
|
|
138
|
+
url_option_for(data, included)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def expand_addition!(data, included, reference)
|
|
142
|
+
addition = data[included]
|
|
143
|
+
options = options_for_data(addition)
|
|
144
|
+
options = extend_with_reference(options, reference)
|
|
145
|
+
record = record_for_options(options) || self
|
|
146
|
+
options = convert_options_to_endpoints(options) if record_for_options(options)
|
|
147
|
+
expanded_data = record.request(options)
|
|
148
|
+
data.extend!(expanded_data, included)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def expanded_data?(addition)
|
|
152
|
+
return false if addition.blank?
|
|
153
|
+
if addition.item?
|
|
154
|
+
(addition._raw.keys - [:href]).any?
|
|
155
|
+
elsif addition.collection?
|
|
156
|
+
addition.any? do |item|
|
|
157
|
+
next if item.blank?
|
|
158
|
+
if item._raw.is_a?(Hash)
|
|
159
|
+
(item._raw.keys - [:href]).any?
|
|
160
|
+
elsif item._raw.is_a?(Array)
|
|
161
|
+
item.any? { |item| (item._raw.keys - [:href]).any? }
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Extends request options with options provided for this reference
|
|
168
|
+
def extend_with_reference(options, reference)
|
|
169
|
+
return options if reference.blank?
|
|
170
|
+
reference = reference.except(:url)
|
|
171
|
+
options ||= {}
|
|
172
|
+
if options.is_a?(Array)
|
|
173
|
+
options.map { |request_options| request_options.merge(reference) if request_options.present? }
|
|
174
|
+
elsif options.present?
|
|
175
|
+
options.merge(reference)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def skip_loading_includes?(data, included)
|
|
180
|
+
if data.collection?
|
|
181
|
+
data.to_a.none? { |item| item[included].present? }
|
|
182
|
+
elsif data.dig(included).blank?
|
|
183
|
+
true
|
|
184
|
+
elsif data[included].item? && data[included][:href].blank?
|
|
185
|
+
true
|
|
186
|
+
else
|
|
187
|
+
!data._raw.key?(included)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# After fetching the first page,
|
|
192
|
+
# we can evaluate if there are further remote objects remaining
|
|
193
|
+
# and after preparing all the requests that have to be made in order to fetch all
|
|
194
|
+
# remote items during this batch, they are fetched in parallel
|
|
195
|
+
def load_and_merge_remaining_objects!(data:, options:, load_not_paginated_collection: false)
|
|
196
|
+
if paginated?(data._raw)
|
|
197
|
+
load_and_merge_paginated_collection!(data, options)
|
|
198
|
+
elsif data.collection? && paginated?(data.first.try(:_raw))
|
|
199
|
+
load_and_merge_set_of_paginated_collections!(data, options)
|
|
200
|
+
elsif load_not_paginated_collection && data.collection?
|
|
201
|
+
warn('[Warning] "all" has been requested, but endpoint does not provide pagination meta data. If you just want to fetch the first response, use "where" or "fetch".')
|
|
202
|
+
load_and_merge_not_paginated_collection!(data, options)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def load_and_merge_not_paginated_collection!(data, options)
|
|
207
|
+
return if data.length.zero?
|
|
208
|
+
options = options.is_a?(Hash) ? options : {}
|
|
209
|
+
limit = options.dig(:params, limit_key(:parameter)) || pagination_class::DEFAULT_LIMIT
|
|
210
|
+
offset = options.dig(:params, pagination_key(:parameter)) || pagination_class::DEFAULT_OFFSET
|
|
211
|
+
options[:params] = options.fetch(:params, {}).merge(
|
|
212
|
+
limit_key(:parameter) => limit,
|
|
213
|
+
pagination_key(:parameter) => pagination_class.next_offset(
|
|
214
|
+
offset,
|
|
215
|
+
limit
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
additional_data = data._record.request(options)
|
|
219
|
+
additional_data.each do |item_data|
|
|
220
|
+
data.concat(input: data._raw, items: [item_data], record: self)
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# sets nested data for a source object that needs to be accessed with a given path e.g. [:response, :total]
|
|
225
|
+
def set_nested_data(source, path, value)
|
|
226
|
+
return source[path] = value unless path.is_a?(Array)
|
|
227
|
+
path = path.dup
|
|
228
|
+
last = path.pop
|
|
229
|
+
path.inject(source, :fetch)[last] = value
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def load_and_merge_paginated_collection!(data, options)
|
|
233
|
+
set_nested_data(data._raw, limit_key(:body), data.length) if data._raw.dig(*limit_key(:body)).blank? && !data.length.zero?
|
|
234
|
+
pagination = data._record.pagination(data)
|
|
235
|
+
return data unless pagination.pages_left?
|
|
236
|
+
record = data._record
|
|
237
|
+
if pagination.parallel?
|
|
238
|
+
load_and_merge_parallel_requests!(record, data, pagination, options)
|
|
239
|
+
else
|
|
240
|
+
load_and_merge_sequential_requests!(record, data, options, data._raw.dig(:next, :href), pagination)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def load_and_merge_parallel_requests!(record, data, pagination, options)
|
|
245
|
+
record.request(
|
|
246
|
+
options_for_next_batch(record, pagination, options)
|
|
247
|
+
).each do |batch_data|
|
|
248
|
+
merge_batch_data_with_parent!(batch_data, data)
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def load_and_merge_sequential_requests!(record, data, options, next_link, pagination)
|
|
253
|
+
warn '[WARNING] You are loading all pages from a resource paginated with links only. As this is performed sequentially, it can result in very poor performance! (https://github.com/DePayFi/dhs#pagination-strategy-link).'
|
|
254
|
+
while next_link.present?
|
|
255
|
+
page_data = record.request(
|
|
256
|
+
options.except(:all).merge(url: next_link)
|
|
257
|
+
)
|
|
258
|
+
next_link = page_data._raw.dig(:next, :href)
|
|
259
|
+
merge_batch_data_with_parent!(page_data, data, pagination)
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def load_and_merge_set_of_paginated_collections!(data, options)
|
|
264
|
+
options_for_next_batch = []
|
|
265
|
+
options.each_with_index do |element, index|
|
|
266
|
+
next if element.nil?
|
|
267
|
+
record = data[index]._record
|
|
268
|
+
pagination = record.pagination(data[index])
|
|
269
|
+
next unless pagination.pages_left?
|
|
270
|
+
options_for_next_batch.push(
|
|
271
|
+
options_for_next_batch(record, pagination, options[index]).tap do |options|
|
|
272
|
+
options.each do |option|
|
|
273
|
+
option[:merge_with_index] = index
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
)
|
|
277
|
+
end
|
|
278
|
+
data._record.request(options_for_next_batch.flatten).each do |batch_data|
|
|
279
|
+
merge_batch_data_with_parent!(batch_data, data[batch_data._request.options[:merge_with_index]])
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def load_existing_includes(options, data, sub_includes, references)
|
|
284
|
+
if data.collection? && data.any?(&:blank?)
|
|
285
|
+
# filter only existing items
|
|
286
|
+
loaded_includes = load_include(options.compact, data.compact, sub_includes, references)
|
|
287
|
+
# fill up skipped items before returning
|
|
288
|
+
data.each_with_index do |item, index|
|
|
289
|
+
next if item.present?
|
|
290
|
+
loaded_includes.insert(index, {})
|
|
291
|
+
end
|
|
292
|
+
loaded_includes
|
|
293
|
+
else
|
|
294
|
+
load_include(options, data, sub_includes, references)
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Load additional resources that are requested with include
|
|
299
|
+
def load_include(options, _data, sub_includes, references)
|
|
300
|
+
record = record_for_options(options) || self
|
|
301
|
+
options = convert_options_to_endpoints(options) if record_for_options(options)
|
|
302
|
+
prepare_options_for_include_request!(options, sub_includes, references)
|
|
303
|
+
if references && references[:all] # include all linked resources
|
|
304
|
+
load_include_all!(options, record, sub_includes, references)
|
|
305
|
+
else # simply request first page/batch
|
|
306
|
+
load_include_simple!(options, record)
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def load_include_all!(options, record, sub_includes, references)
|
|
311
|
+
prepare_options_for_include_all_request!(options)
|
|
312
|
+
data = load_all_included!(record, options)
|
|
313
|
+
references.delete(:all) # for this reference all remote objects have been fetched
|
|
314
|
+
continue_including(data, sub_includes, references)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def load_include_simple!(options, record)
|
|
318
|
+
data = record.request(options)
|
|
319
|
+
warn "[WARNING] You included `#{options[:url]}`, but this endpoint is paginated. You might want to use `includes_all` instead of `includes` (https://github.com/DePayFi/dhs#includes_all-for-paginated-endpoints)." if data && paginated?(data._raw)
|
|
320
|
+
data
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Continues loading included resources after one complete batch/level has been fetched
|
|
324
|
+
def continue_including(data, included, reference)
|
|
325
|
+
return data if included.blank? || data.blank?
|
|
326
|
+
expand_data!(data, included, reference) unless expanded_data?(data)
|
|
327
|
+
handle_includes(included, data, reference)
|
|
328
|
+
data
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def expand_data!(data, _included, reference)
|
|
332
|
+
options = options_for_data(data)
|
|
333
|
+
options = extend_with_reference(options, reference)
|
|
334
|
+
record = record_for_options(options) || self
|
|
335
|
+
options = convert_options_to_endpoints(options) if record_for_options(options)
|
|
336
|
+
expanded_data = record.request(options)
|
|
337
|
+
data.extend!(expanded_data)
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# Loads all included/linked resources,
|
|
341
|
+
# paginates itself to ensure all records are fetched
|
|
342
|
+
def load_all_included!(record, options)
|
|
343
|
+
data = record.request(options)
|
|
344
|
+
pagination = data._record.pagination(data)
|
|
345
|
+
load_and_merge_remaining_objects!(data: data, options: options) if pagination.parallel?
|
|
346
|
+
data
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def prepare_options_for_include_all_request!(options)
|
|
350
|
+
if options.is_a?(Array)
|
|
351
|
+
options.each do |option|
|
|
352
|
+
prepare_option_for_include_all_request!(option)
|
|
353
|
+
end
|
|
354
|
+
else
|
|
355
|
+
prepare_option_for_include_all_request!(options)
|
|
356
|
+
end
|
|
357
|
+
options
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
# When including all resources on one level, don't forward :includes & :references
|
|
361
|
+
# as we have to fetch all resources on this level first, before we continue_including
|
|
362
|
+
def prepare_option_for_include_all_request!(option)
|
|
363
|
+
return option if option.blank? || option[:url].nil?
|
|
364
|
+
uri = parse_uri(option[:url], option)
|
|
365
|
+
get_params = Rack::Utils.parse_nested_query(uri.query)
|
|
366
|
+
.symbolize_keys
|
|
367
|
+
.except(limit_key(:parameter), pagination_key(:parameter))
|
|
368
|
+
option[:params] ||= {}
|
|
369
|
+
option[:params].reverse_merge!(get_params)
|
|
370
|
+
option[:params][limit_key(:parameter)] ||= DHS::Pagination::Base::DEFAULT_LIMIT
|
|
371
|
+
option[:url] = option[:url].gsub("?#{uri.query}", '')
|
|
372
|
+
option.delete(:including)
|
|
373
|
+
option.delete(:referencing)
|
|
374
|
+
option
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def prepare_options_for_include_request!(options, sub_includes, references)
|
|
378
|
+
if options.is_a?(Array)
|
|
379
|
+
options.each { |option| option.merge!(including: sub_includes, referencing: references) if sub_includes.present? }
|
|
380
|
+
elsif sub_includes.present?
|
|
381
|
+
options.merge!(including: sub_includes, referencing: references)
|
|
382
|
+
end
|
|
383
|
+
options || {}
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
def merge_batch_data_with_parent!(batch_data, parent_data, pagination = nil)
|
|
387
|
+
parent_data.concat(input: parent_data._raw, items: batch_data.raw_items, record: self)
|
|
388
|
+
return if pagination.present? && pagination.is_a?(DHS::Pagination::Link)
|
|
389
|
+
[limit_key(:body), total_key, pagination_key(:body)].each do |pagination_attribute|
|
|
390
|
+
set_nested_data(
|
|
391
|
+
parent_data._raw,
|
|
392
|
+
pagination_attribute,
|
|
393
|
+
batch_data._raw.dig(*pagination_attribute)
|
|
394
|
+
)
|
|
395
|
+
end
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
# Merge explicit params nested in 'params' namespace with original hash.
|
|
399
|
+
def merge_explicit_params!(params)
|
|
400
|
+
return true unless params
|
|
401
|
+
explicit_params = params[:params]
|
|
402
|
+
params.delete(:params)
|
|
403
|
+
params.merge!(explicit_params) if explicit_params
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def multiple_requests(options)
|
|
407
|
+
options = options.map do |option|
|
|
408
|
+
next if option.blank?
|
|
409
|
+
process_options(option, find_endpoint(option[:params], option.fetch(:url, nil)))
|
|
410
|
+
end
|
|
411
|
+
data = DHC.request(options.compact).map do |response|
|
|
412
|
+
DHS::Data.new(response.body, nil, self, response.request)
|
|
413
|
+
end
|
|
414
|
+
including = DHS::Complex.reduce(options.compact.map { |options| options.delete(:including) }.compact)
|
|
415
|
+
referencing = DHS::Complex.reduce(options.compact.map { |options| options.delete(:referencing) }.compact)
|
|
416
|
+
data = restore_with_nils(data, locate_nils(options)) # nil objects in data provide location information for mapping
|
|
417
|
+
data = DHS::Data.new(data, nil, self)
|
|
418
|
+
handle_includes(including, data, referencing) if including.present? && data.present?
|
|
419
|
+
data
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def locate_nils(array)
|
|
423
|
+
nils = []
|
|
424
|
+
array.each_with_index { |value, index| nils << index if value.nil? }
|
|
425
|
+
nils
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
def restore_with_nils(array, nils)
|
|
429
|
+
array = array.dup
|
|
430
|
+
nils.sort.each { |index| array.insert(index, nil) }
|
|
431
|
+
array
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def options_for_multiple(data, key = nil)
|
|
435
|
+
data.map do |item|
|
|
436
|
+
url_option_for(item, key)
|
|
437
|
+
end.flatten
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
def options_for_nested_items(data, key = nil)
|
|
441
|
+
data[key].map do |item|
|
|
442
|
+
url_option_for(item)
|
|
443
|
+
end.flatten
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
def options_for_next_batch(record, pagination, options)
|
|
447
|
+
batch_options = []
|
|
448
|
+
pagination.pages_left.times do |index|
|
|
449
|
+
page_options = {
|
|
450
|
+
params: {
|
|
451
|
+
record.limit_key(:parameter) => pagination.limit,
|
|
452
|
+
record.pagination_key(:parameter) => pagination.next_offset(index + 1)
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
batch_options.push(
|
|
456
|
+
options.deep_dup.deep_merge(page_options)
|
|
457
|
+
)
|
|
458
|
+
end
|
|
459
|
+
batch_options
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
# Merge explicit params and take configured endpoints options as base
|
|
463
|
+
def process_options(options, endpoint)
|
|
464
|
+
ignored_errors = options[:ignored_errors]
|
|
465
|
+
options = options.deep_dup
|
|
466
|
+
options[:ignored_errors] = ignored_errors if ignored_errors.present?
|
|
467
|
+
options[:params]&.deep_symbolize_keys!
|
|
468
|
+
options[:rescue] = merge_error_handlers(options[:rescue]) if options[:rescue]
|
|
469
|
+
options = (provider_options || {})
|
|
470
|
+
.deep_merge(endpoint.options || {})
|
|
471
|
+
.deep_merge(options)
|
|
472
|
+
options[:url] = compute_url!(options[:params]) unless options.key?(:url)
|
|
473
|
+
merge_explicit_params!(options[:params])
|
|
474
|
+
options.delete(:params) if options[:params]&.empty?
|
|
475
|
+
inject_interceptors!(options)
|
|
476
|
+
options
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def inject_interceptors!(options)
|
|
480
|
+
if DHS.config.request_cycle_cache_enabled
|
|
481
|
+
inject_interceptor!(
|
|
482
|
+
options,
|
|
483
|
+
DHS::Interceptors::RequestCycleCache::Interceptor,
|
|
484
|
+
DHC::Caching,
|
|
485
|
+
"[WARNING] Can't enable request cycle cache as DHC::Caching interceptor is not enabled/configured (see https://github.com/DePayFi/dhc/blob/master/README.md#caching-interceptor)!"
|
|
486
|
+
)
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
endpoint = find_endpoint(options[:params], options.fetch(:url, nil))
|
|
490
|
+
if auto_oauth? || (endpoint.options&.dig(:oauth) && DHS.config.auto_oauth) || options[:oauth]
|
|
491
|
+
inject_interceptor!(
|
|
492
|
+
options.merge!(record: self),
|
|
493
|
+
DHS::Interceptors::AutoOauth::Interceptor,
|
|
494
|
+
DHC::Auth,
|
|
495
|
+
"[WARNING] Can't enable auto oauth as DHC::Auth interceptor is not enabled/configured (see https://github.com/DePayFi/dhc/blob/master/README.md#authentication-interceptor)!"
|
|
496
|
+
)
|
|
497
|
+
end
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
def inject_interceptor!(options, interceptor, dependecy, warning)
|
|
501
|
+
interceptors = options[:interceptors] || DHC.config.interceptors
|
|
502
|
+
if interceptors.include?(dependecy)
|
|
503
|
+
# Ensure interceptor is prepend
|
|
504
|
+
interceptors.unshift(interceptor)
|
|
505
|
+
options[:interceptors] = interceptors
|
|
506
|
+
else
|
|
507
|
+
warn(warning)
|
|
508
|
+
end
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
# DHC supports only one error handler, merge all error handlers to one
|
|
512
|
+
# and reraise
|
|
513
|
+
def merge_error_handlers(handlers)
|
|
514
|
+
lambda do |response|
|
|
515
|
+
return_data = nil
|
|
516
|
+
error_class = DHC::Error.find(response)
|
|
517
|
+
error = error_class.new(error_class, response)
|
|
518
|
+
handlers = handlers.map(&:to_a).to_a.select { |handler_error_class, _| error.is_a? handler_error_class }
|
|
519
|
+
raise(error) unless handlers.any?
|
|
520
|
+
handlers.each do |_, handler|
|
|
521
|
+
handlers_return = handler.call(response)
|
|
522
|
+
return_data = handlers_return if handlers_return.present?
|
|
523
|
+
end
|
|
524
|
+
return return_data
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
def record_for_options(options)
|
|
529
|
+
records = []
|
|
530
|
+
if options.is_a?(Array)
|
|
531
|
+
options.compact.each do |option|
|
|
532
|
+
record = DHS::Record.for_url(option[:url])
|
|
533
|
+
next unless record
|
|
534
|
+
records.push(record)
|
|
535
|
+
end
|
|
536
|
+
raise 'Found more than one record that could be used to do the request' if records.uniq.count > 1
|
|
537
|
+
records.uniq.first
|
|
538
|
+
else # Hash
|
|
539
|
+
DHS::Record.for_url(options[:url])
|
|
540
|
+
end
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
def single_request(options)
|
|
544
|
+
options ||= {}
|
|
545
|
+
options = options.dup
|
|
546
|
+
including = options.delete(:including)
|
|
547
|
+
referencing = options.delete(:referencing)
|
|
548
|
+
endpoint = find_endpoint(options[:params], options.fetch(:url, nil))
|
|
549
|
+
apply_limit!(options)
|
|
550
|
+
response = DHC.request(process_options(options, endpoint))
|
|
551
|
+
return nil if !response.success? && response.error_ignored?
|
|
552
|
+
data = DHS::Data.new(response.body, nil, self, response.request, endpoint)
|
|
553
|
+
single_request_load_and_merge_remaining_objects!(data, options, endpoint)
|
|
554
|
+
expand_items(data, options[:expanded]) if data.collection? && options[:expanded]
|
|
555
|
+
handle_includes(including, data, referencing) if including.present? && data.present?
|
|
556
|
+
data
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
def expand_items(data, expand_options)
|
|
560
|
+
expand_options = {} unless expand_options.is_a?(Hash)
|
|
561
|
+
options = data.map do |item|
|
|
562
|
+
expand_options.merge(url: item.href)
|
|
563
|
+
end
|
|
564
|
+
expanded_data = request(options)
|
|
565
|
+
data.each_with_index do |item, index|
|
|
566
|
+
item.merge_raw!(expanded_data[index])
|
|
567
|
+
end
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
def url_option_for(item, key = nil)
|
|
571
|
+
link = key ? item[key] : item
|
|
572
|
+
return if link.blank?
|
|
573
|
+
return { url: link.href } unless link.collection?
|
|
574
|
+
|
|
575
|
+
link.map do |item|
|
|
576
|
+
{ url: item.href } if item.present? && item.href.present?
|
|
577
|
+
end.compact
|
|
578
|
+
end
|
|
579
|
+
end
|
|
580
|
+
end
|
|
581
|
+
end
|