lhs 21.2.1 → 21.3.0.pre.autoauth.1

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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +60 -0
  3. data/lib/lhs.rb +8 -0
  4. data/lib/lhs/concerns/autoload_records.rb +20 -3
  5. data/lib/lhs/concerns/o_auth.rb +25 -0
  6. data/lib/lhs/concerns/record/configuration.rb +9 -1
  7. data/lib/lhs/concerns/record/request.rb +25 -9
  8. data/lib/lhs/config.rb +1 -1
  9. data/lib/lhs/interceptors/auto_oauth/interceptor.rb +18 -0
  10. data/lib/lhs/interceptors/auto_oauth/thread_registry.rb +18 -0
  11. data/lib/lhs/version.rb +1 -1
  12. data/spec/auto_oauth_spec.rb +51 -0
  13. data/spec/autoloading_spec.rb +35 -8
  14. data/spec/dummy/app/controllers/application_controller.rb +7 -0
  15. data/spec/dummy/app/controllers/automatic_authentication_controller.rb +11 -0
  16. data/spec/dummy/app/controllers/error_handling_with_chains_controller.rb +2 -2
  17. data/spec/dummy/app/controllers/extended_rollbar_controller.rb +2 -2
  18. data/spec/dummy/app/controllers/option_blocks_controller.rb +2 -2
  19. data/spec/dummy/app/models/dummy_customer.rb +6 -0
  20. data/spec/dummy/app/models/{record.rb → dummy_record.rb} +1 -1
  21. data/spec/dummy/app/models/dummy_record_with_oauth.rb +7 -0
  22. data/spec/dummy/app/models/{user.rb → dummy_user.rb} +1 -1
  23. data/spec/dummy/app/models/providers/customer_system.rb +7 -0
  24. data/spec/dummy/config/initializers/lhs.rb +3 -0
  25. data/spec/dummy/config/routes.rb +3 -0
  26. data/spec/option_blocks/ensure_reset_between_requests_spec.rb +2 -1
  27. data/spec/record/error_handling_integration_spec.rb +1 -1
  28. data/spec/record/includes_spec.rb +59 -0
  29. data/spec/request_cycle_cache_spec.rb +3 -3
  30. data/spec/support/reset.rb +31 -9
  31. metadata +23 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cdee54df382d303ac6c2df71e4dfb3ce6816d098578fc1919262b2daa937763c
4
- data.tar.gz: '0340809599cfa8cb547c85dd031f8e767a644885637e77bcdc09ad011e2be617'
3
+ metadata.gz: a8391f6f7e78b219c2f48ca87b7f160a28feab0f5e113553a41b339d8f3b72df
4
+ data.tar.gz: 30e48a8d78cca4d1ed76595cfd72c90bda2cfd77873590254bdf92685df97df1
5
5
  SHA512:
6
- metadata.gz: e01af57617fc98df8e61a053bb4b69a527701a29aa1cfd3b842ec1b08ca47e925702ffeedf31b019997a1693409fc00637da0dba4cc205b0cfafba0f1b2769c2
7
- data.tar.gz: 0f7418cee11af1966be59ad017f13469aaa94a96e4214045c05434312690b5d9fad08d4193b8705844e0d5f3411a1988498b50a966cf7324c167f9062493aa8a
6
+ metadata.gz: 00f6961b5546311a919830c913153ecfcbbbeec7a67079db66284ec9280caa6656ea023db44945b0387a0cf02b372736e20632318315f0b3d87811ded26f6aa8
7
+ data.tar.gz: 56c2dd947ba8c5b82800997198c7185bc4d331195d3d1040b779431569ece6bed89eb6822d98bf80aa880482e176f6337cf98518ee176c951363644ce01c2d64
data/README.md CHANGED
@@ -2444,6 +2444,66 @@ LHS.configure do |config|
2444
2444
  end
2445
2445
  ```
2446
2446
 
2447
+ ## Automatic Authentication (OAuth)
2448
+
2449
+ LHS provides a way to have records automatically fetch and use OAuth authentication when performing requests within Rails.
2450
+
2451
+ In order to enable automatic oauth authentication, perform the following steps:
2452
+
2453
+ 1. Make sure LHS is configured to perform `auto_oauth`. Provide a block that when executed in the controller context returns a valid access_token/bearer_token.
2454
+ ```ruby
2455
+ # config/initializers/lhs.rb
2456
+
2457
+ LHS.configure do |config|
2458
+ config.auto_oauth = -> { access_token }
2459
+ end
2460
+ ```
2461
+
2462
+ 2. Opt-in records requiring oauth authentication:
2463
+
2464
+ ```ruby
2465
+ # app/models/record.rb
2466
+
2467
+ class Record < LHS::Record
2468
+ oauth
2469
+ # ...
2470
+ end
2471
+ ```
2472
+
2473
+ 3. Include the `LHS::OAuth` context into your application controller:
2474
+
2475
+ ```ruby
2476
+ # app/controllers/application_controller.rb
2477
+
2478
+ class ApplicationController < ActionController::Base
2479
+ include LHS::OAuth
2480
+
2481
+ # ...
2482
+ end
2483
+ ```
2484
+
2485
+ 4. Make sure you have the `LHC::Auth` interceptor enabled:
2486
+
2487
+ ```ruby
2488
+ # config/initializers/lhc.rb
2489
+
2490
+ LHC.configure do |config|
2491
+ config.interceptors = [LHC::Auth]
2492
+ end
2493
+ ```
2494
+
2495
+ Now you can perform requests based on the record that will be auto authenticated from now on:
2496
+
2497
+ ```ruby
2498
+ # app/controllers/some_controller.rb
2499
+
2500
+ Record.find(1)
2501
+ ```
2502
+ ```
2503
+ https://records/1
2504
+ Authentication: 'Bearer token-12345'
2505
+ ```
2506
+
2447
2507
  ## Option Blocks
2448
2508
 
2449
2509
  In order to apply options to all requests performed in a give block, LHS provides option blocks.
data/lib/lhs.rb CHANGED
@@ -22,6 +22,12 @@ module LHS
22
22
  autoload :Inspect,
23
23
  'lhs/concerns/inspect'
24
24
  module Interceptors
25
+ module AutoOauth
26
+ autoload :ThreadRegistry,
27
+ 'lhs/interceptors/auto_oauth/thread_registry'
28
+ autoload :Interceptor,
29
+ 'lhs/interceptors/auto_oauth/interceptor'
30
+ end
25
31
  module RequestCycleCache
26
32
  autoload :ThreadRegistry,
27
33
  'lhs/interceptors/request_cycle_cache/thread_registry'
@@ -41,6 +47,8 @@ module LHS
41
47
  'lhs/concerns/is_href'
42
48
  autoload :Item,
43
49
  'lhs/item'
50
+ autoload :OAuth,
51
+ 'lhs/concerns/o_auth.rb'
44
52
  autoload :OptionBlocks,
45
53
  'lhs/concerns/option_blocks'
46
54
  autoload :Pagination,
@@ -27,11 +27,28 @@ module AutoloadRecords
27
27
  @app.call(env)
28
28
  end
29
29
 
30
- def self.require_records
31
- Dir.glob(Rails.root.join('app', 'models', '**', '*.rb')).each do |file|
32
- require_dependency file if File.read(file).match('LHS::Record')
30
+ def self.model_files
31
+ Dir.glob(Rails.root.join('app', 'models', '**', '*.rb'))
32
+ end
33
+
34
+ def self.require_direct_inheritance
35
+ model_files.map do |file|
36
+ next unless File.read(file).match('LHS::Record')
37
+ require_dependency file
38
+ file.split('models/').last.gsub('.rb', '').classify
39
+ end.compact
40
+ end
41
+
42
+ def self.require_inheriting_records(parents)
43
+ model_files.each do |file|
44
+ next if parents.none? { |parent| File.read(file).match(parent) }
45
+ require_dependency file
33
46
  end
34
47
  end
48
+
49
+ def self.require_records
50
+ require_inheriting_records(require_direct_inheritance)
51
+ end
35
52
  end
36
53
  end
37
54
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ module LHS
6
+ module OAuth
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ prepend_before_action :lhs_store_oauth_access_token
11
+ end
12
+
13
+ private
14
+
15
+ def lhs_store_oauth_access_token
16
+ lhs_check_auto_oauth_enabled!
17
+ LHS::Interceptors::AutoOauth::ThreadRegistry.access_token = instance_exec(&LHS.config.auto_oauth)
18
+ end
19
+
20
+ def lhs_check_auto_oauth_enabled!
21
+ return if LHS.config.auto_oauth.present? && LHS.config.auto_oauth.is_a?(Proc)
22
+ raise 'You have to enable LHS.config.auto_oauth by passing a proc returning an access token!'
23
+ end
24
+ end
25
+ end
@@ -13,7 +13,15 @@ class LHS::Record
13
13
 
14
14
  module ClassMethods
15
15
  def configuration(args)
16
- @configuration = args.freeze || {}
16
+ @configuration = args || {}
17
+ end
18
+
19
+ def auto_oauth?
20
+ LHS.config.auto_oauth && @configuration && @configuration.fetch(:auto_oauth, false)
21
+ end
22
+
23
+ def oauth
24
+ @configuration.present? ? @configuration.merge!(auto_oauth: true) : configuration(auto_oauth: true)
17
25
  end
18
26
 
19
27
  def item_key
@@ -176,7 +176,7 @@ class LHS::Record
176
176
  else
177
177
  handle_include(includes, data, nil, references[includes])
178
178
  end
179
- data.clear_cache! # as we just included new nested resources
179
+ data.clear_cache! if data.present? # as we just included new nested resources
180
180
  end
181
181
 
182
182
  def handle_include(included, data, sub_includes = nil, reference = nil)
@@ -246,7 +246,9 @@ class LHS::Record
246
246
  def skip_loading_includes?(data, included)
247
247
  if data.collection?
248
248
  data.to_a.none? { |item| item[included].present? }
249
- elsif data[included].present? && data[included].item? && data[included].href.blank?
249
+ elsif data.dig(included).blank?
250
+ true
251
+ elsif data[included].item? && data[included][:href].blank?
250
252
  true
251
253
  else
252
254
  !data._raw.key?(included)
@@ -515,20 +517,34 @@ class LHS::Record
515
517
  options[:url] = compute_url!(options[:params]) unless options.key?(:url)
516
518
  merge_explicit_params!(options[:params])
517
519
  options.delete(:params) if options[:params]&.empty?
518
- inject_request_cycle_cache!(options)
520
+ inject_interceptors!(options)
519
521
  options
520
522
  end
521
523
 
522
- # Injects options into request, that enable the request cycle cache interceptor
523
- def inject_request_cycle_cache!(options)
524
- return unless LHS.config.request_cycle_cache_enabled
524
+ def inject_interceptors!(options)
525
+ inject_interceptor!(
526
+ options,
527
+ LHS::Interceptors::RequestCycleCache::Interceptor,
528
+ LHC::Caching,
529
+ "[WARNING] Can't enable request cycle cache as LHC::Caching interceptor is not enabled/configured (see https://github.com/local-ch/lhc/blob/master/README.md#caching-interceptor)!"
530
+ ) if LHS.config.request_cycle_cache_enabled
531
+
532
+ inject_interceptor!(
533
+ options,
534
+ LHS::Interceptors::AutoOauth::Interceptor,
535
+ LHC::Auth,
536
+ "[WARNING] Can't enable auto oauth as LHC::Auth interceptor is not enabled/configured (see https://github.com/local-ch/lhc/blob/master/README.md#authentication-interceptor)!"
537
+ ) if self.auto_oauth?
538
+ end
539
+
540
+ def inject_interceptor!(options, interceptor, dependecy, warning)
525
541
  interceptors = options[:interceptors] || LHC.config.interceptors
526
- if interceptors.include?(LHC::Caching)
542
+ if interceptors.include?(dependecy)
527
543
  # Ensure interceptor is prepend
528
- interceptors = interceptors.unshift(LHS::Interceptors::RequestCycleCache::Interceptor)
544
+ interceptors = interceptors.unshift(interceptor)
529
545
  options[:interceptors] = interceptors
530
546
  else
531
- warn("[WARNING] Can't enable request cycle cache as LHC::Caching interceptor is not enabled/configured (see https://github.com/local-ch/lhc/blob/master/docs/interceptors/caching.md#caching-interceptor)!")
547
+ warn(warning)
532
548
  end
533
549
  end
534
550
 
@@ -5,7 +5,7 @@ require 'singleton'
5
5
  class LHS::Config
6
6
  include Singleton
7
7
 
8
- attr_accessor :request_cycle_cache_enabled, :request_cycle_cache, :trace
8
+ attr_accessor :request_cycle_cache_enabled, :request_cycle_cache, :trace, :auto_oauth
9
9
 
10
10
  def initialize
11
11
  self.request_cycle_cache_enabled ||= true
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ module LHS
6
+ module Interceptors
7
+ module AutoOauth
8
+ extend ActiveSupport::Concern
9
+
10
+ class Interceptor < LHC::Interceptor
11
+
12
+ def before_request
13
+ request.options[:auth] = { bearer: LHS::Interceptors::AutoOauth::ThreadRegistry.access_token }
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'active_support/per_thread_registry'
5
+
6
+ module LHS
7
+ module Interceptors
8
+ module AutoOauth
9
+ extend ActiveSupport::Concern
10
+ class ThreadRegistry
11
+ # Using ActiveSupports PerThreadRegistry to be able to support Active Support v4.
12
+ # Will switch to thread_mattr_accessor (which comes with Activesupport) when we dropping support for Active Support v4.
13
+ extend ActiveSupport::PerThreadRegistry
14
+ attr_accessor :access_token
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LHS
4
- VERSION = '21.2.1'
4
+ VERSION = '21.3.0.pre.autoauth.1'
5
5
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe 'Auto OAuth Authentication', type: :request, dummy_models: true do
6
+
7
+ context 'without LHC::Auth interceptor enabled' do
8
+
9
+ it 'shows a warning that it can not perform auto authentication' do
10
+ expect(lambda do
11
+ get '/automatic_authentication/oauth'
12
+ end).to output(
13
+ %r{\[WARNING\] Can't enable auto oauth as LHC::Auth interceptor is not enabled\/configured \(see https://github.com/local-ch/lhc/blob/master/README.md#authentication-interceptor\)!}
14
+ ).to_stderr
15
+ end
16
+ end
17
+
18
+ context 'with LHC::Auth interceptor enabled' do
19
+ let(:record_request) do
20
+ stub_request(:get, "http://datastore/v2/records_with_oauth/1")
21
+ .with(
22
+ headers: { 'Authorization' => "Bearer #{ApplicationController::ACCESS_TOKEN}" }
23
+ ).to_return(status: 200, body: { name: 'Record' }.to_json)
24
+ end
25
+
26
+ let(:records_request) do
27
+ stub_request(:get, "http://datastore/v2/records_with_oauth?color=blue")
28
+ .with(
29
+ headers: { 'Authorization' => "Bearer #{ApplicationController::ACCESS_TOKEN}" }
30
+ ).to_return(status: 200, body: { items: [ { name: 'Record' } ] }.to_json)
31
+ end
32
+
33
+ before do
34
+ LHC.configure do |config|
35
+ config.interceptors = [LHC::Auth]
36
+ end
37
+ record_request
38
+ records_request
39
+ end
40
+
41
+ after do
42
+ LHC.config.reset
43
+ end
44
+
45
+ it 'applies OAuth credentials for the individual request automatically' do
46
+ get '/automatic_authentication/oauth'
47
+ expect(record_request).to have_been_requested
48
+ expect(records_request).to have_been_requested
49
+ end
50
+ end
51
+ end
@@ -4,18 +4,45 @@ require "rails_helper"
4
4
 
5
5
  describe LHS, type: :request do
6
6
  context 'autoloading' do
7
- it "pre/re-loads all LHS classes initialy,|
8
- because it's necessary for endpoint-to-record-class-discovery",
9
- reset_before: false do
10
- all_endpoints = LHS::Record::Endpoints.all
11
- expect(all_endpoints['http://datastore/v2/users']).to be_present
12
- expect(all_endpoints['http://datastore/v2/users/{id}']).to be_present
7
+
8
+ let(:endpoints) { LHS::Record::Endpoints.all }
9
+
10
+ it "pre/re-loads all LHS classes initialy, because it's necessary for endpoint-to-record-class-discovery", reset_before: false do
11
+
12
+ expect(endpoints['http://datastore/v2/users']).to be_present
13
+ expect(endpoints['http://datastore/v2/users/{id}']).to be_present
14
+
13
15
  expect(
14
- User.endpoints.detect { |endpoint| endpoint.url == 'http://datastore/v2/users' }
16
+ DummyUser.endpoints.detect { |endpoint| endpoint.url == 'http://datastore/v2/users' }
15
17
  ).to be_present
16
18
  expect(
17
- User.endpoints.detect { |endpoint| endpoint.url == 'http://datastore/v2/users/{id}' }
19
+ DummyUser.endpoints.detect { |endpoint| endpoint.url == 'http://datastore/v2/users/{id}' }
18
20
  ).to be_present
19
21
  end
22
+
23
+ it "also pre/re-loads all LHS classes that inherited from an LHS provider, because it's necessary for endpoint-to-record-class-discovery", reset_before: false do
24
+
25
+ expect(endpoints['http://customers']).to be_present
26
+ expect(endpoints['http://customers/{id}']).to be_present
27
+
28
+ expect(
29
+ DummyCustomer.endpoints.detect { |endpoint| endpoint.url == 'http://customers' }
30
+ ).to be_present
31
+ expect(
32
+ DummyCustomer.endpoints.detect { |endpoint| endpoint.url == 'http://customers/{id}' }
33
+ ).to be_present
34
+
35
+ customer_request = stub_request(:get, "http://customers/1")
36
+ .with(
37
+ headers: {
38
+ 'Authorization' => 'token123'
39
+ }
40
+ )
41
+ .to_return(body: { name: 'Steve' }.to_json)
42
+
43
+ DummyCustomer.find(1)
44
+
45
+ expect(customer_request).to have_been_requested
46
+ end
20
47
  end
21
48
  end
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ApplicationController < ActionController::Base
4
+ include LHS::OAuth
5
+ ACCESS_TOKEN = 'token-12345'.freeze
6
+
4
7
  # Prevent CSRF attacks by raising an exception.
5
8
  # For APIs, you may want to use :null_session instead.
6
9
  protect_from_forgery with: :exception
@@ -8,4 +11,8 @@ class ApplicationController < ActionController::Base
8
11
  def root
9
12
  render nothing: true
10
13
  end
14
+
15
+ def access_token
16
+ ACCESS_TOKEN
17
+ end
11
18
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AutomaticAuthenticationController < ApplicationController
4
+
5
+ def o_auth
6
+ render json: {
7
+ record: DummyRecordWithOauth.find(1).as_json,
8
+ records: DummyRecordWithOauth.where(color: 'blue').as_json
9
+ }
10
+ end
11
+ end
@@ -5,7 +5,7 @@ class ErrorHandlingWithChainsController < ApplicationController
5
5
  # Example where the query chain is resolved
6
6
  # in the view (during render 'show')
7
7
  def fetch_in_view
8
- @records = Record
8
+ @records = DummyRecord
9
9
  .handle(LHC::Error, ->(error) { handle_error(error) })
10
10
  .where(color: 'blue')
11
11
  render 'show'
@@ -15,7 +15,7 @@ class ErrorHandlingWithChainsController < ApplicationController
15
15
  # Example where the query chain is resolved
16
16
  # before the view is rendered
17
17
  def fetch_in_controller
18
- @records = Record
18
+ @records = DummyRecord
19
19
  .handle(LHC::Error, ->(error) { handle_error(error) })
20
20
  .where(color: 'blue').fetch
21
21
  render 'show'
@@ -3,8 +3,8 @@
3
3
  class ExtendedRollbarController < ApplicationController
4
4
 
5
5
  def extended_rollbar
6
- Record.where(color: 'blue').fetch
7
- Record.where(color: 'red').fetch
6
+ DummyRecord.where(color: 'blue').fetch
7
+ DummyRecord.where(color: 'red').fetch
8
8
  raise "Let's see if rollbar logs information about what kind of requests where made around here!"
9
9
  end
10
10
  end
@@ -4,12 +4,12 @@ class OptionBlocksController < ApplicationController
4
4
 
5
5
  def first
6
6
  LHS::OptionBlocks::CurrentOptionBlock.options = { params: { request: 'first' } }
7
- Record.where(request: 'second').fetch
7
+ DummyRecord.where(request: 'second').fetch
8
8
  render text: 'ok'
9
9
  end
10
10
 
11
11
  def second
12
- Record.where(request: 'second').fetch
12
+ DummyRecord.where(request: 'second').fetch
13
13
  render text: 'ok'
14
14
  end
15
15
  end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DummyCustomer < Providers::CustomerSystem
4
+ endpoint 'http://customers'
5
+ endpoint 'http://customers/{id}'
6
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Record < LHS::Record
3
+ class DummyRecord < LHS::Record
4
4
  endpoint 'http://datastore/v2/records'
5
5
  endpoint 'http://datastore/v2/records/{id}'
6
6
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DummyRecordWithOauth < LHS::Record
4
+ oauth
5
+ endpoint 'http://datastore/v2/records_with_oauth'
6
+ endpoint 'http://datastore/v2/records_with_oauth/{id}'
7
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class User < LHS::Record
3
+ class DummyUser < LHS::Record
4
4
  endpoint 'http://datastore/v2/users'
5
5
  endpoint 'http://datastore/v2/users/{id}'
6
6
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Providers
4
+ class CustomerSystem < LHS::Record
5
+ provider(headers: { 'Authorization': 'token123' })
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ LHS.configure do |config|
2
+ config.auto_oauth = -> { access_token }
3
+ end
@@ -3,6 +3,9 @@
3
3
  Rails.application.routes.draw do
4
4
  root 'application#root'
5
5
 
6
+ # Automatic Authentication
7
+ get 'automatic_authentication/oauth' => 'automatic_authentication#o_auth'
8
+
6
9
  # Request Cycle Cache
7
10
  get 'request_cycle_cache/simple' => 'request_cycle_cache#simple'
8
11
  get 'request_cycle_cache/no_caching_interceptor' => 'request_cycle_cache#no_caching_interceptor'
@@ -13,7 +13,8 @@ describe 'Option Blocks', type: :request do
13
13
  .to_return(status: 200)
14
14
  end
15
15
 
16
- it 'always ensures option blocks are always reset for new requests', dummy_models: true do
16
+ it 'always ensures option blocks are always reset for new requests',
17
+ dummy_models: true, reset_before: true do
17
18
  get '/option_blocks/first'
18
19
  expect(first_request).to have_been_made.once
19
20
  get '/option_blocks/second'
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rails_helper'
4
- require 'lhc/test/cache_helper.rb'
4
+ require 'lhc/rspec'
5
5
 
6
6
  describe 'Error handling with chains', type: :request do
7
7
  let!(:request) do
@@ -664,5 +664,64 @@ describe LHS::Record do
664
664
  expect(nested_request).to have_been_requested
665
665
  expect(place.customer.salesforce.name).to eq 'Steve'
666
666
  end
667
+
668
+ context 'included data has a configured record endpoint option' do
669
+ before do
670
+ class SalesforceCustomer < LHS::Record
671
+ endpoint 'https://salesforce/customers/{id}', headers: { 'Authorization': 'Bearer 123' }
672
+ end
673
+ end
674
+
675
+ let!(:nested_request) do
676
+ stub_request(:get, "https://salesforce/customers/1")
677
+ .with(headers: { 'Authorization' => 'Bearer 123' })
678
+ .to_return(body: {
679
+ name: 'Steve'
680
+ }.to_json)
681
+ end
682
+
683
+ it 'includes data that has been nested in an additional structure' do
684
+ place = Place.includes(customer: :salesforce).find(1)
685
+ expect(nested_request).to have_been_requested
686
+ expect(place.customer.salesforce.name).to eq 'Steve'
687
+ end
688
+ end
689
+ end
690
+
691
+ context 'include empty structures' do
692
+ before do
693
+ class Place < LHS::Record
694
+ endpoint 'https://places/{id}'
695
+ end
696
+ stub_request(:get, "https://places/1")
697
+ .to_return(body: {
698
+ id: '123'
699
+ }.to_json)
700
+ end
701
+
702
+ it 'skips includes when there is nothing and also does not raise an exception' do
703
+ expect(-> {
704
+ Place.includes(contracts: :product).find(1)
705
+ }).not_to raise_exception
706
+ end
707
+ end
708
+
709
+ context 'include partially empty structures' do
710
+ before do
711
+ class Place < LHS::Record
712
+ endpoint 'https://places/{id}'
713
+ end
714
+ stub_request(:get, "https://places/1")
715
+ .to_return(body: {
716
+ id: '123',
717
+ customer: {}
718
+ }.to_json)
719
+ end
720
+
721
+ it 'skips includes when there is nothing and also does not raise an exception' do
722
+ expect(-> {
723
+ Place.includes(customer: :salesforce).find(1)
724
+ }).not_to raise_exception
725
+ end
667
726
  end
668
727
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rails_helper'
4
- require 'lhc/test/cache_helper.rb'
4
+ require 'lhc/rspec'
5
5
 
6
6
  describe 'Request Cycle Cache', type: :request do
7
7
  let!(:request) do
@@ -37,7 +37,7 @@ describe 'Request Cycle Cache', type: :request do
37
37
  expect(lambda do
38
38
  get '/request_cycle_cache/no_caching_interceptor'
39
39
  end).to output(
40
- %r{\[WARNING\] Can't enable request cycle cache as LHC::Caching interceptor is not enabled/configured \(see https://github.com/local-ch/lhc/blob/master/docs/interceptors/caching.md#caching-interceptor\)!}
40
+ %r{\[WARNING\] Can't enable request cycle cache as LHC::Caching interceptor is not enabled/configured \(see https://github.com/local-ch/lhc/blob/master/README.md#caching-interceptor\)!}
41
41
  ).to_stderr
42
42
  expect(request).to have_been_made.times(2)
43
43
  end
@@ -70,7 +70,7 @@ describe 'Request Cycle Cache', type: :request do
70
70
  expect(lambda do
71
71
  get '/request_cycle_cache/no_caching_interceptor'
72
72
  end).not_to output(
73
- %r{\[WARNING\] Can't enable request cycle cache as LHC::Caching interceptor is not enabled/configured \(see https://github.com/local-ch/lhc/blob/master/docs/interceptors/caching.md#caching-interceptor\)!}
73
+ %r{\[WARNING\] Can't enable request cycle cache as LHC::Caching interceptor is not enabled/configured \(see https://github.com/local-ch/lhc/blob/master/README.md#caching-interceptor\)!}
74
74
  ).to_stderr
75
75
  expect(request).to have_been_made.times(2)
76
76
  end
@@ -12,10 +12,15 @@ end
12
12
 
13
13
  class LHS::Record
14
14
 
15
- CHILDREN = []
15
+ DESCENDANTS = []
16
16
 
17
17
  def self.inherited(child)
18
- CHILDREN.push(child)
18
+ DESCENDANTS.push(child)
19
+ child.singleton_class.class_eval do
20
+ define_method(:inherited) do |grand_child|
21
+ DESCENDANTS.push(grand_child)
22
+ end
23
+ end
19
24
  super
20
25
  end
21
26
 
@@ -27,9 +32,28 @@ end
27
32
 
28
33
  def reset_lhs
29
34
  LHS::Record::Endpoints.all = {}
30
- LHS::Record::CHILDREN.each do |child|
31
- child.endpoints = [] if !child.name['LHS'] && defined?(child.endpoints)
32
- child.configuration({}) if !child.name['LHS']
35
+ LHS::Record::DESCENDANTS.each do |decendant|
36
+ decendant.endpoints = [] if !decendant.name['LHS'] && defined?(decendant.endpoints)
37
+ decendant.configuration({}) if !decendant.name['LHS']
38
+ end
39
+ end
40
+
41
+ def model_files_to_reload
42
+ Dir.glob(Rails.root.join('app', 'models', '**', '*.rb'))
43
+ end
44
+
45
+ def reload_direct_inheritance
46
+ model_files_to_reload.map do |file|
47
+ next unless File.read(file).match('LHS::Record')
48
+ load file
49
+ file.split('models/').last.gsub('.rb', '').classify
50
+ end.compact
51
+ end
52
+
53
+ def reload_inheriting_records(parents)
54
+ model_files_to_reload.each do |file|
55
+ next if parents.none? { |parent| File.read(file).match(parent) }
56
+ load file
33
57
  end
34
58
  end
35
59
 
@@ -37,9 +61,7 @@ RSpec.configure do |config|
37
61
  config.before do |spec|
38
62
  reset_lhc unless spec.metadata.key?(:reset_before) && spec.metadata[:reset_before] == false
39
63
  reset_lhs unless spec.metadata.key?(:reset_before) && spec.metadata[:reset_before] == false
40
- next unless spec.metadata.key?(:dummy_models) && spec.metadata[:dummy_models] == true
41
- Dir.glob(Rails.root.join('app', 'models', '**', '*.rb')).each do |file|
42
- load file if File.read(file).match('LHS::Record')
43
- end
64
+ next if !spec.metadata.key?(:dummy_models) || spec.metadata[:dummy_models] != true
65
+ reload_inheriting_records(reload_direct_inheritance)
44
66
  end
45
67
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lhs
3
3
  version: !ruby/object:Gem::Version
4
- version: 21.2.1
4
+ version: 21.3.0.pre.autoauth.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - https://github.com/local-ch/lhs/graphs/contributors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-20 00:00:00.000000000 Z
11
+ date: 2020-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -283,6 +283,7 @@ files:
283
283
  - lib/lhs/concerns/item/save.rb
284
284
  - lib/lhs/concerns/item/update.rb
285
285
  - lib/lhs/concerns/item/validation.rb
286
+ - lib/lhs/concerns/o_auth.rb
286
287
  - lib/lhs/concerns/option_blocks.rb
287
288
  - lib/lhs/concerns/proxy/accessors.rb
288
289
  - lib/lhs/concerns/proxy/create.rb
@@ -314,6 +315,8 @@ files:
314
315
  - lib/lhs/config.rb
315
316
  - lib/lhs/data.rb
316
317
  - lib/lhs/endpoint.rb
318
+ - lib/lhs/interceptors/auto_oauth/interceptor.rb
319
+ - lib/lhs/interceptors/auto_oauth/thread_registry.rb
317
320
  - lib/lhs/interceptors/extended_rollbar/handler.rb
318
321
  - lib/lhs/interceptors/extended_rollbar/interceptor.rb
319
322
  - lib/lhs/interceptors/extended_rollbar/thread_registry.rb
@@ -340,6 +343,7 @@ files:
340
343
  - lib/lhs/version.rb
341
344
  - script/ci/build.sh
342
345
  - spec/.DS_Store
346
+ - spec/auto_oauth_spec.rb
343
347
  - spec/autoloading_spec.rb
344
348
  - spec/collection/accessors_spec.rb
345
349
  - spec/collection/collection_items_spec.rb
@@ -372,6 +376,7 @@ files:
372
376
  - spec/dummy/app/assets/javascripts/application.js
373
377
  - spec/dummy/app/assets/stylesheets/application.css
374
378
  - spec/dummy/app/controllers/application_controller.rb
379
+ - spec/dummy/app/controllers/automatic_authentication_controller.rb
375
380
  - spec/dummy/app/controllers/concerns/.keep
376
381
  - spec/dummy/app/controllers/error_handling_with_chains_controller.rb
377
382
  - spec/dummy/app/controllers/extended_rollbar_controller.rb
@@ -381,8 +386,11 @@ files:
381
386
  - spec/dummy/app/mailers/.keep
382
387
  - spec/dummy/app/models/.keep
383
388
  - spec/dummy/app/models/concerns/.keep
384
- - spec/dummy/app/models/record.rb
385
- - spec/dummy/app/models/user.rb
389
+ - spec/dummy/app/models/dummy_customer.rb
390
+ - spec/dummy/app/models/dummy_record.rb
391
+ - spec/dummy/app/models/dummy_record_with_oauth.rb
392
+ - spec/dummy/app/models/dummy_user.rb
393
+ - spec/dummy/app/models/providers/customer_system.rb
386
394
  - spec/dummy/app/views/error_handling_with_chains/error.html.erb
387
395
  - spec/dummy/app/views/error_handling_with_chains/show.html.erb
388
396
  - spec/dummy/app/views/form_for.html.erb
@@ -402,6 +410,7 @@ files:
402
410
  - spec/dummy/config/initializers/cookies_serializer.rb
403
411
  - spec/dummy/config/initializers/filter_parameter_logging.rb
404
412
  - spec/dummy/config/initializers/inflections.rb
413
+ - spec/dummy/config/initializers/lhs.rb
405
414
  - spec/dummy/config/initializers/mime_types.rb
406
415
  - spec/dummy/config/initializers/rollbar.rb
407
416
  - spec/dummy/config/initializers/session_store.rb
@@ -546,9 +555,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
546
555
  version: 2.3.0
547
556
  required_rubygems_version: !ruby/object:Gem::Requirement
548
557
  requirements:
549
- - - ">="
558
+ - - ">"
550
559
  - !ruby/object:Gem::Version
551
- version: '0'
560
+ version: 1.3.1
552
561
  requirements:
553
562
  - Ruby >= 2.3.0
554
563
  rubygems_version: 3.0.6
@@ -557,6 +566,7 @@ specification_version: 4
557
566
  summary: 'REST services accelerator: Rails gem providing an easy, active-record-like
558
567
  interface for http (hypermedia) json services'
559
568
  test_files:
569
+ - spec/auto_oauth_spec.rb
560
570
  - spec/autoloading_spec.rb
561
571
  - spec/collection/accessors_spec.rb
562
572
  - spec/collection/collection_items_spec.rb
@@ -589,6 +599,7 @@ test_files:
589
599
  - spec/dummy/app/assets/javascripts/application.js
590
600
  - spec/dummy/app/assets/stylesheets/application.css
591
601
  - spec/dummy/app/controllers/application_controller.rb
602
+ - spec/dummy/app/controllers/automatic_authentication_controller.rb
592
603
  - spec/dummy/app/controllers/concerns/.keep
593
604
  - spec/dummy/app/controllers/error_handling_with_chains_controller.rb
594
605
  - spec/dummy/app/controllers/extended_rollbar_controller.rb
@@ -598,8 +609,11 @@ test_files:
598
609
  - spec/dummy/app/mailers/.keep
599
610
  - spec/dummy/app/models/.keep
600
611
  - spec/dummy/app/models/concerns/.keep
601
- - spec/dummy/app/models/record.rb
602
- - spec/dummy/app/models/user.rb
612
+ - spec/dummy/app/models/dummy_customer.rb
613
+ - spec/dummy/app/models/dummy_record.rb
614
+ - spec/dummy/app/models/dummy_record_with_oauth.rb
615
+ - spec/dummy/app/models/dummy_user.rb
616
+ - spec/dummy/app/models/providers/customer_system.rb
603
617
  - spec/dummy/app/views/error_handling_with_chains/error.html.erb
604
618
  - spec/dummy/app/views/error_handling_with_chains/show.html.erb
605
619
  - spec/dummy/app/views/form_for.html.erb
@@ -619,6 +633,7 @@ test_files:
619
633
  - spec/dummy/config/initializers/cookies_serializer.rb
620
634
  - spec/dummy/config/initializers/filter_parameter_logging.rb
621
635
  - spec/dummy/config/initializers/inflections.rb
636
+ - spec/dummy/config/initializers/lhs.rb
622
637
  - spec/dummy/config/initializers/mime_types.rb
623
638
  - spec/dummy/config/initializers/rollbar.rb
624
639
  - spec/dummy/config/initializers/session_store.rb