lhs 21.2.1 → 21.3.0.pre.autoauth.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +60 -0
- data/lib/lhs.rb +8 -0
- data/lib/lhs/concerns/autoload_records.rb +20 -3
- data/lib/lhs/concerns/o_auth.rb +25 -0
- data/lib/lhs/concerns/record/configuration.rb +9 -1
- data/lib/lhs/concerns/record/request.rb +25 -9
- data/lib/lhs/config.rb +1 -1
- data/lib/lhs/interceptors/auto_oauth/interceptor.rb +18 -0
- data/lib/lhs/interceptors/auto_oauth/thread_registry.rb +18 -0
- data/lib/lhs/version.rb +1 -1
- data/spec/auto_oauth_spec.rb +51 -0
- data/spec/autoloading_spec.rb +35 -8
- data/spec/dummy/app/controllers/application_controller.rb +7 -0
- data/spec/dummy/app/controllers/automatic_authentication_controller.rb +11 -0
- data/spec/dummy/app/controllers/error_handling_with_chains_controller.rb +2 -2
- data/spec/dummy/app/controllers/extended_rollbar_controller.rb +2 -2
- data/spec/dummy/app/controllers/option_blocks_controller.rb +2 -2
- data/spec/dummy/app/models/dummy_customer.rb +6 -0
- data/spec/dummy/app/models/{record.rb → dummy_record.rb} +1 -1
- data/spec/dummy/app/models/dummy_record_with_oauth.rb +7 -0
- data/spec/dummy/app/models/{user.rb → dummy_user.rb} +1 -1
- data/spec/dummy/app/models/providers/customer_system.rb +7 -0
- data/spec/dummy/config/initializers/lhs.rb +3 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/option_blocks/ensure_reset_between_requests_spec.rb +2 -1
- data/spec/record/error_handling_integration_spec.rb +1 -1
- data/spec/record/includes_spec.rb +59 -0
- data/spec/request_cycle_cache_spec.rb +3 -3
- data/spec/support/reset.rb +31 -9
- metadata +23 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a8391f6f7e78b219c2f48ca87b7f160a28feab0f5e113553a41b339d8f3b72df
|
4
|
+
data.tar.gz: 30e48a8d78cca4d1ed76595cfd72c90bda2cfd77873590254bdf92685df97df1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
31
|
-
Dir.glob(Rails.root.join('app', 'models', '**', '*.rb'))
|
32
|
-
|
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
|
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
|
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
|
-
|
520
|
+
inject_interceptors!(options)
|
519
521
|
options
|
520
522
|
end
|
521
523
|
|
522
|
-
|
523
|
-
|
524
|
-
|
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?(
|
542
|
+
if interceptors.include?(dependecy)
|
527
543
|
# Ensure interceptor is prepend
|
528
|
-
interceptors = interceptors.unshift(
|
544
|
+
interceptors = interceptors.unshift(interceptor)
|
529
545
|
options[:interceptors] = interceptors
|
530
546
|
else
|
531
|
-
warn(
|
547
|
+
warn(warning)
|
532
548
|
end
|
533
549
|
end
|
534
550
|
|
data/lib/lhs/config.rb
CHANGED
@@ -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
|
data/lib/lhs/version.rb
CHANGED
@@ -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
|
data/spec/autoloading_spec.rb
CHANGED
@@ -4,18 +4,45 @@ require "rails_helper"
|
|
4
4
|
|
5
5
|
describe LHS, type: :request do
|
6
6
|
context 'autoloading' do
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
expect(
|
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
|
-
|
16
|
+
DummyUser.endpoints.detect { |endpoint| endpoint.url == 'http://datastore/v2/users' }
|
15
17
|
).to be_present
|
16
18
|
expect(
|
17
|
-
|
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
|
@@ -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 =
|
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 =
|
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
|
-
|
7
|
-
|
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
|
-
|
7
|
+
DummyRecord.where(request: 'second').fetch
|
8
8
|
render text: 'ok'
|
9
9
|
end
|
10
10
|
|
11
11
|
def second
|
12
|
-
|
12
|
+
DummyRecord.where(request: 'second').fetch
|
13
13
|
render text: 'ok'
|
14
14
|
end
|
15
15
|
end
|
data/spec/dummy/config/routes.rb
CHANGED
@@ -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',
|
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'
|
@@ -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/
|
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/
|
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/
|
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
|
data/spec/support/reset.rb
CHANGED
@@ -12,10 +12,15 @@ end
|
|
12
12
|
|
13
13
|
class LHS::Record
|
14
14
|
|
15
|
-
|
15
|
+
DESCENDANTS = []
|
16
16
|
|
17
17
|
def self.inherited(child)
|
18
|
-
|
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::
|
31
|
-
|
32
|
-
|
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
|
41
|
-
|
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.
|
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-
|
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/
|
385
|
-
- spec/dummy/app/models/
|
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:
|
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/
|
602
|
-
- spec/dummy/app/models/
|
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
|