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,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Record do
6
+ context 'equality' do
7
+ before do
8
+ class Record < DHS::Record
9
+ endpoint 'http://depay.fi/records'
10
+ end
11
+ end
12
+
13
+ let(:raw) do
14
+ { name: 'Steve' }
15
+ end
16
+
17
+ def record
18
+ DHS::Record.new DHS::Data.new(raw, nil, Record)
19
+ end
20
+
21
+ it 'is equal when two data objects share the same raw data' do
22
+ expect(
23
+ record
24
+ ).to eq record
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+ require 'dhc/rspec'
5
+
6
+ describe 'Error handling with chains', type: :request do
7
+ let!(:request) do
8
+ stub_request(:get, 'http://datastore/v2/records?color=blue')
9
+ .to_return(status: 404)
10
+ end
11
+
12
+ it 'handles errors in rails controllers when query resolved in controller',
13
+ dummy_models: true do
14
+ get '/error_handling_with_chains/fetch_in_controller'
15
+ expect(request).to have_been_made.once
16
+ expect(response.body).to include('Sorry there was an error.')
17
+ end
18
+
19
+ it 'handles errors in rails controllers when query resolved in view',
20
+ dummy_models: true do
21
+ get '/error_handling_with_chains/fetch_in_view'
22
+ expect(request).to have_been_made.once
23
+ expect(response.body).to include('Sorry there was an error.')
24
+ end
25
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Record do
6
+ let(:handler) { spy('handler') }
7
+
8
+ before do
9
+ class Record < DHS::Record
10
+ endpoint 'http://depay.fi/v2/records'
11
+ end
12
+ stub_request(:get, 'http://depay.fi/v2/records?color=blue')
13
+ .to_return(status: 400)
14
+ end
15
+
16
+ it 'allows to chain error handling' do
17
+ expect {
18
+ Record.where(color: 'blue').rescue(DHC::Error, ->(_error) { handler.handle }).first
19
+ }.not_to raise_error
20
+ expect(handler).to have_received(:handle)
21
+ end
22
+
23
+ it 'reraises in case chained error is not matched' do
24
+ expect {
25
+ Record.where(color: 'blue').rescue(DHC::Conflict, ->(_error) { handler.handle }).first
26
+ }.to raise_error(DHC::Error)
27
+ expect(handler).not_to have_received(:handle)
28
+ end
29
+
30
+ it 'calls all the handlers' do
31
+ expect {
32
+ Record.where(color: 'blue')
33
+ .rescue(DHC::Error, ->(_error) { handler.handle_1 })
34
+ .rescue(DHC::Error, ->(_error) { handler.handle_2 })
35
+ .first
36
+ }.not_to raise_error
37
+ expect(handler).to have_received(:handle_1)
38
+ expect(handler).to have_received(:handle_2)
39
+ end
40
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Record do
6
+ before do
7
+ class Record < DHS::Record
8
+ endpoint 'http://depay.fi/v2/records'
9
+ end
10
+ end
11
+
12
+ let!(:request_collection) do
13
+ stub_request(:get, 'http://depay.fi/v2/records?color=blue')
14
+ .to_return(body: {
15
+ items: [
16
+ { href: 'http://depay.fi/v2/records/1' },
17
+ { href: 'http://depay.fi/v2/records/2' }
18
+ ]
19
+ }.to_json)
20
+ end
21
+
22
+ let!(:request_item_1) do
23
+ stub_request(:get, 'http://depay.fi/v2/records/1?via=collection')
24
+ .to_return(body: {
25
+ name: 'Steve'
26
+ }.to_json)
27
+ end
28
+
29
+ let!(:request_item_2) do
30
+ stub_request(:get, 'http://depay.fi/v2/records/2?via=collection')
31
+ .to_return(body: {
32
+ name: 'John'
33
+ }.to_json)
34
+ end
35
+
36
+ it 'expands collections that just contains links' do
37
+ records = Record.where(color: 'blue').expanded(params: { via: 'collection' })
38
+ expect(records[0].name).to eq 'Steve'
39
+ expect(records[1].name).to eq 'John'
40
+ assert_requested request_collection
41
+ assert_requested request_item_1
42
+ assert_requested request_item_2
43
+ end
44
+
45
+ context 'without options' do
46
+ let!(:request_item_1) do
47
+ stub_request(:get, 'http://depay.fi/v2/records/1')
48
+ .to_return(body: {
49
+ name: 'Steve'
50
+ }.to_json)
51
+ end
52
+
53
+ let!(:request_item_2) do
54
+ stub_request(:get, 'http://depay.fi/v2/records/2')
55
+ .to_return(body: {
56
+ name: 'John'
57
+ }.to_json)
58
+ end
59
+
60
+ it 'works also without options' do
61
+ records = Record.where(color: 'blue').expanded
62
+ expect(records[0].name).to eq 'Steve'
63
+ expect(records[1].name).to eq 'John'
64
+ assert_requested request_collection
65
+ assert_requested request_item_1
66
+ assert_requested request_item_2
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Record do
6
+ before do
7
+ class Record < DHS::Record
8
+ endpoint 'http://datastore/records/'
9
+ end
10
+ end
11
+
12
+ context 'fetch' do
13
+ context 'to resolve the chain' do
14
+ let!(:request_stub) do
15
+ stub_request(:get, 'http://datastore/records/?available=true&color=blue&range=%3E26')
16
+ .to_return(body: [{
17
+ name: 'Steve'
18
+ }].to_json)
19
+ end
20
+
21
+ it 'resolves chains' do
22
+ records = Record.where(color: 'blue').where(range: '>26', available: true).fetch
23
+ expect(request_stub).to have_been_requested
24
+ expect(records.first.name).to eq 'Steve'
25
+ end
26
+ end
27
+
28
+ context 'to fetch the first response' do
29
+ let!(:request_stub) do
30
+ stub_request(:get, 'http://datastore/records/')
31
+ .to_return(body: [{ name: 'Steve' }])
32
+ end
33
+
34
+ it 'does fetch the first response if requested directly on the record' do
35
+ records = Record.fetch
36
+ expect(records.first.name).to eq 'Steve'
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Record do
6
+ before do
7
+ class Record < DHS::Record
8
+ endpoint 'http://datastore/records/'
9
+ end
10
+ stub_request(:get, 'http://datastore/records/?limit=1&name=Steve&color=blue')
11
+ .to_return(body: [{ name: 'Steve', color: 'blue' }].to_json)
12
+ end
13
+
14
+ it 'allows chaining find_by' do
15
+ Record.options(params: { color: 'blue' }).find_by(name: 'Steve')
16
+ end
17
+
18
+ it 'allows chaining find_by!' do
19
+ Record.options(params: { color: 'blue' }).find_by!(name: 'Steve')
20
+ end
21
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Record do
6
+ let(:datastore) { 'http://depay.fi/v2' }
7
+
8
+ before do
9
+ DHC.config.placeholder(:datastore, datastore)
10
+ class Record < DHS::Record
11
+ endpoint '{+datastore}/content-ads/{campaign_id}/feedbacks'
12
+ endpoint '{+datastore}/content-ads/{campaign_id}/feedbacks/{id}'
13
+ endpoint '{+datastore}/feedbacks'
14
+ endpoint '{+datastore}/feedbacks/{id}'
15
+ end
16
+ end
17
+
18
+ context 'find_by' do
19
+ it 'finds a single record' do
20
+ stub_request(:get, "#{datastore}/feedbacks/z12f-3asm3ngals?limit=1")
21
+ .to_return(status: 200, body: load_json(:feedback))
22
+ record = Record.find_by(id: 'z12f-3asm3ngals')
23
+ expect(record.source_id).to be_kind_of String
24
+ expect(record).to be_kind_of Record
25
+ end
26
+
27
+ it 'returns nil if no record was found' do
28
+ stub_request(:get, "#{datastore}/feedbacks/something-inexistent?limit=1")
29
+ .to_return(status: 404)
30
+ expect(
31
+ Record.find_by(id: 'something-inexistent')
32
+ ).to eq nil
33
+ end
34
+
35
+ it 'return first item by parameters' do
36
+ json = load_json(:feedbacks)
37
+ stub_request(:get, "#{datastore}/feedbacks?has_reviews=true&limit=1")
38
+ .to_return(status: 200, body: json)
39
+ expect(
40
+ Record.find_by(has_reviews: true).id
41
+ ).to eq json['items'].first['id']
42
+ end
43
+
44
+ it 'returns nil if empty id' do
45
+ expect { Record.find_by(id: '') }.to raise_error DHS::Unprocessable
46
+ end
47
+
48
+ context 'when record has custom configurations for limit_key' do
49
+ before do
50
+ class Record < DHS::Record
51
+ endpoint '{+datastore}/feedbacks/{id}'
52
+ configuration(
53
+ limit_key: { body: %i[response max], parameter: :max }
54
+ )
55
+ end
56
+ end
57
+
58
+ it 'finds a single record with max parameter' do
59
+ stub_request(:get, "#{datastore}/feedbacks/z12f-3asm3ngals?max=1")
60
+ .to_return(status: 200, body: load_json(:feedback))
61
+ record = Record.find_by(id: 'z12f-3asm3ngals')
62
+ expect(record.source_id).to be_kind_of String
63
+ expect(record).to be_kind_of Record
64
+ end
65
+ end
66
+ end
67
+
68
+ context 'find_by!' do
69
+ it 'raises if nothing was found with parameters' do
70
+ stub_request(:get, "#{datastore}/feedbacks?has_reviews=true&limit=1")
71
+ .to_return(status: 200, body: { items: [] }.to_json)
72
+ expect { Record.find_by!(has_reviews: true) }
73
+ .to raise_error DHC::NotFound
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Collection do
6
+ let(:total) { 443 }
7
+
8
+ let(:limit) { 100 }
9
+
10
+ def api_response(ids, offset)
11
+ records = ids.map { |i| { id: i } }
12
+ {
13
+ items: records,
14
+ total: total,
15
+ limit: limit,
16
+ offset: offset
17
+ }.to_json
18
+ end
19
+
20
+ let(:datastore) { 'http://depay.fi/v2' }
21
+
22
+ before do
23
+ DHC.config.placeholder('datastore', datastore)
24
+ class Record < DHS::Record
25
+ endpoint '{+datastore}/{campaign_id}/feedbacks'
26
+ endpoint '{+datastore}/feedbacks'
27
+ end
28
+ end
29
+
30
+ context 'find_each' do
31
+ it 'processes each record by fetching records in batches' do
32
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=1").to_return(status: 200, body: api_response((1..100).to_a, 1))
33
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=101").to_return(status: 200, body: api_response((101..200).to_a, 101))
34
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=201").to_return(status: 200, body: api_response((201..300).to_a, 201))
35
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=301").to_return(status: 200, body: api_response((301..400).to_a, 301))
36
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=401").to_return(status: 200, body: api_response((401..total).to_a, 401))
37
+ count = 0
38
+ Record.find_each do |record|
39
+ count += 1
40
+ expect(record.id).to eq(count)
41
+ expect(record).to be_kind_of Record
42
+ expect(record._proxy).to be_kind_of DHS::Item
43
+ end
44
+ expect(count).to eq total
45
+ end
46
+
47
+ it 'passes options to the requests made' do
48
+ request = stub_request(:get, 'http://depay.fi/v2/feedbacks?limit=100&offset=1')
49
+ .with(headers: { 'Authorization' => 'Bearer 123' })
50
+ .to_return(body: {
51
+ items: []
52
+ }.to_json)
53
+ Record.find_each(headers: { 'Authorization' => 'Bearer 123' }) { |record| }
54
+ expect(request).to have_been_made
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Collection do
6
+ let(:total) { 443 }
7
+
8
+ let(:limit) { 100 }
9
+
10
+ def api_response(ids, offset, options = {})
11
+ records = ids.map { |i| { id: i } }
12
+ {
13
+ options.fetch(:items_key, :items) => records,
14
+ options.fetch(:total_key, :total) => total,
15
+ options.fetch(:limit_key, :limit) => limit,
16
+ options.fetch(:pagination_key, :offset) => offset
17
+ }.to_json
18
+ end
19
+
20
+ let(:datastore) { 'http://depay.fi/v2' }
21
+
22
+ before do
23
+ DHC.config.placeholder('datastore', datastore)
24
+ class Record < DHS::Record
25
+ endpoint '{+datastore}/{campaign_id}/feedbacks'
26
+ endpoint '{+datastore}/feedbacks'
27
+ end
28
+ end
29
+
30
+ context 'find_batches' do
31
+ it 'processes records in batches' do
32
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=1").to_return(status: 200, body: api_response((1..100).to_a, 1))
33
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=101").to_return(status: 200, body: api_response((101..200).to_a, 101))
34
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=201").to_return(status: 200, body: api_response((201..300).to_a, 201))
35
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=301").to_return(status: 200, body: api_response((301..400).to_a, 301))
36
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=401").to_return(status: 200, body: api_response((401..total).to_a, 401))
37
+ length = 0
38
+ Record.find_in_batches do |records|
39
+ length += records.length
40
+ expect(records).to be_kind_of Record
41
+ expect(records._proxy).to be_kind_of DHS::Collection
42
+ end
43
+ expect(length).to eq total
44
+ end
45
+
46
+ it 'adapts to backend max limit' do
47
+ stub_request(:get, "#{datastore}/feedbacks?limit=230&offset=1").to_return(status: 200, body: api_response((1..100).to_a, 1))
48
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=101").to_return(status: 200, body: api_response((101..200).to_a, 101))
49
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=201").to_return(status: 200, body: api_response((201..300).to_a, 201))
50
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=301").to_return(status: 200, body: api_response((301..400).to_a, 301))
51
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=401").to_return(status: 200, body: api_response((401..total).to_a, 401))
52
+ length = 0
53
+ Record.find_in_batches(batch_size: 230) do |records|
54
+ length += records.length
55
+ expect(records).to be_kind_of Record
56
+ expect(records._proxy).to be_kind_of DHS::Collection
57
+ end
58
+ expect(length).to eq total
59
+ end
60
+
61
+ it 'forwards offset' do
62
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=401").to_return(status: 200, body: api_response((401..total).to_a, 401))
63
+ Record.find_in_batches(start: 401) do |records|
64
+ expect(records.length).to eq(total - 400)
65
+ end
66
+ end
67
+ end
68
+
69
+ context 'configured pagination' do
70
+ before do
71
+ class Record < DHS::Record
72
+ endpoint '{+datastore}/{campaign_id}/feedbacks'
73
+ endpoint '{+datastore}/feedbacks'
74
+ configuration items_key: 'docs', limit_key: 'size', pagination_key: 'start', pagination_strategy: 'start', total_key: 'totalResults'
75
+ end
76
+ end
77
+
78
+ let(:options) { { items_key: 'docs', limit_key: 'size', pagination_key: 'start', total_key: 'totalResults' } }
79
+
80
+ it 'capable to do batch processing with configured pagination' do
81
+ stub_request(:get, "#{datastore}/feedbacks?size=230&start=1").to_return(status: 200, body: api_response((1..100).to_a, 1, options))
82
+ stub_request(:get, "#{datastore}/feedbacks?size=100&start=101").to_return(status: 200, body: api_response((101..200).to_a, 101, options))
83
+ stub_request(:get, "#{datastore}/feedbacks?size=100&start=201").to_return(status: 200, body: api_response((201..300).to_a, 201, options))
84
+ stub_request(:get, "#{datastore}/feedbacks?size=100&start=301").to_return(status: 200, body: api_response((301..400).to_a, 301, options))
85
+ stub_request(:get, "#{datastore}/feedbacks?size=100&start=401").to_return(status: 200, body: api_response((401..total).to_a, 401, options))
86
+ length = 0
87
+ Record.find_in_batches(batch_size: 230) do |records|
88
+ length += records.length
89
+ expect(records).to be_kind_of Record
90
+ expect(records._proxy).to be_kind_of DHS::Collection
91
+ end
92
+ expect(length).to eq total
93
+ end
94
+ end
95
+
96
+ context 'pagination with nested response' do
97
+ before do
98
+ class Record < DHS::Record
99
+ endpoint '{+datastore}/{campaign_id}/feedbacks'
100
+ endpoint '{+datastore}/feedbacks'
101
+ configuration items_key: %i[response docs], limit_key: { body: %i[response size], parameter: :size }, pagination_key: { body: %i[response start], parameter: :start }, pagination_strategy: :start, total_key: %i[response totalResults]
102
+ end
103
+ end
104
+
105
+ let(:options) { { items_key: 'docs', limit_key: 'size', pagination_key: 'start', total_key: 'totalResults' } }
106
+
107
+ it 'capable to do batch processing with configured pagination' do
108
+ stub_request(:get, "#{datastore}/feedbacks?size=230&start=1").to_return(status: 200, body: "{\"response\":#{api_response((1..100).to_a, 1, options)}}")
109
+ stub_request(:get, "#{datastore}/feedbacks?size=100&start=101").to_return(status: 200, body: "{\"response\":#{api_response((101..200).to_a, 101, options)}}")
110
+ stub_request(:get, "#{datastore}/feedbacks?size=100&start=201").to_return(status: 200, body: "{\"response\":#{api_response((201..300).to_a, 201, options)}}")
111
+ stub_request(:get, "#{datastore}/feedbacks?size=100&start=301").to_return(status: 200, body: "{\"response\":#{api_response((301..400).to_a, 301, options)}}")
112
+ stub_request(:get, "#{datastore}/feedbacks?size=100&start=401").to_return(status: 200, body: "{\"response\":#{api_response((401..total).to_a, 401, options)}}")
113
+ length = 0
114
+ Record.find_in_batches(batch_size: 230) do |records|
115
+ length += records.length
116
+ expect(records).to be_kind_of Record
117
+ expect(records._proxy).to be_kind_of DHS::Collection
118
+ end
119
+ expect(length).to eq total
120
+ end
121
+ end
122
+ end