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.
Files changed (301) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/rubocop.yml +27 -0
  3. data/.github/workflows/test.yml +27 -0
  4. data/.gitignore +39 -0
  5. data/.rubocop.yml +186 -0
  6. data/.ruby-version +1 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +674 -0
  9. data/README.md +2807 -0
  10. data/Rakefile +26 -0
  11. data/dhs.gemspec +44 -0
  12. data/docs/accessing-data.png +0 -0
  13. data/lib/dhs.rb +67 -0
  14. data/lib/dhs/collection.rb +84 -0
  15. data/lib/dhs/complex.rb +158 -0
  16. data/lib/dhs/concerns/autoload_records.rb +57 -0
  17. data/lib/dhs/concerns/collection/handle_nested.rb +43 -0
  18. data/lib/dhs/concerns/collection/internal_collection.rb +74 -0
  19. data/lib/dhs/concerns/configuration.rb +20 -0
  20. data/lib/dhs/concerns/data/becomes.rb +18 -0
  21. data/lib/dhs/concerns/data/equality.rb +14 -0
  22. data/lib/dhs/concerns/data/extend.rb +87 -0
  23. data/lib/dhs/concerns/data/json.rb +14 -0
  24. data/lib/dhs/concerns/data/to_hash.rb +14 -0
  25. data/lib/dhs/concerns/inspect.rb +70 -0
  26. data/lib/dhs/concerns/is_href.rb +15 -0
  27. data/lib/dhs/concerns/item/destroy.rb +38 -0
  28. data/lib/dhs/concerns/item/endpoint_lookup.rb +27 -0
  29. data/lib/dhs/concerns/item/save.rb +55 -0
  30. data/lib/dhs/concerns/item/update.rb +50 -0
  31. data/lib/dhs/concerns/item/validation.rb +61 -0
  32. data/lib/dhs/concerns/o_auth.rb +25 -0
  33. data/lib/dhs/concerns/option_blocks.rb +26 -0
  34. data/lib/dhs/concerns/proxy/accessors.rb +132 -0
  35. data/lib/dhs/concerns/proxy/create.rb +45 -0
  36. data/lib/dhs/concerns/proxy/link.rb +25 -0
  37. data/lib/dhs/concerns/proxy/problems.rb +27 -0
  38. data/lib/dhs/concerns/record/attribute_assignment.rb +25 -0
  39. data/lib/dhs/concerns/record/batch.rb +40 -0
  40. data/lib/dhs/concerns/record/chainable.rb +465 -0
  41. data/lib/dhs/concerns/record/configuration.rb +103 -0
  42. data/lib/dhs/concerns/record/create.rb +24 -0
  43. data/lib/dhs/concerns/record/custom_setters.rb +22 -0
  44. data/lib/dhs/concerns/record/destroy.rb +18 -0
  45. data/lib/dhs/concerns/record/endpoints.rb +108 -0
  46. data/lib/dhs/concerns/record/equality.rb +14 -0
  47. data/lib/dhs/concerns/record/find.rb +86 -0
  48. data/lib/dhs/concerns/record/find_by.rb +38 -0
  49. data/lib/dhs/concerns/record/first.rb +20 -0
  50. data/lib/dhs/concerns/record/href_for.rb +19 -0
  51. data/lib/dhs/concerns/record/last.rb +27 -0
  52. data/lib/dhs/concerns/record/mapping.rb +25 -0
  53. data/lib/dhs/concerns/record/merge.rb +26 -0
  54. data/lib/dhs/concerns/record/model.rb +23 -0
  55. data/lib/dhs/concerns/record/pagination.rb +49 -0
  56. data/lib/dhs/concerns/record/provider.rb +23 -0
  57. data/lib/dhs/concerns/record/relations.rb +26 -0
  58. data/lib/dhs/concerns/record/request.rb +581 -0
  59. data/lib/dhs/concerns/record/scope.rb +25 -0
  60. data/lib/dhs/concerns/record/tracing.rb +24 -0
  61. data/lib/dhs/concerns/record/update.rb +17 -0
  62. data/lib/dhs/config.rb +24 -0
  63. data/lib/dhs/data.rb +180 -0
  64. data/lib/dhs/endpoint.rb +12 -0
  65. data/lib/dhs/interceptors/auto_oauth/interceptor.rb +33 -0
  66. data/lib/dhs/interceptors/auto_oauth/thread_registry.rb +18 -0
  67. data/lib/dhs/interceptors/extended_rollbar/handler.rb +40 -0
  68. data/lib/dhs/interceptors/extended_rollbar/interceptor.rb +20 -0
  69. data/lib/dhs/interceptors/extended_rollbar/thread_registry.rb +19 -0
  70. data/lib/dhs/interceptors/request_cycle_cache/interceptor.rb +41 -0
  71. data/lib/dhs/interceptors/request_cycle_cache/thread_registry.rb +18 -0
  72. data/lib/dhs/item.rb +59 -0
  73. data/lib/dhs/pagination/base.rb +90 -0
  74. data/lib/dhs/pagination/link.rb +21 -0
  75. data/lib/dhs/pagination/offset.rb +22 -0
  76. data/lib/dhs/pagination/page.rb +18 -0
  77. data/lib/dhs/pagination/start.rb +22 -0
  78. data/lib/dhs/pagination/total_pages.rb +9 -0
  79. data/lib/dhs/problems/base.rb +113 -0
  80. data/lib/dhs/problems/errors.rb +69 -0
  81. data/lib/dhs/problems/nested/base.rb +54 -0
  82. data/lib/dhs/problems/nested/errors.rb +16 -0
  83. data/lib/dhs/problems/nested/warnings.rb +15 -0
  84. data/lib/dhs/problems/warnings.rb +24 -0
  85. data/lib/dhs/proxy.rb +69 -0
  86. data/lib/dhs/railtie.rb +34 -0
  87. data/lib/dhs/record.rb +112 -0
  88. data/lib/dhs/rspec.rb +10 -0
  89. data/lib/dhs/test/stubbable_records.rb +34 -0
  90. data/lib/dhs/unprocessable.rb +6 -0
  91. data/lib/dhs/version.rb +5 -0
  92. data/script/ci/build.sh +18 -0
  93. data/spec/auto_oauth_spec.rb +163 -0
  94. data/spec/autoloading_spec.rb +45 -0
  95. data/spec/collection/accessors_spec.rb +31 -0
  96. data/spec/collection/collection_items_spec.rb +44 -0
  97. data/spec/collection/configurable_spec.rb +43 -0
  98. data/spec/collection/delegate_spec.rb +21 -0
  99. data/spec/collection/enumerable_spec.rb +27 -0
  100. data/spec/collection/href_spec.rb +17 -0
  101. data/spec/collection/meta_data_spec.rb +58 -0
  102. data/spec/collection/respond_to_spec.rb +20 -0
  103. data/spec/collection/to_a_spec.rb +34 -0
  104. data/spec/collection/to_ary_spec.rb +40 -0
  105. data/spec/collection/without_object_items_spec.rb +27 -0
  106. data/spec/complex/reduce_spec.rb +202 -0
  107. data/spec/concerns/record/request_spec.rb +78 -0
  108. data/spec/data/collection_spec.rb +56 -0
  109. data/spec/data/equality_spec.rb +23 -0
  110. data/spec/data/inspect_spec.rb +88 -0
  111. data/spec/data/is_item_or_collection_spec.rb +40 -0
  112. data/spec/data/item_spec.rb +106 -0
  113. data/spec/data/merge_spec.rb +27 -0
  114. data/spec/data/parent_spec.rb +39 -0
  115. data/spec/data/raw_spec.rb +48 -0
  116. data/spec/data/respond_to_spec.rb +26 -0
  117. data/spec/data/root_spec.rb +25 -0
  118. data/spec/data/select_spec.rb +27 -0
  119. data/spec/data/to_ary_spec.rb +28 -0
  120. data/spec/data/to_json_spec.rb +68 -0
  121. data/spec/dummy/Rakefile +8 -0
  122. data/spec/dummy/app/assets/images/.keep +0 -0
  123. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  124. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  125. data/spec/dummy/app/controllers/application_controller.rb +26 -0
  126. data/spec/dummy/app/controllers/automatic_authentication_controller.rb +29 -0
  127. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  128. data/spec/dummy/app/controllers/error_handling_with_chains_controller.rb +36 -0
  129. data/spec/dummy/app/controllers/extended_rollbar_controller.rb +10 -0
  130. data/spec/dummy/app/controllers/option_blocks_controller.rb +15 -0
  131. data/spec/dummy/app/controllers/request_cycle_cache_controller.rb +27 -0
  132. data/spec/dummy/app/helpers/application_helper.rb +4 -0
  133. data/spec/dummy/app/mailers/.keep +0 -0
  134. data/spec/dummy/app/models/.keep +0 -0
  135. data/spec/dummy/app/models/concerns/.keep +0 -0
  136. data/spec/dummy/app/models/concerns/dummy_customer/some_concern.rb +9 -0
  137. data/spec/dummy/app/models/dummy_customer.rb +7 -0
  138. data/spec/dummy/app/models/dummy_record.rb +6 -0
  139. data/spec/dummy/app/models/dummy_record_with_auto_oauth_provider.rb +6 -0
  140. data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers1.rb +7 -0
  141. data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers2.rb +7 -0
  142. data/spec/dummy/app/models/dummy_record_with_multiple_providers_per_endpoint.rb +6 -0
  143. data/spec/dummy/app/models/dummy_record_with_oauth.rb +7 -0
  144. data/spec/dummy/app/models/dummy_user.rb +6 -0
  145. data/spec/dummy/app/models/providers/customer_system.rb +7 -0
  146. data/spec/dummy/app/models/providers/internal_services.rb +7 -0
  147. data/spec/dummy/app/views/error_handling_with_chains/error.html.erb +1 -0
  148. data/spec/dummy/app/views/error_handling_with_chains/show.html.erb +3 -0
  149. data/spec/dummy/app/views/form_for.html.erb +5 -0
  150. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  151. data/spec/dummy/bin/bundle +5 -0
  152. data/spec/dummy/bin/rails +6 -0
  153. data/spec/dummy/bin/rake +6 -0
  154. data/spec/dummy/config.ru +6 -0
  155. data/spec/dummy/config/application.rb +16 -0
  156. data/spec/dummy/config/boot.rb +7 -0
  157. data/spec/dummy/config/environment.rb +7 -0
  158. data/spec/dummy/config/environments/development.rb +36 -0
  159. data/spec/dummy/config/environments/production.rb +77 -0
  160. data/spec/dummy/config/environments/test.rb +40 -0
  161. data/spec/dummy/config/initializers/assets.rb +10 -0
  162. data/spec/dummy/config/initializers/backtrace_silencers.rb +9 -0
  163. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  164. data/spec/dummy/config/initializers/dhs.rb +5 -0
  165. data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  166. data/spec/dummy/config/initializers/inflections.rb +18 -0
  167. data/spec/dummy/config/initializers/mime_types.rb +6 -0
  168. data/spec/dummy/config/initializers/rollbar.rb +9 -0
  169. data/spec/dummy/config/initializers/session_store.rb +5 -0
  170. data/spec/dummy/config/initializers/wrap_parameters.rb +11 -0
  171. data/spec/dummy/config/locales/en.yml +23 -0
  172. data/spec/dummy/config/routes.rb +27 -0
  173. data/spec/dummy/config/secrets.yml +22 -0
  174. data/spec/dummy/lib/assets/.keep +0 -0
  175. data/spec/dummy/public/404.html +67 -0
  176. data/spec/dummy/public/422.html +67 -0
  177. data/spec/dummy/public/500.html +66 -0
  178. data/spec/dummy/public/favicon.ico +0 -0
  179. data/spec/endpoint/for_url_spec.rb +27 -0
  180. data/spec/extended_rollbar_spec.rb +67 -0
  181. data/spec/item/access_errors_spec.rb +31 -0
  182. data/spec/item/accessors_spec.rb +21 -0
  183. data/spec/item/add_error_spec.rb +21 -0
  184. data/spec/item/becomes_spec.rb +38 -0
  185. data/spec/item/blacklisted_keywords_spec.rb +28 -0
  186. data/spec/item/delegate_spec.rb +32 -0
  187. data/spec/item/destroy_spec.rb +113 -0
  188. data/spec/item/dig_spec.rb +29 -0
  189. data/spec/item/error_codes_spec.rb +55 -0
  190. data/spec/item/errors_spec.rb +324 -0
  191. data/spec/item/fetch_spec.rb +39 -0
  192. data/spec/item/getter_spec.rb +24 -0
  193. data/spec/item/internal_data_structure_spec.rb +37 -0
  194. data/spec/item/map_spec.rb +46 -0
  195. data/spec/item/nested_errors_spec.rb +27 -0
  196. data/spec/item/partial_update_spec.rb +168 -0
  197. data/spec/item/respond_to_spec.rb +31 -0
  198. data/spec/item/save_spec.rb +115 -0
  199. data/spec/item/setter_spec.rb +44 -0
  200. data/spec/item/translate_errors_spec.rb +257 -0
  201. data/spec/item/update_spec.rb +161 -0
  202. data/spec/item/validation_spec.rb +131 -0
  203. data/spec/item/warning_codes_spec.rb +55 -0
  204. data/spec/item/warnings_spec.rb +51 -0
  205. data/spec/option_blocks/ensure_reset_between_requests_spec.rb +23 -0
  206. data/spec/option_blocks/main_spec.rb +54 -0
  207. data/spec/pagination/link/current_page_spec.rb +19 -0
  208. data/spec/pagination/link/pages_left_spec.rb +36 -0
  209. data/spec/pagination/link/parallel_spec.rb +19 -0
  210. data/spec/pagination/link/total_spec.rb +45 -0
  211. data/spec/pagination/offset/pages_left_spec.rb +26 -0
  212. data/spec/pagination/parameters_spec.rb +59 -0
  213. data/spec/pagination/total_pages_spec.rb +51 -0
  214. data/spec/proxy/create_sub_resource_spec.rb +182 -0
  215. data/spec/proxy/load_spec.rb +75 -0
  216. data/spec/proxy/record_identification_spec.rb +35 -0
  217. data/spec/rails_helper.rb +13 -0
  218. data/spec/record/all_spec.rb +133 -0
  219. data/spec/record/attribute_assignment_spec.rb +28 -0
  220. data/spec/record/build_spec.rb +26 -0
  221. data/spec/record/cast_nested_data_spec.rb +80 -0
  222. data/spec/record/compact_spec.rb +93 -0
  223. data/spec/record/create_spec.rb +160 -0
  224. data/spec/record/creation_failed_spec.rb +55 -0
  225. data/spec/record/custom_setters_spec.rb +42 -0
  226. data/spec/record/definitions_spec.rb +29 -0
  227. data/spec/record/destroy_spec.rb +38 -0
  228. data/spec/record/dig_configuration_spec.rb +75 -0
  229. data/spec/record/dup_spec.rb +20 -0
  230. data/spec/record/endpoint_inheritance_spec.rb +65 -0
  231. data/spec/record/endpoint_options_spec.rb +51 -0
  232. data/spec/record/endpoint_priorities_spec.rb +24 -0
  233. data/spec/record/endpoints_spec.rb +96 -0
  234. data/spec/record/equality_spec.rb +27 -0
  235. data/spec/record/error_handling_integration_spec.rb +25 -0
  236. data/spec/record/error_handling_spec.rb +40 -0
  237. data/spec/record/expanded_spec.rb +69 -0
  238. data/spec/record/fetch_spec.rb +40 -0
  239. data/spec/record/find_by_chains_spec.rb +21 -0
  240. data/spec/record/find_by_spec.rb +76 -0
  241. data/spec/record/find_each_spec.rb +57 -0
  242. data/spec/record/find_in_batches_spec.rb +122 -0
  243. data/spec/record/find_in_parallel_spec.rb +67 -0
  244. data/spec/record/find_spec.rb +103 -0
  245. data/spec/record/first_spec.rb +39 -0
  246. data/spec/record/force_merge_spec.rb +55 -0
  247. data/spec/record/handle_includes_errors_spec.rb +33 -0
  248. data/spec/record/has_many_spec.rb +118 -0
  249. data/spec/record/has_one_spec.rb +114 -0
  250. data/spec/record/href_for_spec.rb +24 -0
  251. data/spec/record/ignore_errors_spec.rb +137 -0
  252. data/spec/record/immutable_chains_spec.rb +22 -0
  253. data/spec/record/includes_after_expansion_spec.rb +70 -0
  254. data/spec/record/includes_expanded_spec.rb +37 -0
  255. data/spec/record/includes_first_page_spec.rb +738 -0
  256. data/spec/record/includes_missing_spec.rb +57 -0
  257. data/spec/record/includes_spec.rb +690 -0
  258. data/spec/record/includes_warning_spec.rb +46 -0
  259. data/spec/record/item_key_spec.rb +81 -0
  260. data/spec/record/items_created_key_configuration_spec.rb +37 -0
  261. data/spec/record/last_spec.rb +64 -0
  262. data/spec/record/loading_twice_spec.rb +19 -0
  263. data/spec/record/mapping_spec.rb +103 -0
  264. data/spec/record/model_name_spec.rb +17 -0
  265. data/spec/record/new_spec.rb +106 -0
  266. data/spec/record/options_getter_spec.rb +25 -0
  267. data/spec/record/options_spec.rb +164 -0
  268. data/spec/record/paginatable_collection_spec.rb +360 -0
  269. data/spec/record/pagination_chain_spec.rb +101 -0
  270. data/spec/record/pagination_links_spec.rb +72 -0
  271. data/spec/record/pagination_spec.rb +71 -0
  272. data/spec/record/persisted_spec.rb +52 -0
  273. data/spec/record/provider_spec.rb +40 -0
  274. data/spec/record/references_spec.rb +95 -0
  275. data/spec/record/relation_caching_spec.rb +120 -0
  276. data/spec/record/reload_by_id_spec.rb +43 -0
  277. data/spec/record/reload_spec.rb +64 -0
  278. data/spec/record/request_spec.rb +90 -0
  279. data/spec/record/save_spec.rb +40 -0
  280. data/spec/record/scope_chains_spec.rb +39 -0
  281. data/spec/record/select_spec.rb +17 -0
  282. data/spec/record/to_ary_spec.rb +65 -0
  283. data/spec/record/to_hash_spec.rb +22 -0
  284. data/spec/record/to_json_spec.rb +22 -0
  285. data/spec/record/tracing_spec.rb +149 -0
  286. data/spec/record/update_spec.rb +61 -0
  287. data/spec/record/where_chains_spec.rb +57 -0
  288. data/spec/record/where_spec.rb +62 -0
  289. data/spec/record/where_values_hash_spec.rb +32 -0
  290. data/spec/request_cycle_cache_spec.rb +106 -0
  291. data/spec/require_dhs_spec.rb +9 -0
  292. data/spec/spec_helper.rb +6 -0
  293. data/spec/stubs/all_spec.rb +69 -0
  294. data/spec/support/fixtures/json/feedback.json +11 -0
  295. data/spec/support/fixtures/json/feedbacks.json +174 -0
  296. data/spec/support/fixtures/json/localina_content_ad.json +23 -0
  297. data/spec/support/load_json.rb +5 -0
  298. data/spec/support/request_cycle_cache.rb +10 -0
  299. data/spec/support/reset.rb +67 -0
  300. data/spec/views/form_for_spec.rb +20 -0
  301. metadata +783 -0
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'active_support/per_thread_registry'
5
+
6
+ module DHS
7
+ module Interceptors
8
+ module RequestCycleCache
9
+ extend ActiveSupport::Concern
10
+ class ThreadRegistry
11
+ # Using ActiveSupports PerThreadRegistry to be able to support Active Support v4.
12
+ # Will switch to thread_mattr_accessor (which comes with Activesupport) when we dropping support for Active Support v4.
13
+ extend ActiveSupport::PerThreadRegistry
14
+ attr_accessor :request_id
15
+ end
16
+ end
17
+ end
18
+ end
data/lib/dhs/item.rb ADDED
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ # An item is a concrete record.
4
+ # It can be part of another proxy like collection.
5
+ class DHS::Item < DHS::Proxy
6
+
7
+ autoload :Destroy,
8
+ 'dhs/concerns/item/destroy'
9
+ autoload :Save,
10
+ 'dhs/concerns/item/save'
11
+ autoload :Update,
12
+ 'dhs/concerns/item/update'
13
+ autoload :Validation,
14
+ 'dhs/concerns/item/validation'
15
+
16
+ include Create
17
+ include Destroy
18
+ include Save
19
+ include Update
20
+ include Validation
21
+
22
+ delegate :map, :present?, :blank?, :empty?, to: :_raw, allow_nil: true
23
+ delegate :_raw, to: :_data
24
+
25
+ def collection?
26
+ false
27
+ end
28
+
29
+ def item?
30
+ true
31
+ end
32
+
33
+ def respond_to?(sym, include_all = false)
34
+ if sym == :to_ary
35
+ false
36
+ else
37
+ super(sym, include_all)
38
+ end
39
+ end
40
+
41
+ protected
42
+
43
+ def method_missing(name, *args, **_keyword_args, &_block)
44
+ return set(name, args) if name.to_s[/=$/]
45
+ get(name, *args)
46
+ end
47
+
48
+ def respond_to_missing?(name, _include_all = false)
49
+ # We accept every message that does not belong to set of keywords
50
+ !BLACKLISTED_KEYWORDS.include?(name.to_s)
51
+ end
52
+
53
+ def unwrap_nested_item
54
+ return _data unless _record.item_key
55
+ nested_data = _data.dig(*_record.item_key)
56
+ return _data unless nested_data
57
+ DHS::Data.new(nested_data, _data._parent, _record, _data._request, _data._endpoint)
58
+ end
59
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/module/delegation'
4
+
5
+ # Pagination is used to navigate paginateable collections
6
+ module DHS::Pagination
7
+ class Base
8
+
9
+ DEFAULT_LIMIT = 100
10
+
11
+ delegate :_record, to: :data
12
+ attr_accessor :data
13
+
14
+ def initialize(data)
15
+ self.data = data
16
+ end
17
+
18
+ def total
19
+ data._raw.dig(*_record.total_key) || 0
20
+ end
21
+
22
+ # as standard in Rails' ActiveRecord count is not summing up, but using the number provided from data source
23
+ alias count total
24
+
25
+ def limit
26
+ data._raw.dig(*_record.limit_key(:body)) || DEFAULT_LIMIT
27
+ end
28
+
29
+ def offset
30
+ data._raw.dig(*_record.pagination_key(:body)) || self.class::DEFAULT_OFFSET
31
+ end
32
+ alias start offset
33
+
34
+ def pages_left
35
+ total_pages - current_page
36
+ end
37
+
38
+ def pages_left?
39
+ pages_left > 0
40
+ end
41
+
42
+ def next_offset(_step = 1)
43
+ raise 'to be implemented in subclass'
44
+ end
45
+
46
+ def current_page
47
+ # should be implemented in subclass (optional)
48
+ end
49
+
50
+ def first_page
51
+ 1
52
+ end
53
+
54
+ def last_page
55
+ total_pages
56
+ end
57
+
58
+ def parallel?
59
+ true
60
+ end
61
+
62
+ def next?
63
+ data._raw[:next].present?
64
+ end
65
+
66
+ def previous?
67
+ data._raw[:previous].present?
68
+ end
69
+
70
+ def prev_page
71
+ current_page - 1
72
+ end
73
+
74
+ def next_page
75
+ current_page + 1
76
+ end
77
+
78
+ def limit_value
79
+ limit
80
+ end
81
+
82
+ def total_pages
83
+ (total.to_f / limit).ceil
84
+ end
85
+
86
+ def self.page_to_offset(page, _limit)
87
+ page.to_i
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DHS::Pagination::Link < DHS::Pagination::Base
4
+ def total
5
+ data._raw.dig(*_record.items_key).count || 0
6
+ end
7
+
8
+ alias count total
9
+
10
+ def pages_left
11
+ pages_left? ? 1 : 0
12
+ end
13
+
14
+ def pages_left?
15
+ data._raw[:next].present?
16
+ end
17
+
18
+ def parallel?
19
+ false
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DHS::Pagination::Offset < DHS::Pagination::Base
4
+
5
+ DEFAULT_OFFSET = 0
6
+
7
+ def current_page
8
+ (offset + limit) / limit
9
+ end
10
+
11
+ def next_offset(step = 1)
12
+ self.class.next_offset(offset, limit, step)
13
+ end
14
+
15
+ def self.page_to_offset(page, limit = DEFAULT_LIMIT)
16
+ (page.to_i - 1) * limit.to_i
17
+ end
18
+
19
+ def self.next_offset(offset, limit, step = 1)
20
+ offset.to_i + limit.to_i * step.to_i
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DHS::Pagination::Page < DHS::Pagination::Base
4
+
5
+ DEFAULT_OFFSET = 1
6
+
7
+ def current_page
8
+ offset
9
+ end
10
+
11
+ def next_offset(step = 1)
12
+ self.class.next_offset(current_page, limit, step)
13
+ end
14
+
15
+ def self.next_offset(current_page, _limit, step = 1)
16
+ current_page.to_i + step.to_i
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DHS::Pagination::Start < DHS::Pagination::Base
4
+
5
+ DEFAULT_OFFSET = 1
6
+
7
+ def current_page
8
+ (offset + limit - 1) / limit
9
+ end
10
+
11
+ def next_offset(step = 1)
12
+ self.class.next_offset(offset, limit, step)
13
+ end
14
+
15
+ def self.page_to_offset(page, limit = DEFAULT_LIMIT)
16
+ (page.to_i - 1) * limit.to_i + 1
17
+ end
18
+
19
+ def self.next_offset(offset, limit, step = 1)
20
+ offset.to_i + limit.to_i * step.to_i
21
+ end
22
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DHS::Pagination::TotalPages < DHS::Pagination::Page
4
+
5
+ def total
6
+ (data._raw.dig(*_record.total_key) || 0) * limit
7
+ end
8
+ alias count total
9
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/module'
4
+ require 'active_support/core_ext/hash'
5
+
6
+ # Like ActiveModel::Errors
7
+ module DHS::Problems
8
+ class Base
9
+ include Enumerable
10
+
11
+ attr_reader :raw, :messages, :codes, :record
12
+
13
+ def include?(attribute)
14
+ messages[attribute].present?
15
+ end
16
+ alias has_key? include?
17
+ alias key? include?
18
+
19
+ def add(attribute, message = :invalid, options = {})
20
+ self[attribute]
21
+ messages[attribute] << generate_message(attribute, message, options)
22
+ end
23
+
24
+ def get(key)
25
+ messages[key]
26
+ end
27
+
28
+ def set(key, message)
29
+ return if message.blank?
30
+ messages[key] = [generate_message(key, message)]
31
+ end
32
+
33
+ delegate :delete, to: :messages
34
+
35
+ def [](attribute)
36
+ get(attribute.to_sym) || messages[attribute] = []
37
+ end
38
+
39
+ def []=(attribute, message)
40
+ self[attribute] << generate_message(attribute, message)
41
+ end
42
+
43
+ def each
44
+ if messages.is_a?(Hash)
45
+ messages.each_key do |attribute|
46
+ self[attribute].each { |message| yield attribute, message }
47
+ end
48
+ elsif messages.is_a?(Array)
49
+ messages.each { |message| yield message }
50
+ end
51
+ end
52
+
53
+ def size
54
+ values.flatten.size
55
+ end
56
+
57
+ def clear
58
+ @raw = nil
59
+ @messages.clear
60
+ @codes.clear
61
+ end
62
+
63
+ delegate :values, to: :messages
64
+
65
+ delegate :keys, to: :messages
66
+
67
+ def count
68
+ to_a.size
69
+ end
70
+
71
+ def empty?
72
+ all? { |_k, v| v&.empty? && !v.is_a?(String) }
73
+ end
74
+
75
+ private
76
+
77
+ def add_error(messages, key, value)
78
+ key = key.to_sym
79
+ codes[key] ||= []
80
+ codes[key].push(value)
81
+ messages[key] ||= []
82
+ messages[key].push(generate_message(key, value))
83
+ end
84
+
85
+ def generate_message(attribute, message, _options = {})
86
+ problem_type = self.class.name.demodulize.downcase
87
+ find_translated_message(attribute, message, problem_type) || message
88
+ end
89
+
90
+ def find_translated_message(attribute, message, problem_type)
91
+ normalized_attribute = attribute.to_s.underscore.gsub(/\.[\d+\.]/, '')
92
+ normalized_message = message.to_s.underscore
93
+ messages = []
94
+ messages = messages_for_record(normalized_attribute, normalized_message, problem_type) if record
95
+ messages.concat([
96
+ ['dhs', problem_type, 'messages', normalized_message],
97
+ ['dhs', problem_type, 'attributes', normalized_attribute, normalized_message],
98
+ ['dhs', problem_type, 'fallback_message']
99
+ ]).detect do |path|
100
+ key = path.join('.')
101
+ return I18n.translate(key) if I18n.exists?(key)
102
+ end
103
+ end
104
+
105
+ def messages_for_record(normalized_attribute, normalized_message, problem_type)
106
+ record_name = record.model_name.name.underscore
107
+ [
108
+ ['dhs', problem_type, 'records', record_name, 'attributes', normalized_attribute, normalized_message],
109
+ ['dhs', problem_type, 'records', record_name, normalized_message]
110
+ ]
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DHS::Problems
4
+ class Errors < Base
5
+
6
+ attr_reader :status_code, :message
7
+
8
+ def initialize(response = nil, record = nil)
9
+ @raw = response.body if response
10
+ @record = record
11
+ @codes = {}.with_indifferent_access
12
+ @messages = messages_from_response(response).with_indifferent_access
13
+ @message = message_from_response(response)
14
+ @status_code = response.code if response
15
+ rescue JSON::ParserError
16
+ @messages = (messages || {}).with_indifferent_access
17
+ @codes = (codes || {}).with_indifferent_access
18
+ @message = 'parse error'
19
+ add_error(@messages, 'body', 'parse error')
20
+ end
21
+
22
+ private
23
+
24
+ def parse_messages(json)
25
+ messages = {}
26
+ fields_to_errors(json, messages) if json['fields']
27
+ field_errors_to_errors(json, messages) if json['field_errors']
28
+ fallback_errors(json, messages) if messages.empty? && !json['fields'] && !json['field_errors']
29
+ messages
30
+ end
31
+
32
+ def fallback_errors(json, messages)
33
+ if json.present?
34
+ json.each do |key, value|
35
+ add_error(messages, key, value)
36
+ end
37
+ else
38
+ add_error(messages, 'unknown', 'error')
39
+ end
40
+ end
41
+
42
+ def field_errors_to_errors(json, messages)
43
+ json['field_errors'].each do |field_error|
44
+ add_error(messages, field_error['path'].join('.').to_sym, field_error['code'])
45
+ end
46
+ end
47
+
48
+ def fields_to_errors(json, messages)
49
+ json['fields'].each do |field|
50
+ field['details'].each do |detail|
51
+ add_error(messages, field['name'].to_sym, detail['code'])
52
+ end
53
+ end
54
+ end
55
+
56
+ def messages_from_response(response = nil)
57
+ return {} if !response || !response.body.is_a?(String) || response.body.length.zero?
58
+ json = JSON.parse(response.body)
59
+ parse_messages(json)
60
+ end
61
+
62
+ def message_from_response(response = nil)
63
+ return if response.blank?
64
+ raise JSON::ParserError if response.body.blank?
65
+ json = JSON.parse(response.body)
66
+ json['message']
67
+ end
68
+ end
69
+ end