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,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Item do
6
+ before do
7
+ class Record < DHS::Record
8
+ end
9
+ end
10
+
11
+ subject { Record.new }
12
+
13
+ context 'add error' do
14
+ it 'allows to add validation errors to the instance itself' do
15
+ subject.errors.add(:name, 'This date is invalid')
16
+ expect(
17
+ subject.errors.first
18
+ ).to eq ['name', 'This date is invalid']
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Item do
6
+ before do
7
+ class Location < DHS::Record
8
+ endpoint 'http://sync/locations'
9
+ endpoint 'http://sync/locations/{id}'
10
+ end
11
+
12
+ class Synchronization < DHS::Record
13
+ endpoint 'http://sync/locations/{id}/sync'
14
+ end
15
+
16
+ stub_request(:get, 'http://sync/locations/1')
17
+ .to_return(body: {
18
+ id: 1,
19
+ name: 'depay'
20
+ }.to_json)
21
+
22
+ stub_request(:post, 'http://sync/locations/1/sync')
23
+ .with(body: {
24
+ name: 'depay'
25
+ }.to_json)
26
+ .to_return(status: 201)
27
+ end
28
+
29
+ context 'convert records from class A to class B' do
30
+ it 'becomes a record of another class' do
31
+ location = Location.find(1)
32
+ synchronization = location.becomes(Synchronization)
33
+ expect(synchronization).to be_kind_of(Synchronization)
34
+ synchronization.save!
35
+ expect(synchronization).to be_kind_of(Synchronization)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'dhs'
5
+
6
+ describe DHS::Item do
7
+ before do
8
+ class Record < DHS::Record
9
+ endpoint 'http://datastore/v2/records'
10
+ end
11
+ end
12
+
13
+ context 'requires active support when dealing with Item' do
14
+ it 'does not raise an error' do
15
+ stub_request(:get, 'http://datastore/v2/records?color=blue')
16
+ .to_return(
17
+ body: {
18
+ items: [{ name: 'Steve' }]
19
+ }.to_json
20
+ )
21
+ Record.where(color: :blue).each do |record|
22
+ expect(record.respond_to_missing?(:new)).to eq false
23
+ expect(record.respond_to_missing?(:proxy_association)).to eq false
24
+ expect(record.respond_to_missing?(:name)).to eq true
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Item do
6
+ before do
7
+ class Record < DHS::Record
8
+ endpoint '{+datastore}/v2/{campaign_id}/feedbacks'
9
+ endpoint '{+datastore}/v2/feedbacks'
10
+ end
11
+ end
12
+
13
+ let(:json) do
14
+ load_json(:feedbacks)
15
+ end
16
+
17
+ let(:data) do
18
+ DHS::Data.new(json, nil, Record)
19
+ end
20
+
21
+ let(:item) do
22
+ data[0]
23
+ end
24
+
25
+ context 'delegates methods to raw' do
26
+ %w(present? blank? empty?).each do |method|
27
+ it "delegates #{method} to raw" do
28
+ expect(item.send(method.to_sym)).not_to be_nil
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Item do
6
+ before do
7
+ DHC.config.placeholder('datastore', datastore)
8
+ class Record < DHS::Record
9
+ endpoint '{+datastore}/v2/{campaign_id}/feedbacks'
10
+ endpoint '{+datastore}/v2/feedbacks'
11
+ end
12
+ end
13
+
14
+ let(:datastore) do
15
+ 'http://depay.fi'
16
+ end
17
+
18
+ let(:json) do
19
+ load_json(:feedbacks)
20
+ end
21
+
22
+ let(:data) do
23
+ DHS::Data.new(json, nil, Record)
24
+ end
25
+
26
+ let(:item) do
27
+ data[0]
28
+ end
29
+
30
+ context 'destroy' do
31
+ it 'destroys the item on the backend' do
32
+ stub_request(:delete, "#{datastore}/v2/feedbacks/0sdaetZ-OWVg4oBiBJ-7IQ")
33
+ .to_return(status: 200)
34
+ expect(item.destroy._raw).to eq item._raw
35
+ end
36
+
37
+ it 'updates the request information on the item' do
38
+ no_content = 204
39
+ stub_request(:delete, "#{datastore}/v2/feedbacks/0sdaetZ-OWVg4oBiBJ-7IQ")
40
+ .to_return(status: no_content)
41
+ expect(item.destroy._request.response.code).to eq no_content
42
+ end
43
+
44
+ context 'includes and empty response' do
45
+ before do
46
+ class AnotherRecord < DHS::Record
47
+ endpoint '{+datastore}/v2/:campaign_id/restaurants'
48
+ end
49
+ end
50
+
51
+ it 'destroys an item even though it includes additonal services and an empty response body' do
52
+ stub_request(:delete, "#{datastore}/v2/feedbacks/1")
53
+ .to_return(status: 204, body: '')
54
+ data = { href: "#{datastore}/v2/feedbacks/1", restaurant: { href: "#{datastore}/v2/restaurants/1" } }
55
+ stub_request(:get, "#{datastore}/v2/feedbacks?id=1")
56
+ .to_return(status: 200, body: data.to_json)
57
+ stub_request(:get, "#{datastore}/v2/restaurants/1")
58
+ .to_return(status: 200, body: { name: 'Casa Ferlin' }.to_json)
59
+ item = Record.includes_first_page(:restaurant).find(1)
60
+ item.destroy
61
+ end
62
+ end
63
+
64
+ context 'when item does not have any href' do
65
+ let(:business_data) do
66
+ {
67
+ id: '12345',
68
+ name: 'depay'
69
+ }
70
+ end
71
+
72
+ let(:business_collection) do
73
+ [business_data]
74
+ end
75
+
76
+ before do
77
+ class Business < DHS::Record
78
+ endpoint 'https://uberall/businesses'
79
+ endpoint 'https://uberall/businesses/{id}'
80
+ end
81
+
82
+ stub_request(:get, 'https://uberall/businesses')
83
+ .to_return(body: business_collection.to_json)
84
+
85
+ stub_request(:delete, 'https://uberall/businesses/12345')
86
+ .to_return(body: {
87
+ status: 'SUCCESS',
88
+ response: {
89
+ success: true
90
+ }
91
+ }.to_json)
92
+ end
93
+
94
+ it "destroys the item using it's own id (data)" do
95
+ business = Business.fetch.first
96
+ expect(business.destroy._raw).to eq business_data
97
+ end
98
+
99
+ context 'item does not have an id' do
100
+ let(:business_data) do
101
+ {
102
+ name: 'depay'
103
+ }
104
+ end
105
+
106
+ it 'destroy the item using the id passed as options' do
107
+ business = Business.fetch.first
108
+ expect(business.destroy(params: { id: '12345' })._raw).to eq business_data
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Item do
6
+ before do
7
+ class Record < DHS::Record
8
+ endpoint '{+datastore}/records'
9
+ end
10
+ end
11
+
12
+ let(:json) do
13
+ {
14
+ local_entry: {
15
+ local_entry_id: 'ABC123'
16
+ }
17
+ }
18
+ end
19
+
20
+ let(:item) do
21
+ DHS::Data.new(json, nil, Record)
22
+ end
23
+
24
+ it 'is possible to dig data' do
25
+ expect(
26
+ item.dig(:local_entry, :local_entry_id)
27
+ ).to eq 'ABC123'
28
+ end
29
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Item do
6
+ context 'error codes' do
7
+ before do
8
+ I18n.reload!
9
+ I18n.backend.store_translations(:en, YAML.safe_load(translation)) if translation.present?
10
+ class Record < DHS::Record
11
+ endpoint 'http://datastore/records'
12
+ end
13
+ end
14
+
15
+ let(:translation) do
16
+ %q{
17
+ dhs:
18
+ errors:
19
+ fallback_message: 'This value is wrong'
20
+ }
21
+ end
22
+
23
+ it 'provides error codes along side with translated messages' do
24
+ stub_request(:post, 'http://datastore/records')
25
+ .to_return(body: {
26
+ field_errors: [{
27
+ code: 'UNSUPPORTED_PROPERTY_VALUE',
28
+ path: ['gender'],
29
+ message: 'The property value is unsupported.'
30
+ }, {
31
+ code: 'INCOMPLETE_PROPERTY_VALUE',
32
+ path: ['gender'],
33
+ message: 'The property value is incomplete. It misses some data'
34
+ }, {
35
+ code: 'INCOMPLETE_PROPERTY_VALUE',
36
+ path: %w[contract entry_id],
37
+ message: 'The property value is incomplete. It misses some data'
38
+ }]
39
+ }.to_json)
40
+ record = Record.create
41
+ expect(record.errors.messages['gender']).to eq(
42
+ ['This value is wrong', 'This value is wrong']
43
+ )
44
+ expect(record.errors.codes['gender']).to eq(
45
+ %w[UNSUPPORTED_PROPERTY_VALUE INCOMPLETE_PROPERTY_VALUE]
46
+ )
47
+ expect(record.errors.messages['contract.entry_id']).to eq(
48
+ ['This value is wrong']
49
+ )
50
+ expect(record.errors.codes['contract.entry_id']).to eq(
51
+ ['INCOMPLETE_PROPERTY_VALUE']
52
+ )
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,324 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe DHS::Item do
6
+ let(:datastore) { 'http://depay.fi/v2' }
7
+
8
+ let(:error_format_fields) do
9
+ {
10
+ 'status' => 400,
11
+ 'message' => 'ratings must be set when review or name or review_title is set | The property value is required; it cannot be null, empty, or blank.',
12
+ 'fields' => [
13
+ {
14
+ 'name' => 'ratings',
15
+ 'details' => [{ 'code' => 'REQUIRED_PROPERTY_VALUE' }, { 'code' => 'UNSUPPORTED_PROPERTY_VALUE' }]
16
+ }, {
17
+ 'name' => 'recommended',
18
+ 'details' => [{ 'code' => 'REQUIRED_PROPERTY_VALUE' }]
19
+ }
20
+ ]
21
+ }
22
+ end
23
+
24
+ let(:error_format_field_errors) do
25
+ {
26
+ 'status' => 400,
27
+ 'message' => 'Some data in the request body failed validation. Inspect the field errors for details.',
28
+ 'field_errors' => [{
29
+ 'code' => 'UNSUPPORTED_PROPERTY_VALUE',
30
+ 'path' => ['gender'],
31
+ 'message' => 'The property value is unsupported. Supported values are: FEMALE, MALE'
32
+ }, {
33
+ 'code' => 'INCOMPLETE_PROPERTY_VALUE',
34
+ 'path' => ['gender'],
35
+ 'message' => 'The property value is incomplete. It misses some data'
36
+ }, {
37
+ 'code' => 'INCOMPLETE_PROPERTY_VALUE',
38
+ 'path' => %w[contract entry_id],
39
+ 'message' => 'The property value is incomplete. It misses some data'
40
+ }]
41
+ }
42
+ end
43
+
44
+ let(:not_defined_error_format) do
45
+ {
46
+ 'error' => 'missing_token',
47
+ 'error_description' => 'Bearer token is missing'
48
+ }
49
+ end
50
+
51
+ let(:unparsable_error_body) do
52
+ '<html></html>'
53
+ end
54
+
55
+ before do
56
+ I18n.reload!
57
+ I18n.backend.store_translations(:en, {}) if defined? translations
58
+ DHC.config.placeholder(:datastore, datastore)
59
+ class Record < DHS::Record
60
+ endpoint '{+datastore}/{campaign_id}/feedbacks'
61
+ endpoint '{+datastore}/feedbacks'
62
+ end
63
+ end
64
+
65
+ context 'save failed' do
66
+ let(:record) { Record.build(name: 'Steve') }
67
+
68
+ it 'parses fields correctly when creation failed' do
69
+ stub_request(:post, "#{datastore}/feedbacks")
70
+ .to_return(status: 400, body: error_format_fields.to_json)
71
+ result = record.save
72
+ expect(result).to eq false
73
+ expect(record.errors).to be_present
74
+ expect(record.errors.any?).to eq true
75
+ expect(record.name).to eq 'Steve'
76
+ expect(record.errors.include?(:ratings)).to eq true
77
+ expect(record.errors.include?(:recommended)).to eq true
78
+ expect(record.errors[:ratings]).to eq %w[REQUIRED_PROPERTY_VALUE UNSUPPORTED_PROPERTY_VALUE]
79
+ expect(record.errors[:recommended]).to eq ['REQUIRED_PROPERTY_VALUE']
80
+ end
81
+
82
+ it 'allows accessing error messages as a hash with indifferent access' do
83
+ stub_request(:post, "#{datastore}/feedbacks")
84
+ .to_return(status: 400, body: error_format_fields.to_json)
85
+ record.save
86
+ expect(record.errors.messages[:ratings]).to be_present
87
+ expect(record.errors.messages['ratings']).to be_present
88
+ end
89
+
90
+ it 'parses field errors correctly when creation failed' do
91
+ stub_request(:post, "#{datastore}/feedbacks")
92
+ .to_return(status: 400, body: error_format_field_errors.to_json)
93
+ result = record.save
94
+ expect(result).to eq false
95
+ expect(record.errors).to be_present
96
+ expect(record.errors.any?).to eq true
97
+ expect(record.errors.include?(:gender)).to eq true
98
+ expect(record.errors.include?(:"contract.entry_id")).to eq true
99
+ expect(record.errors[:gender]).to eq %w[UNSUPPORTED_PROPERTY_VALUE INCOMPLETE_PROPERTY_VALUE]
100
+ expect(record.errors[:"contract.entry_id"]).to eq ['INCOMPLETE_PROPERTY_VALUE']
101
+ end
102
+
103
+ it 'parses field errors correctly when exception in raised' do
104
+ stub_request(:post, "#{datastore}/feedbacks")
105
+ .to_return(status: 400, body: error_format_fields.to_json)
106
+ expect { record.save! }.to raise_error(DHC::BadRequest)
107
+ expect(record.errors).to be_present
108
+ expect(record.errors.any?).to eq true
109
+ expect(record.name).to eq 'Steve'
110
+ expect(record.errors.include?(:ratings)).to eq true
111
+ expect(record.errors.include?(:recommended)).to eq true
112
+ expect(record.errors[:ratings]).to eq %w[REQUIRED_PROPERTY_VALUE UNSUPPORTED_PROPERTY_VALUE]
113
+ expect(record.errors[:recommended]).to eq ['REQUIRED_PROPERTY_VALUE']
114
+ end
115
+ end
116
+
117
+ context 'raw error data' do
118
+ it 'provides access to raw error data' do
119
+ stub_request(:post, "#{datastore}/feedbacks")
120
+ .to_return(status: 400, body: error_format_field_errors.to_json)
121
+ record = Record.build
122
+ record.save
123
+ expect(record.errors.raw).to be_present
124
+ expect(record.errors.any?).to eq true
125
+ json = JSON.parse(record.errors.raw)
126
+ expect(json['status']).to be_present
127
+ expect(json['message']).to be_present
128
+ expect(json['field_errors']).to be_present
129
+ end
130
+ end
131
+
132
+ context 'request fails with unformated error message' do
133
+ it 'still tells us that there is an error' do
134
+ stub_request(:post, "#{datastore}/feedbacks")
135
+ .to_return(status: 400, body: not_defined_error_format.to_json)
136
+ record = Record.build
137
+ record.name = 'Steve'
138
+ result = record.save
139
+ expect(result).to eq false
140
+ expect(record.errors).to be_present
141
+ expect(record.errors.any?).to eq true
142
+ expect(record.errors['error']).to eq ['missing_token']
143
+ expect(record.errors['error_description']).to eq ['Bearer token is missing']
144
+ end
145
+ end
146
+
147
+ context 'empty error response body' do
148
+ it 'still tells us that there is an error' do
149
+ stub_request(:post, "#{datastore}/feedbacks").to_return(status: 400)
150
+ record = Record.build
151
+ record.name = 'Steve'
152
+ result = record.save
153
+ expect(result).to eq false
154
+ expect(record.errors).to be_present
155
+ expect(record.errors.any?).to eq true
156
+ expect(record.errors['body']).to eq ['parse error']
157
+ end
158
+ end
159
+
160
+ context 'unparsable error body' do
161
+ it 'still tells us that there is an error' do
162
+ stub_request(:post, "#{datastore}/feedbacks").to_return(status: 400, body: unparsable_error_body)
163
+ record = Record.build
164
+ record.name = 'Steve'
165
+ result = record.save
166
+ expect(result).to eq false
167
+ expect(record.errors).to be_present
168
+ expect(record.errors.any?).to eq true
169
+ expect(record.errors['body']).to eq ['parse error']
170
+ end
171
+ end
172
+
173
+ describe '#clear' do
174
+ let(:record) { Record.build(name: 'Steve') }
175
+
176
+ before do
177
+ stub_request(:post, "#{datastore}/feedbacks")
178
+ .to_return(status: 400, body: error_format_fields.to_json)
179
+ end
180
+
181
+ it 'resets all errors' do
182
+ record.save
183
+ expect(record.errors.any?).to eq true
184
+ record.errors.clear
185
+ expect(record.errors.any?).to eq false
186
+ end
187
+ end
188
+
189
+ context 'nested data' do
190
+ let(:body_with_errors) do
191
+ {
192
+ 'status' => 400,
193
+ 'message' => 'Some data in the request body failed validation. Inspect the field errors for details.',
194
+ 'field_errors' => [{
195
+ 'code' => 'UNSUPPORTED_PROPERTY_VALUE',
196
+ 'path' => ['reviews', 0, 'name'],
197
+ 'message' => 'The property value is unsupported. Supported values are: FEMALE, MALE'
198
+ }, {
199
+ 'code' => 'INCOMPLETE_PROPERTY_VALUE',
200
+ 'path' => %w[address street name],
201
+ 'message' => 'The property value is incomplete. It misses some data'
202
+ }, {
203
+ 'code' => 'REQUIRED_PROPERTY_VALUE',
204
+ 'path' => %w[address street additional_line1],
205
+ 'message' => 'The property value is required'
206
+ }]
207
+ }
208
+ end
209
+
210
+ let(:record) do
211
+ Record.build(
212
+ reviews: [{ name: 123, suggested: false }],
213
+ address: {
214
+ additional_line1: '',
215
+ street: {
216
+ name: 'Förlib'
217
+ }
218
+ }
219
+ )
220
+ end
221
+
222
+ let(:errrors) { record.errors }
223
+
224
+ before do
225
+ stub_request(:post, "#{datastore}/feedbacks")
226
+ .to_return(status: 400, body: body_with_errors.to_json)
227
+ end
228
+
229
+ it 'forwards errors to nested data' do
230
+ record.save
231
+ expect(record.errors['address.street.name']).to include 'INCOMPLETE_PROPERTY_VALUE'
232
+ expect(record.errors['reviews.0.name']).to include 'UNSUPPORTED_PROPERTY_VALUE'
233
+ expect(record.address.errors).to be_present
234
+ expect(record.address.errors['street.name']).to be_present
235
+ expect(record.address.street.errors).to be_present
236
+ expect(record.address.street.errors[:name]).to include 'INCOMPLETE_PROPERTY_VALUE'
237
+ expect(record.reviews.errors).to be_present
238
+ expect(record.reviews.first.errors).to be_present
239
+ expect(record.reviews.first.errors[:name]).to include 'UNSUPPORTED_PROPERTY_VALUE'
240
+ expect(record.reviews.last.errors).to be_present
241
+ expect(record.reviews.last.errors[:name]).to include 'UNSUPPORTED_PROPERTY_VALUE'
242
+ end
243
+
244
+ context 'accessing no model' do
245
+ it 'does not raise an error when trying to find error record model name' do
246
+ expect(lambda do
247
+ record.reviews.first.errors[:name]
248
+ end).not_to raise_error
249
+ end
250
+ end
251
+
252
+ it 'provides http status code for errors' do
253
+ record.save
254
+ expect(record.errors.status_code).to eq 400
255
+ end
256
+
257
+ context 'with general error fallback message configured' do
258
+ before do
259
+ I18n.reload!
260
+ I18n.backend.store_translations(:en, YAML.safe_load(translation)) if translation.present?
261
+ end
262
+
263
+ let(:translation) do
264
+ %q{
265
+ dhs:
266
+ errors:
267
+ fallback_message: 'This value is wrong'
268
+ }
269
+ end
270
+
271
+ it 'is capable to access errors/attributes that dont have any validation errors' do
272
+ record.save
273
+ expect(record.reviews.first.errors[:suggested]).to be_kind_of Array
274
+ expect(record.reviews.first.errors[:suggested]).to be_empty
275
+ end
276
+ end
277
+ end
278
+
279
+ context 'forwards errors to nested data when associations are defined' do
280
+ before do
281
+ class AppointmentProposal < DHS::Record
282
+ endpoint 'http://dataste/appointment_proposals'
283
+ endpoint 'http://dataste/appointment_proposals/{id}'
284
+
285
+ has_many :appointments
286
+ end
287
+
288
+ class Appointment < DHS::Record
289
+ end
290
+
291
+ stub_request(:get, 'http://dataste/appointment_proposals/1')
292
+ .to_return(body: {
293
+ appointments: [
294
+ { 'date_time' => '13.12.2018' },
295
+ { 'date_time' => '18.10.2028' }
296
+ ]
297
+ }.to_json)
298
+
299
+ stub_request(:post, 'http://dataste/appointment_proposals')
300
+ .to_return(
301
+ status: 400,
302
+ body: {
303
+ field_errors: [{
304
+ 'code' => 'DATE_PROPERTY_NOT_IN_FUTURE',
305
+ 'path' => ['appointments', 0, 'date_time']
306
+ }]
307
+ }.to_json
308
+ )
309
+ end
310
+
311
+ it 'translates errors automatically when they are around' do
312
+ appointment_proposal = AppointmentProposal.find(1)
313
+ appointment_proposal.update(
314
+ 'appointments_attributes' => {
315
+ '0' => { 'date_time' => '13.12.2018' },
316
+ '1' => { 'date_time' => '18.10.2028' }
317
+ }
318
+ )
319
+ appointment_proposal.appointments
320
+ appointment = appointment_proposal.appointments[0]
321
+ expect(appointment.errors[:date_time]).to eq ['DATE_PROPERTY_NOT_IN_FUTURE']
322
+ end
323
+ end
324
+ end