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,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Record do
6
+ context 'all' do
7
+ before do
8
+ class Record < DHS::Record
9
+ endpoint 'http://datastore/feedbacks'
10
+ end
11
+ end
12
+
13
+ it 'is querying endpoint without pagination when using all' do
14
+ stub_request(:get, 'http://datastore/feedbacks?limit=100').to_return(body: { items: 300.times.map { { foo: 'bar' } }, total: 300 }.to_json)
15
+ records = Record.all
16
+ expect(records).to be_kind_of Record
17
+ expect(records.size).to eq(300)
18
+ end
19
+
20
+ context 'is chainable with where and works like where' do
21
+ let(:total) { 22 }
22
+ let(:limit) { 10 }
23
+ let!(:first_page_request) do
24
+ stub_request(:get, 'http://datastore/feedbacks?color=blue&limit=100')
25
+ .to_return(body: { items: 10.times.map { { foo: 'bar' } }, total: total, limit: limit, offset: 0 }.to_json)
26
+ end
27
+ let!(:second_page_request) do
28
+ stub_request(:get, "http://datastore/feedbacks?color=blue&limit=#{limit}&offset=10")
29
+ .to_return(body: { items: 10.times.map { { foo: 'bar' } }, total: total, limit: limit, offset: 10 }.to_json)
30
+ end
31
+ let!(:third_page_request) do
32
+ stub_request(:get, "http://datastore/feedbacks?color=blue&limit=#{limit}&offset=20")
33
+ .to_return(body: { items: 2.times.map { { foo: 'bar' } }, total: total, limit: limit, offset: 20 }.to_json)
34
+ end
35
+
36
+ it 'fetches all remote objects' do
37
+ records = Record.where(color: 'blue').all
38
+ expect(records.length).to eq total
39
+ expect(first_page_request).to have_been_requested.times(1)
40
+ expect(second_page_request).to have_been_requested.times(1)
41
+ expect(third_page_request).to have_been_requested.times(1)
42
+ records = Record.all.where(color: 'blue')
43
+ expect(records.length).to eq total
44
+ expect(first_page_request).to have_been_requested.times(2)
45
+ expect(second_page_request).to have_been_requested.times(2)
46
+ expect(third_page_request).to have_been_requested.times(2)
47
+ records = Record.all(color: 'blue')
48
+ expect(records.length).to eq total
49
+ expect(first_page_request).to have_been_requested.times(3)
50
+ expect(second_page_request).to have_been_requested.times(3)
51
+ expect(third_page_request).to have_been_requested.times(3)
52
+ end
53
+
54
+ it 'works in combination with include and includes' do
55
+ records = Record.includes_first_page(:product).includes(:options).all(color: 'blue')
56
+ expect(records.length).to eq total
57
+ expect(first_page_request).to have_been_requested.times(1)
58
+ expect(second_page_request).to have_been_requested.times(1)
59
+ expect(third_page_request).to have_been_requested.times(1)
60
+ end
61
+ end
62
+ end
63
+
64
+ context 'all without current page indicator' do
65
+ before do
66
+ class Category < DHS::Record
67
+ configuration(
68
+ items_key: %i(response results),
69
+ limit_key: :max,
70
+ pagination_key: :offset,
71
+ pagination_strategy: :offset
72
+ )
73
+
74
+ endpoint 'http://store/categories'
75
+ end
76
+ end
77
+
78
+ def stub_batch(url, items = 10)
79
+ stub_request(:get, url)
80
+ .to_return(
81
+ body: {
82
+ response: {
83
+ results: items.times.map { { name: 'category' } }
84
+ }
85
+ }.to_json
86
+ )
87
+ end
88
+
89
+ it 'is able to fetch all remote objects without any current page indicator by simply increasing the offset until response is empty with a limit' do
90
+ stub_batch('http://store/categories?language=en&max=10&offset=0')
91
+ stub_batch('http://store/categories?language=en&max=10&offset=10')
92
+ stub_batch('http://store/categories?language=en&max=10&offset=20')
93
+ stub_batch('http://store/categories?language=en&max=10&offset=30', 0)
94
+ records = Category.limit(10).all(language: 'en').fetch
95
+ expect(records.length).to eq 30
96
+ end
97
+
98
+ it 'is able to fetch all remote objects without any current page indicator by simply increasing the offset until response is empty' do
99
+ stub_batch('http://store/categories?language=en&max=100', 100)
100
+ stub_batch('http://store/categories?language=en&max=100&offset=100', 100)
101
+ stub_batch('http://store/categories?language=en&max=100&offset=200', 100)
102
+ stub_batch('http://store/categories?language=en&max=100&offset=300', 0)
103
+ records = Category.all(language: 'en').fetch
104
+ expect(records.length).to eq 300
105
+ end
106
+
107
+ it 'outputs a warning, if all is requested, but endpoint does not implement pagination meta data' do
108
+ stub_batch('http://store/categories?language=en&max=100', 100)
109
+ stub_batch('http://store/categories?language=en&max=100&offset=100', 100)
110
+ stub_batch('http://store/categories?language=en&max=100&offset=200', 100)
111
+ stub_batch('http://store/categories?language=en&max=100&offset=300', 0)
112
+ expect(-> { Category.all(language: 'en').fetch }).to output(
113
+ %r{\[Warning\] "all" has been requested, but endpoint does not provide pagination meta data. If you just want to fetch the first response, use "where" or "fetch".}
114
+ ).to_stderr
115
+ end
116
+
117
+ context 'with record set to not paginated' do
118
+ before do
119
+ class Record < DHS::Record
120
+ configuration paginated: false
121
+ endpoint 'http://datastore/records'
122
+ end
123
+ end
124
+
125
+ it 'just fetches the first response' do
126
+ stub_request(:get, 'http://datastore/records')
127
+ .to_return(body: [{ name: 'Steve' }].to_json)
128
+ records = Record.all
129
+ expect(records.first.name).to eq 'Steve'
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Record do
6
+ before do
7
+ class LocalEntry < DHS::Record
8
+ endpoint '{+datastore}/local-entries'
9
+ end
10
+ end
11
+
12
+ context '#assign_attributes' do
13
+ it 'sets the attributes' do
14
+ entry = LocalEntry.new
15
+ entry.assign_attributes(company_name: 'depay')
16
+ expect(entry.company_name).to eq 'depay'
17
+ end
18
+ end
19
+
20
+ context 'when not a hash was passed' do
21
+ it 'raises an error' do
22
+ entry = LocalEntry.new
23
+ expect do
24
+ entry.assign_attributes([:company_name, 'depay'])
25
+ end.to raise_error(ArgumentError)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Record do
6
+ context 'build' do
7
+ let(:datastore) { 'http://depay.fi/v2' }
8
+
9
+ before do
10
+ DHC.config.placeholder('datastore', datastore)
11
+ class Feedback < DHS::Record
12
+ endpoint '{+datastore}/content-ads/{campaign_id}/feedbacks'
13
+ endpoint '{+datastore}/feedbacks'
14
+ end
15
+ end
16
+
17
+ it 'builds a new item from scratch' do
18
+ feedback = Feedback.build recommended: true
19
+ expect(feedback).to be_kind_of Feedback
20
+ expect(feedback.recommended).to eq true
21
+ stub_request(:post, 'http://depay.fi/v2/feedbacks')
22
+ .with(body: '{"recommended":true}')
23
+ feedback.save
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Record do
6
+ context 'cast nested data' do
7
+ let(:stub_customer_request) do
8
+ stub_request(:get, 'https://datastore/customers?limit=1')
9
+ .to_return(
10
+ body: {
11
+ items: [
12
+ {
13
+ href: 'https://datastore/customers/12',
14
+ address: {
15
+ href: 'https://datastore/addresses/3'
16
+ },
17
+ contracts: [
18
+ {
19
+ href: 'https://datastore/contracts/2'
20
+ }
21
+ ]
22
+ }
23
+ ]
24
+ }.to_json
25
+ )
26
+ end
27
+
28
+ before do
29
+ class Customer < DHS::Record
30
+ endpoint 'https://datastore/customers'
31
+ endpoint 'https://datastore/customers/{id}'
32
+ end
33
+
34
+ class Contract < DHS::Record
35
+ endpoint 'https://datastore/contracts'
36
+ endpoint 'https://datastore/contracts/{id}'
37
+ end
38
+
39
+ class Address < DHS::Record
40
+ endpoint 'https://datastore/addresses'
41
+ endpoint 'https://datastore/addresses/{id}', headers: { 'Authorization' => 'Bearer 123' }
42
+ end
43
+ stub_customer_request
44
+ end
45
+
46
+ it 'casts nested data properly' do
47
+ customer = Customer.first
48
+ expect(customer).to be_kind_of Customer
49
+ expect(customer.address).to be_kind_of Address
50
+ expect(customer.contracts.first).to be_kind_of Contract
51
+ end
52
+
53
+ context 'interact with nested resouce remotely' do
54
+ let(:address_request_stub) do
55
+ stub_request(:post, 'https://datastore/addresses/3')
56
+ .with(
57
+ body: {
58
+ href: 'https://datastore/addresses/3',
59
+ zip: 8050
60
+ }.to_json,
61
+ headers: {
62
+ 'Authorization': 'Bearer 123'
63
+ }
64
+ )
65
+ .to_return(status: 201)
66
+ end
67
+
68
+ before do
69
+ address_request_stub
70
+ end
71
+
72
+ it 'applies casted records endpoint options to requests made to the nested resource' do
73
+ customer = Customer.first
74
+ address = customer.address
75
+ address.zip = 8050
76
+ address.save
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Record do
6
+ before do
7
+ class Place < DHS::Record
8
+ endpoint 'http://datastore/places/{id}'
9
+ endpoint 'http://datastore/users/{user_id}/places/{id}'
10
+
11
+ def display_name
12
+ "*#{name}*"
13
+ end
14
+ end
15
+
16
+ class User < DHS::Record
17
+ endpoint 'http://datastore/users/{id}'
18
+ end
19
+
20
+ stub_request(:get, 'http://datastore/users/123')
21
+ .to_return(status: 200, body: {
22
+ href: 'http://datastore/users/123',
23
+ places: { href: 'http://datastore/users/123/places' }
24
+ }.to_json)
25
+
26
+ stub_request(:get, 'http://datastore/users/123/places?limit=100')
27
+ .to_return(status: 200, body: {
28
+ href: 'http://datastore/users/123/places?offset=0&limit=100',
29
+ items: [
30
+ {
31
+ href: 'http://datastore/users/123/places/789'
32
+ }, {
33
+ href: 'http://datastore/users/123/places/790'
34
+ }
35
+ ],
36
+ total: 2,
37
+ offset: 0,
38
+ limit: 10
39
+ }.to_json)
40
+
41
+ stub_request(:get, 'http://datastore/users/123/places/789')
42
+ .to_return(
43
+ body: {
44
+ href: 'http://datastore/users/123/places/789?limit=100',
45
+ name: 'Mc Donalds'
46
+ }.to_json
47
+ )
48
+
49
+ stub_request(:get, 'http://datastore/users/123/places/790')
50
+ .to_return(
51
+ status: 404,
52
+ body: {
53
+ status: 404,
54
+ message: 'The requested resource does not exist.'
55
+ }.to_json
56
+ )
57
+ end
58
+
59
+ let(:places) do
60
+ User
61
+ .includes(:places)
62
+ .references(places: { ignore: DHC::NotFound })
63
+ .find('123')
64
+ .places
65
+ end
66
+
67
+ context '.compact' do
68
+ it 'does NOT return an internal data type, but the Record class' do
69
+ expect(places.compact.class).to eq User
70
+ end
71
+
72
+ it 'removes linked resouces which could not get fetched' do
73
+ expect(places.compact.length).to eq 1
74
+ expect(places.length).not_to eq 1 # leaves the original intact
75
+ end
76
+ end
77
+
78
+ context 'record casting' do
79
+ let(:expected_display_name) { '*Mc Donalds*' }
80
+
81
+ it 'finds the right record class' do
82
+ expect(places.first.display_name).to eq expected_display_name
83
+ expect(places.compact.map(&:display_name)).to eq [expected_display_name]
84
+ end
85
+ end
86
+
87
+ context '.compact!' do
88
+ it 'removes linked resouces which could not get fetched' do
89
+ expect(places.compact!.length).to eq 1
90
+ expect(places.length).to eq 1 # and changes the original intact
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Record do
6
+ context 'create' do
7
+ let(:datastore) { 'http://depay.fi/v2' }
8
+
9
+ before do
10
+ DHC.config.placeholder('datastore', datastore)
11
+ class Feedback < DHS::Record
12
+ endpoint '{+datastore}/content-ads/{campaign_id}/feedbacks'
13
+ endpoint '{+datastore}/feedbacks'
14
+ end
15
+ end
16
+
17
+ let(:object) do
18
+ {
19
+ recommended: true,
20
+ source_id: 'aaa'
21
+ }
22
+ end
23
+
24
+ it 'creates new record on the backend' do
25
+ stub_request(:post, "#{datastore}/feedbacks")
26
+ .with(body: object.to_json)
27
+ .to_return(status: 200, body: object.to_json)
28
+ record = Feedback.create(object)
29
+ expect(record).to be_kind_of Feedback
30
+ expect(record.recommended).to eq true
31
+ expect(record.errors.messages).to eq({})
32
+ expect(record.errors.message).to eq nil
33
+ end
34
+
35
+ it 'uses proper endpoint when creating data' do
36
+ stub_request(:post, "#{datastore}/content-ads/12345/feedbacks")
37
+ .with(body: object.to_json)
38
+ .to_return(status: 200, body: object.to_json)
39
+ Feedback.create(object.merge(campaign_id: '12345'))
40
+ end
41
+
42
+ it 'merges backend response object with object' do
43
+ body = object.merge(additional_key: 1)
44
+ stub_request(:post, "#{datastore}/content-ads/12345/feedbacks")
45
+ .with(body: object.to_json)
46
+ .to_return(status: 200, body: body.to_json)
47
+ data = Feedback.create(object.merge(campaign_id: '12345'))
48
+ expect(data.additional_key).to eq 1
49
+ end
50
+
51
+ context 'creation errors' do
52
+ let(:creation_error) do
53
+ {
54
+ 'status' => 400,
55
+ 'fields' => [
56
+ {
57
+ 'name' => 'ratings',
58
+ 'details' => [{ 'code' => 'REQUIRED_PROPERTY_VALUE' }]
59
+ }, {
60
+ 'name' => 'recommended',
61
+ 'details' => [{ 'code' => 'REQUIRED_PROPERTY_VALUE' }]
62
+ }
63
+ ]
64
+ }
65
+ end
66
+
67
+ it 'provides errors accessor on the record when creation failed using create' do
68
+ stub_request(:post, "#{datastore}/content-ads/12345/feedbacks")
69
+ .to_return(status: 400, body: creation_error.to_json)
70
+ feedback = Feedback.create(object.merge(campaign_id: '12345'))
71
+ expect(feedback.errors).to be_kind_of DHS::Problems::Errors
72
+ end
73
+
74
+ it 'raises an exception when creation failed using create!' do
75
+ stub_request(:post, "#{datastore}/content-ads/12345/feedbacks")
76
+ .to_return(status: 400, body: creation_error.to_json)
77
+ expect(lambda {
78
+ Feedback.create!(object.merge(campaign_id: '12345'))
79
+ }).to raise_error(DHC::Error)
80
+ end
81
+ end
82
+
83
+ context 'custom setters' do
84
+ before do
85
+ class Feedback
86
+ def ratings=(ratings)
87
+ _raw[:ratings] = ratings.map { |k, v| { name: k.to_s, value: v } }
88
+ end
89
+ end
90
+
91
+ stub_request(:post, "#{datastore}/feedbacks")
92
+ .with(body: { ratings: converted_ratings }.to_json)
93
+ .to_return(status: 200, body: { ratings: converted_ratings }.to_json)
94
+ end
95
+
96
+ let(:ratings) do
97
+ {
98
+ a: 1,
99
+ b: 2
100
+ }
101
+ end
102
+
103
+ let(:converted_ratings) do
104
+ [
105
+ { name: 'a', value: 1 },
106
+ { name: 'b', value: 2 }
107
+ ]
108
+ end
109
+
110
+ it 'are used by create' do
111
+ feedback = Feedback.create(ratings: ratings)
112
+ expect(feedback.ratings._raw).to eq(converted_ratings)
113
+ end
114
+
115
+ it 'can be used directly to change raw data' do
116
+ feedback = Feedback.create(ratings: ratings)
117
+ feedback.ratings = { z: 3 }
118
+ expect(feedback.ratings.first.name).to eq 'z'
119
+ end
120
+
121
+ context 'and custom getters' do
122
+ before do
123
+ class Feedback
124
+ def ratings
125
+ Hash[_raw[:ratings].map { |r| [r[:name].to_sym, r[:value]] }]
126
+ end
127
+ end
128
+ end
129
+
130
+ it 'uses custom getters to show data for exploration' do
131
+ feedback = Feedback.create(ratings: ratings)
132
+ expect(feedback.ratings).to eq(ratings)
133
+ end
134
+ end
135
+ end
136
+
137
+ context 'location header' do
138
+ before do
139
+ class ContactPerson < DHS::Record
140
+ endpoint 'http://datastore/contact_persons'
141
+ end
142
+ end
143
+
144
+ let(:location) { 'http://datastore/contact_persons/1' }
145
+ let(:created_at) { '2017-12-21' }
146
+ let(:name) { 'Sebastian' }
147
+
148
+ it 'Loads the data from the "Location" header after creation' do
149
+ stub_request(:post, 'http://datastore/contact_persons')
150
+ .to_return(status: 204, headers: { Location: location })
151
+ stub_request(:get, 'http://datastore/contact_persons/1')
152
+ .to_return(body: { href: location, name: name, created_at: created_at }.to_json)
153
+ contact_person = ContactPerson.create!(name: name)
154
+ expect(contact_person.href).to eq location
155
+ expect(contact_person.created_at).to eq Date.parse(created_at)
156
+ expect(contact_person.name).to eq name
157
+ end
158
+ end
159
+ end
160
+ end