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,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DHS::Problems
4
+ module Nested
5
+ module Base
6
+ # Filters base errors by scope
7
+ # and reduces key by given scope name;
8
+ # returns plain array if end of tree is reached
9
+ def nest(messages, scope = nil)
10
+ scope = translate_rails_to_api_scope(scope)
11
+ return messages unless scope
12
+ messages = messages.select do |key, _|
13
+ key.match(/^#{scope}/)
14
+ end
15
+ # if only one key and this key has no dots, exit with plain
16
+ if reached_leaf?(messages)
17
+ messages.first[1]
18
+ else
19
+ remove_scope(messages, scope)
20
+ end
21
+ end
22
+
23
+ # Identifies if the end of nested errors tree is reached
24
+ def reached_leaf?(messages)
25
+ messages.keys.length == 1 &&
26
+ !messages.first[0].match(/\./)
27
+ end
28
+
29
+ # Removes scope from given messages' key
30
+ def remove_scope(messages, scope)
31
+ messages.each_with_object({}) do |element, hash|
32
+ key = element[0].to_s.gsub(/^#{scope}\./, '')
33
+ hash[key.to_sym] = element[1]
34
+ end
35
+ end
36
+
37
+ # Translates rails like accessors for collections
38
+ # like first, second, last to api error paths
39
+ def translate_rails_to_api_scope(scope)
40
+ case scope
41
+ when :first
42
+ 0
43
+ when :second
44
+ 1
45
+ when :last
46
+ return values.length - 1 if messages.present?
47
+ 0
48
+ else
49
+ scope
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DHS::Problems
4
+ module Nested
5
+ class Errors < DHS::Problems::Errors
6
+ include DHS::Problems::Nested::Base
7
+
8
+ def initialize(errors, scope)
9
+ @raw = errors
10
+ @messages = nest(errors.messages, scope)
11
+ @message = errors.message
12
+ @scope = scope
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DHS::Problems
4
+ module Nested
5
+ class Warnings < DHS::Problems::Warnings
6
+ include DHS::Problems::Nested::Base
7
+
8
+ def initialize(warnings, scope)
9
+ @raw = warnings.raw
10
+ @messages = nest(warnings.messages, scope)
11
+ @scope = scope
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DHS::Problems
4
+ class Warnings < Base
5
+
6
+ def initialize(raw, record = nil)
7
+ @raw = raw
8
+ @record = record
9
+ @codes = {}.with_indifferent_access
10
+ @messages = warnings_from_raw
11
+ end
12
+
13
+ private
14
+
15
+ def warnings_from_raw
16
+ messages = {}
17
+ return messages if !raw.is_a?(Hash) || raw[:field_warnings].blank?
18
+ raw[:field_warnings].each do |field_warning|
19
+ add_error(messages, field_warning[:path].join('.').to_sym, field_warning[:code])
20
+ end
21
+ messages.with_indifferent_access
22
+ end
23
+ end
24
+ end
data/lib/dhs/proxy.rb ADDED
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Proxy makes different kind of data accessible
4
+ # If href is present it also alows loading/reloading
5
+ class DHS::Proxy
6
+ autoload :Accessors,
7
+ 'dhs/concerns/proxy/accessors'
8
+ autoload :Create,
9
+ 'dhs/concerns/proxy/create'
10
+ autoload :Problems,
11
+ 'dhs/concerns/proxy/problems'
12
+ autoload :Link,
13
+ 'dhs/concerns/proxy/link'
14
+
15
+ include Accessors
16
+ include Create
17
+ include Link
18
+ include Problems
19
+
20
+ # prevent clashing with attributes of underlying data
21
+ attr_accessor :_data, :_loaded
22
+
23
+ delegate :_record, :becomes, to: :_data, allow_nil: true
24
+
25
+ def initialize(data)
26
+ self._data = data
27
+ self._loaded = false
28
+ end
29
+
30
+ def record
31
+ _data.class
32
+ end
33
+
34
+ def load!(options = nil)
35
+ return self if _loaded
36
+ reload!(options)
37
+ end
38
+
39
+ def reload!(options = nil)
40
+ options = {} if options.blank?
41
+ data = _data.class.request(
42
+ options.merge(method: :get).merge(reload_options)
43
+ )
44
+ _data.merge_raw!(data.unwrap(:item_key))
45
+ self._loaded = true
46
+ return becomes(_record) if _record
47
+ self
48
+ end
49
+
50
+ private
51
+
52
+ def as_record
53
+ @as_record ||= becomes(_record)
54
+ end
55
+
56
+ def reload_options
57
+ return { url: _data.href } if _data.href
58
+ return { params: { id: as_record.id } } if as_record.id
59
+ {}
60
+ end
61
+
62
+ def merge_data_with_options(data, options)
63
+ if options && options[:params]
64
+ data.merge(options[:params])
65
+ else
66
+ data
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DHS
4
+ class Railtie < Rails::Railtie
5
+ initializer 'dhs.hook_into_controller_initialization' do
6
+ class ActionController::Base
7
+
8
+ def initialize
9
+ prepare_dhs_request_cycle_cache
10
+ reset_option_blocks
11
+ reset_extended_rollbar_request_logs
12
+ super
13
+ end
14
+
15
+ private
16
+
17
+ def prepare_dhs_request_cycle_cache
18
+ return unless DHS.config.request_cycle_cache_enabled
19
+ DHS::Interceptors::RequestCycleCache::ThreadRegistry.request_id = [Time.now.to_f, request.object_id].join('#')
20
+ end
21
+
22
+ def reset_option_blocks
23
+ DHS::OptionBlocks::CurrentOptionBlock.options = nil
24
+ end
25
+
26
+ def reset_extended_rollbar_request_logs
27
+ return unless defined?(::Rollbar)
28
+ return unless DHC.config.interceptors.include?(DHS::Interceptors::ExtendedRollbar::Interceptor)
29
+ DHS::Interceptors::ExtendedRollbar::ThreadRegistry.log = []
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
data/lib/dhs/record.rb ADDED
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DHS::Record
4
+ autoload :AttributeAssignment,
5
+ 'dhs/concerns/record/attribute_assignment'
6
+ autoload :Batch,
7
+ 'dhs/concerns/record/batch'
8
+ autoload :Chainable,
9
+ 'dhs/concerns/record/chainable'
10
+ autoload :Configuration,
11
+ 'dhs/concerns/record/configuration'
12
+ autoload :Create,
13
+ 'dhs/concerns/record/create'
14
+ autoload :CustomSetters,
15
+ 'dhs/concerns/record/custom_setters'
16
+ autoload :Destroy,
17
+ 'dhs/concerns/record/destroy'
18
+ autoload :Endpoints,
19
+ 'dhs/concerns/record/endpoints'
20
+ autoload :Equality,
21
+ 'dhs/concerns/record/equality'
22
+ autoload :Find,
23
+ 'dhs/concerns/record/find'
24
+ autoload :FindBy,
25
+ 'dhs/concerns/record/find_by'
26
+ autoload :First,
27
+ 'dhs/concerns/record/first'
28
+ autoload :HrefFor,
29
+ 'dhs/concerns/record/href_for'
30
+ autoload :Last,
31
+ 'dhs/concerns/record/last'
32
+ autoload :Mapping,
33
+ 'dhs/concerns/record/mapping'
34
+ autoload :Merge,
35
+ 'dhs/concerns/record/merge'
36
+ autoload :Model,
37
+ 'dhs/concerns/record/model'
38
+ autoload :Pagination,
39
+ 'dhs/concerns/record/pagination'
40
+ autoload :Provider,
41
+ 'dhs/concerns/record/provider'
42
+ autoload :Request,
43
+ 'dhs/concerns/record/request'
44
+ autoload :Relations,
45
+ 'dhs/concerns/record/relations'
46
+ autoload :Scope,
47
+ 'dhs/concerns/record/scope'
48
+ autoload :Tracing,
49
+ 'dhs/concerns/record/tracing'
50
+ autoload :Update,
51
+ 'dhs/concerns/record/update'
52
+
53
+ include AttributeAssignment
54
+ include Batch
55
+ include Chainable
56
+ include Configuration
57
+ include Create
58
+ include CustomSetters
59
+ include Destroy
60
+ include Endpoints
61
+ include Equality
62
+ include Find
63
+ include FindBy
64
+ include First
65
+ include HrefFor
66
+ include DHS::IsHref
67
+ include Last
68
+ include DHS::Inspect
69
+ include Mapping
70
+ include Merge
71
+ include Model
72
+ include Pagination
73
+ include Provider
74
+ include Request
75
+ include Relations
76
+ include Scope
77
+ include Tracing
78
+ include Update
79
+
80
+ delegate :_proxy, :_endpoint, :merge_raw!, :select, :becomes, :respond_to?, to: :_data
81
+
82
+ def initialize(data = nil, apply_customer_setters = true)
83
+ data ||= DHS::Data.new({}, nil, self.class)
84
+ data = DHS::Data.new(data, nil, self.class) unless data.is_a?(DHS::Data)
85
+ define_singleton_method(:_data) { data }
86
+ apply_custom_setters! if apply_customer_setters
87
+ end
88
+
89
+ def as_json(options = nil)
90
+ _data.as_json(options)
91
+ end
92
+
93
+ def self.build(data = nil)
94
+ new(data)
95
+ end
96
+
97
+ # Override Object#dup because it doesn't support copying any singleton
98
+ # methods, which leads to missing `_data` method when you execute `dup`.
99
+ def dup
100
+ clone
101
+ end
102
+
103
+ protected
104
+
105
+ def method_missing(name, *args, **keyword_args, &block)
106
+ _data.send(name, *args, **keyword_args, &block)
107
+ end
108
+
109
+ def respond_to_missing?(name, include_all = false)
110
+ _data.respond_to_missing?(name, include_all)
111
+ end
112
+ end
data/lib/dhs/rspec.rb ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dhs'
4
+ require 'dhs/test/stubbable_records'
5
+
6
+ RSpec.configure do |config|
7
+ config.before(:each) do
8
+ DHS.config.request_cycle_cache.clear
9
+ end
10
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'local_uri'
4
+ require 'webmock'
5
+
6
+ class DHS::Record
7
+ DEFAULT_LIMIT = DHS::Pagination::Base::DEFAULT_LIMIT
8
+
9
+ def self.stub_all(url, items, options = {})
10
+ extend WebMock::API
11
+
12
+ items.each_slice(DEFAULT_LIMIT).with_index do |(*batch), index|
13
+ uri = LocalUri::URI.new(url)
14
+ uri.query.merge!(
15
+ limit_key(:parameter) => DEFAULT_LIMIT
16
+ )
17
+ offset = pagination_class.page_to_offset(index + 1, DEFAULT_LIMIT)
18
+ unless index.zero?
19
+ uri.query.merge!(
20
+ pagination_key(:parameter) => offset
21
+ )
22
+ end
23
+ request_stub = stub_request(:get, uri.to_s)
24
+ request_stub.with(options) if options.present?
25
+ request_stub.to_return(
26
+ body: {
27
+ items: batch,
28
+ offset: index.zero? ? 0 : offset,
29
+ total: items.length
30
+ }.to_json
31
+ )
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DHS
4
+ class Unprocessable < StandardError
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DHS
4
+ VERSION = '1.0.0'
5
+ end
@@ -0,0 +1,18 @@
1
+ #!/bin/bash -e
2
+
3
+ # custom vars
4
+ # RUBY = ruby-1.9.3-p125
5
+ # GEMSET = location-app-${GitHub pull request id|branch name}
6
+ # standard vars
7
+
8
+ if [ -z "$SKIP_RVM" ]; then
9
+ [[ -s "/usr/local/rvm/scripts/rvm" ]] && . "/usr/local/rvm/scripts/rvm"
10
+ RUBY=${RUBY:-ruby-2.1.2}
11
+ GEMSET=${GEMSET:-DHS}
12
+ rvm use ${RUBY}@${GEMSET} --create
13
+ gem install bundler --no-rdoc --no-ri
14
+ fi
15
+
16
+ bundle install --local || bundle install
17
+ RAILS_ENV=test bundle exec rspec
18
+ rvm --force gemset delete ${RUBY}@${GEMSET}
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe 'Auto OAuth Authentication', type: :request, dummy_models: true do
6
+ context 'without DHC::Auth interceptor enabled' do
7
+ before do
8
+ DHS.configure do |config|
9
+ config.auto_oauth = -> { access_token }
10
+ end
11
+ end
12
+
13
+ it 'shows a warning that it can not perform auto authentication' do
14
+ expect(lambda do
15
+ get '/automatic_authentication/oauth'
16
+ end).to output(
17
+ %r{\[WARNING\] Can't enable auto oauth as DHC::Auth interceptor is not enabled\/configured \(see https://github.com/DePayFi/dhc/blob/master/README.md#authentication-interceptor\)!}
18
+ ).to_stderr
19
+ end
20
+ end
21
+
22
+ context 'with DHC::Auth interceptor enabled' do
23
+ context 'with only one auth provider' do
24
+ let(:token) { ApplicationController::ACCESS_TOKEN }
25
+
26
+ let(:record_request) do
27
+ stub_request(:get, 'http://datastore/v2/records_with_oauth/1')
28
+ .with(
29
+ headers: { 'Authorization' => "Bearer #{token}" }
30
+ ).to_return(status: 200, body: { name: 'Record' }.to_json)
31
+ end
32
+
33
+ let(:records_request) do
34
+ stub_request(:get, 'http://datastore/v2/records_with_oauth?color=blue')
35
+ .with(
36
+ headers: { 'Authorization' => "Bearer #{token}" }
37
+ ).to_return(status: 200, body: { items: [{ name: 'Record' }] }.to_json)
38
+ end
39
+
40
+ before do
41
+ DHS.configure do |config|
42
+ config.auto_oauth = -> { access_token }
43
+ end
44
+ DHC.configure do |config|
45
+ config.interceptors = [DHC::Auth]
46
+ end
47
+ record_request
48
+ records_request
49
+ end
50
+
51
+ after do
52
+ DHC.config.reset
53
+ end
54
+
55
+ it 'applies OAuth credentials for the individual request automatically' do
56
+ get '/automatic_authentication/oauth'
57
+ expect(record_request).to have_been_requested
58
+ expect(records_request).to have_been_requested
59
+ end
60
+ end
61
+
62
+ context 'with multiple auth providers' do
63
+ before do
64
+ DHS.configure do |config|
65
+ config.auto_oauth = proc do
66
+ {
67
+ provider1: access_token_provider_1,
68
+ provider2: access_token_provider_2
69
+ }
70
+ end
71
+ end
72
+ DHC.configure do |config|
73
+ config.interceptors = [DHC::Auth]
74
+ end
75
+ record_request_provider_1
76
+ records_request_provider_2
77
+ records_request_per_endpoint_provider_1
78
+ record_request_per_endpoint_provider_2
79
+ end
80
+
81
+ let(:token) { ApplicationController::ACCESS_TOKEN }
82
+
83
+ let(:record_request_provider_1) do
84
+ stub_request(:get, 'http://datastore/v2/records_with_multiple_oauth_providers_1/1')
85
+ .with(
86
+ headers: { 'Authorization' => "Bearer #{token}_provider_1" }
87
+ ).to_return(status: 200, body: { name: 'Record' }.to_json)
88
+ end
89
+
90
+ let(:records_request_provider_2) do
91
+ stub_request(:get, 'http://datastore/v2/records_with_multiple_oauth_providers_2?color=blue')
92
+ .with(
93
+ headers: { 'Authorization' => "Bearer #{token}_provider_2" }
94
+ ).to_return(status: 200, body: { items: [{ name: 'Record' }] }.to_json)
95
+ end
96
+
97
+ let(:records_request_per_endpoint_provider_1) do
98
+ stub_request(:get, 'http://datastore/v2/records_with_multiple_oauth_providers_per_endpoint?color=blue')
99
+ .with(
100
+ headers: { 'Authorization' => "Bearer #{token}_provider_1" }
101
+ ).to_return(status: 200, body: { items: [{ name: 'Record' }] }.to_json)
102
+ end
103
+
104
+ let(:record_request_per_endpoint_provider_2) do
105
+ stub_request(:get, 'http://datastore/v2/records_with_multiple_oauth_providers_per_endpoint/1')
106
+ .with(
107
+ headers: { 'Authorization' => "Bearer #{token}_provider_2" }
108
+ ).to_return(status: 200, body: { name: 'Record' }.to_json)
109
+ end
110
+
111
+ after do
112
+ DHC.config.reset
113
+ end
114
+
115
+ it 'applies OAuth credentials for the individual request automatically no matter how many auth providers are configured ' do
116
+ get '/automatic_authentication/oauth_with_multiple_providers'
117
+ expect(record_request_provider_1).to have_been_requested
118
+ expect(records_request_provider_2).to have_been_requested
119
+ expect(records_request_per_endpoint_provider_1).to have_been_requested
120
+ expect(record_request_per_endpoint_provider_2).to have_been_requested
121
+ end
122
+ end
123
+
124
+ context 'with provider enabled for auto oauth' do
125
+ let(:token) { ApplicationController::ACCESS_TOKEN }
126
+
127
+ let(:record_request) do
128
+ stub_request(:get, 'http://internalservice/v2/records/1')
129
+ .with(
130
+ headers: { 'Authorization' => "Bearer #{token}" }
131
+ ).to_return(status: 200, body: { name: 'Record' }.to_json)
132
+ end
133
+
134
+ let(:records_request) do
135
+ stub_request(:get, 'http://internalservice/v2/records?color=blue')
136
+ .with(
137
+ headers: { 'Authorization' => "Bearer #{token}" }
138
+ ).to_return(status: 200, body: { items: [{ name: 'Record' }] }.to_json)
139
+ end
140
+
141
+ before do
142
+ DHS.configure do |config|
143
+ config.auto_oauth = -> { access_token }
144
+ end
145
+ DHC.configure do |config|
146
+ config.interceptors = [DHC::Auth]
147
+ end
148
+ record_request
149
+ records_request
150
+ end
151
+
152
+ after do
153
+ DHC.config.reset
154
+ end
155
+
156
+ it 'applies OAuth credentials for the individual request automatically' do
157
+ get '/automatic_authentication/oauth_with_provider'
158
+ expect(record_request).to have_been_requested
159
+ expect(records_request).to have_been_requested
160
+ end
161
+ end
162
+ end
163
+ end