lhs 24.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 (306) hide show
  1. checksums.yaml +7 -0
  2. data/.bundler-version +1 -0
  3. data/.gitignore +39 -0
  4. data/.rubocop.localch.yml +325 -0
  5. data/.rubocop.yml +52 -0
  6. data/.ruby-version +1 -0
  7. data/Gemfile +4 -0
  8. data/Gemfile.activesupport4 +5 -0
  9. data/Gemfile.activesupport5 +4 -0
  10. data/LICENSE +674 -0
  11. data/README.md +2836 -0
  12. data/Rakefile +25 -0
  13. data/cider-ci.yml +6 -0
  14. data/cider-ci/bin/bundle +51 -0
  15. data/cider-ci/bin/ruby_install +8 -0
  16. data/cider-ci/bin/ruby_version +25 -0
  17. data/cider-ci/jobs/rspec-activesupport-4.yml +27 -0
  18. data/cider-ci/jobs/rspec-activesupport-5.yml +26 -0
  19. data/cider-ci/jobs/rspec-activesupport-latest.yml +24 -0
  20. data/cider-ci/jobs/rubocop.yml +18 -0
  21. data/cider-ci/task_components/bundle.yml +22 -0
  22. data/cider-ci/task_components/rspec.yml +37 -0
  23. data/cider-ci/task_components/rubocop.yml +29 -0
  24. data/cider-ci/task_components/ruby.yml +15 -0
  25. data/friday.yml +2 -0
  26. data/lhs.gemspec +43 -0
  27. data/lib/lhs.rb +100 -0
  28. data/lib/lhs/collection.rb +84 -0
  29. data/lib/lhs/complex.rb +158 -0
  30. data/lib/lhs/concerns/autoload_records.rb +55 -0
  31. data/lib/lhs/concerns/collection/handle_nested.rb +43 -0
  32. data/lib/lhs/concerns/collection/internal_collection.rb +49 -0
  33. data/lib/lhs/concerns/configuration.rb +20 -0
  34. data/lib/lhs/concerns/data/becomes.rb +18 -0
  35. data/lib/lhs/concerns/data/equality.rb +14 -0
  36. data/lib/lhs/concerns/data/json.rb +14 -0
  37. data/lib/lhs/concerns/data/to_hash.rb +14 -0
  38. data/lib/lhs/concerns/inspect.rb +70 -0
  39. data/lib/lhs/concerns/is_href.rb +15 -0
  40. data/lib/lhs/concerns/item/destroy.rb +38 -0
  41. data/lib/lhs/concerns/item/endpoint_lookup.rb +27 -0
  42. data/lib/lhs/concerns/item/save.rb +55 -0
  43. data/lib/lhs/concerns/item/update.rb +50 -0
  44. data/lib/lhs/concerns/item/validation.rb +61 -0
  45. data/lib/lhs/concerns/o_auth.rb +25 -0
  46. data/lib/lhs/concerns/option_blocks.rb +26 -0
  47. data/lib/lhs/concerns/proxy/accessors.rb +132 -0
  48. data/lib/lhs/concerns/proxy/create.rb +45 -0
  49. data/lib/lhs/concerns/proxy/link.rb +25 -0
  50. data/lib/lhs/concerns/proxy/problems.rb +27 -0
  51. data/lib/lhs/concerns/record/attribute_assignment.rb +25 -0
  52. data/lib/lhs/concerns/record/batch.rb +40 -0
  53. data/lib/lhs/concerns/record/chainable.rb +465 -0
  54. data/lib/lhs/concerns/record/configuration.rb +103 -0
  55. data/lib/lhs/concerns/record/create.rb +24 -0
  56. data/lib/lhs/concerns/record/custom_setters.rb +22 -0
  57. data/lib/lhs/concerns/record/destroy.rb +18 -0
  58. data/lib/lhs/concerns/record/endpoints.rb +108 -0
  59. data/lib/lhs/concerns/record/equality.rb +14 -0
  60. data/lib/lhs/concerns/record/find.rb +86 -0
  61. data/lib/lhs/concerns/record/find_by.rb +38 -0
  62. data/lib/lhs/concerns/record/first.rb +20 -0
  63. data/lib/lhs/concerns/record/href_for.rb +19 -0
  64. data/lib/lhs/concerns/record/last.rb +27 -0
  65. data/lib/lhs/concerns/record/mapping.rb +25 -0
  66. data/lib/lhs/concerns/record/merge.rb +26 -0
  67. data/lib/lhs/concerns/record/model.rb +23 -0
  68. data/lib/lhs/concerns/record/pagination.rb +47 -0
  69. data/lib/lhs/concerns/record/provider.rb +23 -0
  70. data/lib/lhs/concerns/record/relations.rb +26 -0
  71. data/lib/lhs/concerns/record/request.rb +620 -0
  72. data/lib/lhs/concerns/record/scope.rb +25 -0
  73. data/lib/lhs/concerns/record/tracing.rb +24 -0
  74. data/lib/lhs/concerns/record/update.rb +17 -0
  75. data/lib/lhs/config.rb +24 -0
  76. data/lib/lhs/data.rb +165 -0
  77. data/lib/lhs/endpoint.rb +12 -0
  78. data/lib/lhs/interceptors/auto_oauth/interceptor.rb +33 -0
  79. data/lib/lhs/interceptors/auto_oauth/thread_registry.rb +18 -0
  80. data/lib/lhs/interceptors/extended_rollbar/handler.rb +40 -0
  81. data/lib/lhs/interceptors/extended_rollbar/interceptor.rb +20 -0
  82. data/lib/lhs/interceptors/extended_rollbar/thread_registry.rb +19 -0
  83. data/lib/lhs/interceptors/request_cycle_cache/interceptor.rb +41 -0
  84. data/lib/lhs/interceptors/request_cycle_cache/thread_registry.rb +18 -0
  85. data/lib/lhs/item.rb +59 -0
  86. data/lib/lhs/pagination/base.rb +90 -0
  87. data/lib/lhs/pagination/link.rb +21 -0
  88. data/lib/lhs/pagination/offset.rb +22 -0
  89. data/lib/lhs/pagination/page.rb +18 -0
  90. data/lib/lhs/pagination/start.rb +22 -0
  91. data/lib/lhs/problems/base.rb +113 -0
  92. data/lib/lhs/problems/errors.rb +69 -0
  93. data/lib/lhs/problems/nested/base.rb +54 -0
  94. data/lib/lhs/problems/nested/errors.rb +16 -0
  95. data/lib/lhs/problems/nested/warnings.rb +15 -0
  96. data/lib/lhs/problems/warnings.rb +24 -0
  97. data/lib/lhs/proxy.rb +68 -0
  98. data/lib/lhs/railtie.rb +34 -0
  99. data/lib/lhs/record.rb +112 -0
  100. data/lib/lhs/rspec.rb +10 -0
  101. data/lib/lhs/test/stubbable_records.rb +34 -0
  102. data/lib/lhs/unprocessable.rb +6 -0
  103. data/lib/lhs/version.rb +5 -0
  104. data/script/ci/build.sh +18 -0
  105. data/spec/auto_oauth_spec.rb +169 -0
  106. data/spec/autoloading_spec.rb +48 -0
  107. data/spec/collection/accessors_spec.rb +31 -0
  108. data/spec/collection/collection_items_spec.rb +44 -0
  109. data/spec/collection/configurable_spec.rb +43 -0
  110. data/spec/collection/delegate_spec.rb +21 -0
  111. data/spec/collection/enumerable_spec.rb +27 -0
  112. data/spec/collection/href_spec.rb +17 -0
  113. data/spec/collection/meta_data_spec.rb +57 -0
  114. data/spec/collection/respond_to_spec.rb +20 -0
  115. data/spec/collection/to_a_spec.rb +34 -0
  116. data/spec/collection/to_ary_spec.rb +40 -0
  117. data/spec/collection/without_object_items_spec.rb +27 -0
  118. data/spec/complex/reduce_spec.rb +202 -0
  119. data/spec/concerns/record/request_spec.rb +78 -0
  120. data/spec/data/collection_spec.rb +56 -0
  121. data/spec/data/equality_spec.rb +23 -0
  122. data/spec/data/inspect_spec.rb +88 -0
  123. data/spec/data/is_item_or_collection_spec.rb +40 -0
  124. data/spec/data/item_spec.rb +106 -0
  125. data/spec/data/merge_spec.rb +27 -0
  126. data/spec/data/parent_spec.rb +39 -0
  127. data/spec/data/raw_spec.rb +48 -0
  128. data/spec/data/respond_to_spec.rb +26 -0
  129. data/spec/data/root_spec.rb +25 -0
  130. data/spec/data/select_spec.rb +27 -0
  131. data/spec/data/to_ary_spec.rb +28 -0
  132. data/spec/data/to_json_spec.rb +68 -0
  133. data/spec/dummy/Rakefile +8 -0
  134. data/spec/dummy/app/assets/images/.keep +0 -0
  135. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  136. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  137. data/spec/dummy/app/controllers/application_controller.rb +26 -0
  138. data/spec/dummy/app/controllers/automatic_authentication_controller.rb +29 -0
  139. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  140. data/spec/dummy/app/controllers/error_handling_with_chains_controller.rb +36 -0
  141. data/spec/dummy/app/controllers/extended_rollbar_controller.rb +10 -0
  142. data/spec/dummy/app/controllers/option_blocks_controller.rb +15 -0
  143. data/spec/dummy/app/controllers/request_cycle_cache_controller.rb +27 -0
  144. data/spec/dummy/app/helpers/application_helper.rb +4 -0
  145. data/spec/dummy/app/mailers/.keep +0 -0
  146. data/spec/dummy/app/models/.keep +0 -0
  147. data/spec/dummy/app/models/concerns/.keep +0 -0
  148. data/spec/dummy/app/models/dummy_customer.rb +6 -0
  149. data/spec/dummy/app/models/dummy_record.rb +6 -0
  150. data/spec/dummy/app/models/dummy_record_with_auto_oauth_provider.rb +6 -0
  151. data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers1.rb +7 -0
  152. data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers2.rb +7 -0
  153. data/spec/dummy/app/models/dummy_record_with_multiple_providers_per_endpoint.rb +6 -0
  154. data/spec/dummy/app/models/dummy_record_with_oauth.rb +7 -0
  155. data/spec/dummy/app/models/dummy_user.rb +6 -0
  156. data/spec/dummy/app/models/providers/customer_system.rb +7 -0
  157. data/spec/dummy/app/models/providers/internal_services.rb +7 -0
  158. data/spec/dummy/app/views/error_handling_with_chains/error.html.erb +1 -0
  159. data/spec/dummy/app/views/error_handling_with_chains/show.html.erb +3 -0
  160. data/spec/dummy/app/views/form_for.html.erb +5 -0
  161. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  162. data/spec/dummy/bin/bundle +5 -0
  163. data/spec/dummy/bin/rails +6 -0
  164. data/spec/dummy/bin/rake +6 -0
  165. data/spec/dummy/config.ru +6 -0
  166. data/spec/dummy/config/application.rb +16 -0
  167. data/spec/dummy/config/boot.rb +7 -0
  168. data/spec/dummy/config/environment.rb +7 -0
  169. data/spec/dummy/config/environments/development.rb +36 -0
  170. data/spec/dummy/config/environments/production.rb +77 -0
  171. data/spec/dummy/config/environments/test.rb +40 -0
  172. data/spec/dummy/config/initializers/assets.rb +10 -0
  173. data/spec/dummy/config/initializers/backtrace_silencers.rb +9 -0
  174. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  175. data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  176. data/spec/dummy/config/initializers/inflections.rb +18 -0
  177. data/spec/dummy/config/initializers/mime_types.rb +6 -0
  178. data/spec/dummy/config/initializers/rollbar.rb +9 -0
  179. data/spec/dummy/config/initializers/session_store.rb +5 -0
  180. data/spec/dummy/config/initializers/wrap_parameters.rb +11 -0
  181. data/spec/dummy/config/locales/en.yml +23 -0
  182. data/spec/dummy/config/routes.rb +27 -0
  183. data/spec/dummy/config/secrets.yml +22 -0
  184. data/spec/dummy/lib/assets/.keep +0 -0
  185. data/spec/dummy/public/404.html +67 -0
  186. data/spec/dummy/public/422.html +67 -0
  187. data/spec/dummy/public/500.html +66 -0
  188. data/spec/dummy/public/favicon.ico +0 -0
  189. data/spec/endpoint/for_url_spec.rb +27 -0
  190. data/spec/extended_rollbar_spec.rb +67 -0
  191. data/spec/item/access_errors_spec.rb +33 -0
  192. data/spec/item/accessors_spec.rb +21 -0
  193. data/spec/item/add_error_spec.rb +21 -0
  194. data/spec/item/becomes_spec.rb +38 -0
  195. data/spec/item/blacklisted_keywords_spec.rb +30 -0
  196. data/spec/item/delegate_spec.rb +32 -0
  197. data/spec/item/destroy_spec.rb +113 -0
  198. data/spec/item/dig_spec.rb +29 -0
  199. data/spec/item/error_codes_spec.rb +55 -0
  200. data/spec/item/errors_spec.rb +324 -0
  201. data/spec/item/fetch_spec.rb +39 -0
  202. data/spec/item/getter_spec.rb +24 -0
  203. data/spec/item/internal_data_structure_spec.rb +37 -0
  204. data/spec/item/map_spec.rb +46 -0
  205. data/spec/item/nested_errors_spec.rb +28 -0
  206. data/spec/item/partial_update_spec.rb +170 -0
  207. data/spec/item/respond_to_spec.rb +31 -0
  208. data/spec/item/save_spec.rb +115 -0
  209. data/spec/item/setter_spec.rb +44 -0
  210. data/spec/item/translate_errors_spec.rb +257 -0
  211. data/spec/item/update_spec.rb +161 -0
  212. data/spec/item/validation_spec.rb +131 -0
  213. data/spec/item/warning_codes_spec.rb +55 -0
  214. data/spec/item/warnings_spec.rb +52 -0
  215. data/spec/option_blocks/ensure_reset_between_requests_spec.rb +23 -0
  216. data/spec/option_blocks/main_spec.rb +55 -0
  217. data/spec/pagination/link/current_page_spec.rb +19 -0
  218. data/spec/pagination/link/pages_left_spec.rb +36 -0
  219. data/spec/pagination/link/parallel_spec.rb +19 -0
  220. data/spec/pagination/link/total_spec.rb +46 -0
  221. data/spec/pagination/offset/pages_left_spec.rb +26 -0
  222. data/spec/pagination/parameters_spec.rb +61 -0
  223. data/spec/proxy/create_sub_resource_spec.rb +182 -0
  224. data/spec/proxy/load_spec.rb +75 -0
  225. data/spec/proxy/record_identification_spec.rb +35 -0
  226. data/spec/rails_helper.rb +13 -0
  227. data/spec/record/all_spec.rb +133 -0
  228. data/spec/record/attribute_assignment_spec.rb +28 -0
  229. data/spec/record/build_spec.rb +26 -0
  230. data/spec/record/cast_nested_data_spec.rb +78 -0
  231. data/spec/record/create_spec.rb +160 -0
  232. data/spec/record/creation_failed_spec.rb +55 -0
  233. data/spec/record/custom_setters_spec.rb +45 -0
  234. data/spec/record/definitions_spec.rb +29 -0
  235. data/spec/record/destroy_spec.rb +38 -0
  236. data/spec/record/dig_configuration_spec.rb +75 -0
  237. data/spec/record/dup_spec.rb +20 -0
  238. data/spec/record/endpoint_inheritance_spec.rb +65 -0
  239. data/spec/record/endpoint_options_spec.rb +52 -0
  240. data/spec/record/endpoint_priorities_spec.rb +26 -0
  241. data/spec/record/endpoints_spec.rb +96 -0
  242. data/spec/record/equality_spec.rb +27 -0
  243. data/spec/record/error_handling_integration_spec.rb +25 -0
  244. data/spec/record/error_handling_spec.rb +40 -0
  245. data/spec/record/expanded_spec.rb +69 -0
  246. data/spec/record/fetch_spec.rb +41 -0
  247. data/spec/record/find_by_chains_spec.rb +21 -0
  248. data/spec/record/find_by_spec.rb +76 -0
  249. data/spec/record/find_each_spec.rb +57 -0
  250. data/spec/record/find_in_batches_spec.rb +122 -0
  251. data/spec/record/find_in_parallel_spec.rb +67 -0
  252. data/spec/record/find_spec.rb +104 -0
  253. data/spec/record/first_spec.rb +39 -0
  254. data/spec/record/force_merge_spec.rb +56 -0
  255. data/spec/record/handle_includes_errors_spec.rb +32 -0
  256. data/spec/record/has_many_spec.rb +120 -0
  257. data/spec/record/has_one_spec.rb +116 -0
  258. data/spec/record/href_for_spec.rb +25 -0
  259. data/spec/record/ignore_errors_spec.rb +139 -0
  260. data/spec/record/immutable_chains_spec.rb +22 -0
  261. data/spec/record/includes_first_page_spec.rb +737 -0
  262. data/spec/record/includes_spec.rb +693 -0
  263. data/spec/record/includes_warning_spec.rb +46 -0
  264. data/spec/record/item_key_spec.rb +81 -0
  265. data/spec/record/items_created_key_configuration_spec.rb +37 -0
  266. data/spec/record/last_spec.rb +68 -0
  267. data/spec/record/loading_twice_spec.rb +19 -0
  268. data/spec/record/mapping_spec.rb +102 -0
  269. data/spec/record/model_name_spec.rb +17 -0
  270. data/spec/record/new_spec.rb +106 -0
  271. data/spec/record/options_getter_spec.rb +26 -0
  272. data/spec/record/options_spec.rb +164 -0
  273. data/spec/record/paginatable_collection_spec.rb +360 -0
  274. data/spec/record/pagination_chain_spec.rb +101 -0
  275. data/spec/record/pagination_links_spec.rb +72 -0
  276. data/spec/record/pagination_spec.rb +71 -0
  277. data/spec/record/persisted_spec.rb +52 -0
  278. data/spec/record/provider_spec.rb +41 -0
  279. data/spec/record/references_spec.rb +53 -0
  280. data/spec/record/relation_caching_spec.rb +121 -0
  281. data/spec/record/reload_by_id_spec.rb +43 -0
  282. data/spec/record/reload_spec.rb +65 -0
  283. data/spec/record/request_spec.rb +90 -0
  284. data/spec/record/save_spec.rb +40 -0
  285. data/spec/record/scope_chains_spec.rb +39 -0
  286. data/spec/record/select_spec.rb +17 -0
  287. data/spec/record/to_ary_spec.rb +65 -0
  288. data/spec/record/to_hash_spec.rb +22 -0
  289. data/spec/record/to_json_spec.rb +22 -0
  290. data/spec/record/tracing_spec.rb +154 -0
  291. data/spec/record/update_spec.rb +62 -0
  292. data/spec/record/where_chains_spec.rb +57 -0
  293. data/spec/record/where_spec.rb +62 -0
  294. data/spec/record/where_values_hash_spec.rb +32 -0
  295. data/spec/request_cycle_cache_spec.rb +106 -0
  296. data/spec/require_lhs_spec.rb +9 -0
  297. data/spec/spec_helper.rb +6 -0
  298. data/spec/stubs/all_spec.rb +72 -0
  299. data/spec/support/fixtures/json/feedback.json +11 -0
  300. data/spec/support/fixtures/json/feedbacks.json +174 -0
  301. data/spec/support/fixtures/json/localina_content_ad.json +23 -0
  302. data/spec/support/load_json.rb +5 -0
  303. data/spec/support/request_cycle_cache.rb +10 -0
  304. data/spec/support/reset.rb +67 -0
  305. data/spec/views/form_for_spec.rb +20 -0
  306. metadata +776 -0
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHS::Record do
6
+ before do
7
+ class Record < LHS::Record
8
+ endpoint 'http://datastore/records/'
9
+ end
10
+ end
11
+
12
+ context 'fetch' do
13
+
14
+ context 'to resolve the chain' do
15
+ let!(:request_stub) do
16
+ stub_request(:get, "http://datastore/records/?available=true&color=blue&range=%3E26")
17
+ .to_return(body: [{
18
+ name: 'Steve'
19
+ }].to_json)
20
+ end
21
+
22
+ it 'resolves chains' do
23
+ records = Record.where(color: 'blue').where(range: '>26', available: true).fetch
24
+ expect(request_stub).to have_been_requested
25
+ expect(records.first.name).to eq 'Steve'
26
+ end
27
+ end
28
+
29
+ context 'to fetch the first response' do
30
+ let!(:request_stub) do
31
+ stub_request(:get, "http://datastore/records/")
32
+ .to_return(body: [{ name: 'Steve' }])
33
+ end
34
+
35
+ it 'does fetch the first response if requested directly on the record' do
36
+ records = Record.fetch
37
+ expect(records.first.name).to eq 'Steve'
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHS::Record do
6
+ before do
7
+ class Record < LHS::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 LHS::Record do
6
+ let(:datastore) { 'http://local.ch/v2' }
7
+
8
+ before do
9
+ LHC.config.placeholder(:datastore, datastore)
10
+ class Record < LHS::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 LHS::Unprocessable
46
+ end
47
+
48
+ context 'when record has custom configurations for limit_key' do
49
+ before do
50
+ class Record < LHS::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 LHC::NotFound
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHS::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://local.ch/v2' }
21
+
22
+ before do
23
+ LHC.config.placeholder('datastore', datastore)
24
+ class Record < LHS::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 LHS::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://local.ch/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 LHS::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://local.ch/v2' }
21
+
22
+ before do
23
+ LHC.config.placeholder('datastore', datastore)
24
+ class Record < LHS::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 LHS::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 LHS::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 < LHS::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 LHS::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 < LHS::Record
99
+ endpoint '{+datastore}/{campaign_id}/feedbacks'
100
+ endpoint '{+datastore}/feedbacks'
101
+ configuration items_key: [:response, :docs], limit_key: { body: [:response, :size], parameter: :size }, pagination_key: { body: [:response, :start], parameter: :start }, pagination_strategy: :start, total_key: [: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 LHS::Collection
118
+ end
119
+ expect(length).to eq total
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHS::Record do
6
+ before do
7
+ class Record < LHS::Record
8
+ endpoint 'http://datastore/records/{id}'
9
+ end
10
+ end
11
+
12
+ context 'find in parallel' do
13
+ before do
14
+ stub_request(:get, "http://datastore/records/1").to_return(status: 200, body: { id: 1 }.to_json)
15
+ stub_request(:get, "http://datastore/records/3").to_return(status: 200, body: { id: 3 }.to_json)
16
+ end
17
+
18
+ it 'finds records in parallel' do
19
+ stub_request(:get, "http://datastore/records/2").to_return(status: 200, body: { id: 2 }.to_json)
20
+ allow(Record).to receive(:request).and_call_original
21
+ data = Record.find([1, 2, 3])
22
+ expect(Record).to have_received(:request).once
23
+ expect(data[0].id).to eq 1
24
+ expect(data[1].id).to eq 2
25
+ expect(data[2].id).to eq 3
26
+ end
27
+
28
+ it 'raises an exeption if one of the parallel request fails' do
29
+ stub_request(:get, "http://datastore/records/2").to_return(status: 401)
30
+ expect(-> { Record.find([1, 2, 3]) }).to raise_error(LHC::Unauthorized)
31
+ end
32
+
33
+ it 'applies error handlers from the chain and returns whatever the error handler returns' do
34
+ stub_request(:get, "http://datastore/records/2").to_return(status: 401)
35
+ data = Record
36
+ .rescue(LHC::Unauthorized, ->(_response) { Record.new(name: 'unknown') })
37
+ .find(1, 2, 3)
38
+ expect(data[1].name).to eq 'unknown'
39
+ end
40
+ end
41
+
42
+ context 'find in parallel with extra methods' do
43
+ before do
44
+ class Record < LHS::Record
45
+ endpoint 'http://datastore/records/{id}'
46
+
47
+ def identifier
48
+ 123456
49
+ end
50
+ end
51
+
52
+ stub_request(:get, "http://datastore/records/1").to_return(status: 200, body: { id: 1 }.to_json)
53
+ stub_request(:get, "http://datastore/records/2").to_return(status: 200, body: { id: 2 }.to_json)
54
+ stub_request(:get, "http://datastore/records/3").to_return(status: 200, body: { id: 3 }.to_json)
55
+ end
56
+
57
+ it 'finds single record in parallel' do
58
+ data = Record.find([1])
59
+ expect(data[0].identifier).to eq 123456
60
+ end
61
+
62
+ it 'finds records in parallel' do
63
+ data = Record.find([1, 2, 3])
64
+ expect(data[0].identifier).to eq 123456
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHS::Record do
6
+ let(:datastore) { 'http://local.ch/v2' }
7
+
8
+ before do
9
+ LHC.config.placeholder(:datastore, datastore)
10
+ class Record < LHS::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' do
19
+ context 'finds a single unique record' do
20
+ before do
21
+ stub_request(:get, "#{datastore}/feedbacks/z12f-3asm3ngals")
22
+ .to_return(status: 200, body: load_json(:feedback))
23
+ end
24
+
25
+ it 'by id' do
26
+ record = Record.find('z12f-3asm3ngals')
27
+ expect(record).to be_kind_of Record
28
+ expect(record.source_id).to be_kind_of String
29
+ end
30
+
31
+ it 'by href' do
32
+ record = Record.find("#{datastore}/feedbacks/z12f-3asm3ngals")
33
+ expect(record).to be_kind_of Record
34
+ expect(record.source_id).to be_kind_of String
35
+ end
36
+ end
37
+
38
+ context 'endpoint without identifier' do
39
+ before do
40
+ class LatestAGB < LHS::Record
41
+ endpoint 'agbs/latest'
42
+ end
43
+ end
44
+
45
+ it 'finds a single unique record' do
46
+ stub_request(:get, 'http://agbs/latest')
47
+ .to_return(body: { pdf: 'http://local.ch/1.pdf' }.to_json)
48
+
49
+ expect(LatestAGB.find.pdf).to eq 'http://local.ch/1.pdf'
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ it 'raises if empty id' do
56
+ expect { Record.find('') }.to raise_error LHS::Unprocessable
57
+ end
58
+
59
+ it 'raises if nothing was found' do
60
+ stub_request(:get, "#{datastore}/feedbacks/not-existing")
61
+ .to_return(status: 404)
62
+ expect { Record.find('not-existing') }.to raise_error LHC::NotFound
63
+ end
64
+
65
+ it 'finds unique item by providing parameters' do
66
+ stub_request(:get, "#{datastore}/content-ads/123/feedbacks/123")
67
+ .to_return(body: "{}")
68
+ data = Record.find(campaign_id: '123', id: '123')
69
+ expect(data._proxy).to be_kind_of LHS::Item
70
+ end
71
+
72
+ it 'returns item in case of backend returning collection' do
73
+ data = JSON.parse(load_json(:feedbacks))
74
+ data['items'] = [data['items'].first]
75
+ stub_request(:get, "#{datastore}/content-ads/123/feedbacks/123")
76
+ .to_return(body: data.to_json)
77
+ data = Record.find(campaign_id: '123', id: '123')
78
+ expect(data._proxy).to be_kind_of LHS::Item
79
+ end
80
+
81
+ it 'fails when multiple items where found by parameters' do
82
+ stub_request(:get, "#{datastore}/content-ads/123/feedbacks/123")
83
+ .to_return(body: load_json(:feedbacks))
84
+ expect(lambda {
85
+ Record.find(campaign_id: '123', id: '123')
86
+ }).to raise_error LHC::NotFound
87
+ end
88
+
89
+ it 'fails when no item as found by parameters' do
90
+ data = JSON.parse(load_json(:feedbacks))
91
+ data['items'] = []
92
+ stub_request(:get, "#{datastore}/content-ads/123/feedbacks/123")
93
+ .to_return(body: data.to_json)
94
+ expect(lambda {
95
+ Record.find(campaign_id: '123', id: '123')
96
+ }).to raise_error LHC::NotFound
97
+ end
98
+
99
+ it 'raises if nothing was found with parameters' do
100
+ stub_request(:get, "#{datastore}/content-ads/123/feedbacks/123")
101
+ .to_return(status: 404)
102
+ expect { Record.find(campaign_id: '123', id: '123') }.to raise_error LHC::NotFound
103
+ end
104
+ end