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.
- 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
|