pdc 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.editorconfig +27 -0
- data/.gitignore +10 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +21 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +32 -0
- data/Guardfile +36 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +11 -0
- data/bin/console +11 -0
- data/bin/setup +11 -0
- data/docs/.gitignore +3 -0
- data/docs/LICENSE_sphinx_deployment +27 -0
- data/docs/Makefile +179 -0
- data/docs/source/conf.py +257 -0
- data/docs/source/example.rst +10 -0
- data/docs/source/index.rst +23 -0
- data/docs/sphinx_deployment.mk +117 -0
- data/examples/active_attr.rb +33 -0
- data/examples/http_failures.rb +18 -0
- data/examples/local_pdc_dev.rb +15 -0
- data/examples/logger.rb +50 -0
- data/examples/pdc_curb_access_token.rb +36 -0
- data/examples/pdc_resource_tests.rb +173 -0
- data/examples/pdc_test_cache.rb +48 -0
- data/examples/prod_failures.rb +26 -0
- data/examples/prod_pdc.rb +14 -0
- data/lib/pdc/base.rb +14 -0
- data/lib/pdc/config.rb +157 -0
- data/lib/pdc/errors.rb +8 -0
- data/lib/pdc/http/errors.rb +43 -0
- data/lib/pdc/http/request/append_slash.rb +19 -0
- data/lib/pdc/http/request.rb +12 -0
- data/lib/pdc/http/response/pagination.rb +43 -0
- data/lib/pdc/http/response/parser.rb +62 -0
- data/lib/pdc/http/response/raise_error.rb +13 -0
- data/lib/pdc/http/response.rb +3 -0
- data/lib/pdc/http/result.rb +28 -0
- data/lib/pdc/http.rb +12 -0
- data/lib/pdc/logger.rb +19 -0
- data/lib/pdc/resource/attribute_modifier.rb +43 -0
- data/lib/pdc/resource/attribute_store.rb +22 -0
- data/lib/pdc/resource/attributes.rb +144 -0
- data/lib/pdc/resource/errors.rb +3 -0
- data/lib/pdc/resource/identity.rb +75 -0
- data/lib/pdc/resource/path.rb +63 -0
- data/lib/pdc/resource/per_thread_registry.rb +54 -0
- data/lib/pdc/resource/relation/finder.rb +24 -0
- data/lib/pdc/resource/relation/pagination.rb +33 -0
- data/lib/pdc/resource/relation/query.rb +14 -0
- data/lib/pdc/resource/relation.rb +81 -0
- data/lib/pdc/resource/rest_api.rb +34 -0
- data/lib/pdc/resource/scope_registry.rb +19 -0
- data/lib/pdc/resource/scopes.rb +29 -0
- data/lib/pdc/resource/value_parser.rb +31 -0
- data/lib/pdc/resource/wip.rb +0 -0
- data/lib/pdc/resource.rb +12 -0
- data/lib/pdc/v1/arch.rb +6 -0
- data/lib/pdc/v1/product.rb +5 -0
- data/lib/pdc/v1/release.rb +18 -0
- data/lib/pdc/v1/release_variant.rb +17 -0
- data/lib/pdc/v1.rb +8 -0
- data/lib/pdc/version.rb +3 -0
- data/lib/pdc.rb +25 -0
- data/pdc.gemspec +38 -0
- data/spec/fixtures/vcr/_page_count_returns_total_count.yml +141 -0
- data/spec/fixtures/vcr/brew_can_be_nil.yml +61 -0
- data/spec/fixtures/vcr/brew_may_be_present.yml +50 -0
- data/spec/fixtures/vcr/caches_multiple_response.yml +173 -0
- data/spec/fixtures/vcr/caches_response_with_a_query.yml +64 -0
- data/spec/fixtures/vcr/caches_response_with_multiple_query.yml +64 -0
- data/spec/fixtures/vcr/caches_response_without_query.yml +61 -0
- data/spec/fixtures/vcr/can_iterate_using_each.yml +187 -0
- data/spec/fixtures/vcr/fetches_variants_of_a_release.yml +663 -0
- data/spec/fixtures/vcr/must_return_number_of_resources.yml +61 -0
- data/spec/fixtures/vcr/preserves_the_filters.yml +49 -0
- data/spec/fixtures/vcr/returns_resources_on_that_page.yml +49 -0
- data/spec/fixtures/vcr/returns_the_total_count_and_not_items_in_page.yml +135 -0
- data/spec/fixtures/vcr/should_not_be_in_the_list_of_attributes.yml +95 -0
- data/spec/fixtures/vcr/works_with_where.yml +49 -0
- data/spec/pdc/config_spec.rb +115 -0
- data/spec/pdc/http/errors_spec.rb +58 -0
- data/spec/pdc/resource/attributes_spec.rb +231 -0
- data/spec/pdc/resource/cache_spec.rb +88 -0
- data/spec/pdc/resource/count_spec.rb +45 -0
- data/spec/pdc/resource/identity_spec.rb +96 -0
- data/spec/pdc/resource/pagination_spec.rb +51 -0
- data/spec/pdc/resource/path_spec.rb +30 -0
- data/spec/pdc/resource/relation_spec.rb +94 -0
- data/spec/pdc/resource/rest_api_spec.rb +23 -0
- data/spec/pdc/resource/value_parser_spec.rb +15 -0
- data/spec/pdc/resource/wip_spec.rb +0 -0
- data/spec/pdc/v1/arch_spec.rb +34 -0
- data/spec/pdc/v1/release_spec.rb +54 -0
- data/spec/pdc/version_spec.rb +5 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/fixtures.rb +116 -0
- data/spec/support/vcr.rb +10 -0
- data/spec/support/webmock.rb +34 -0
- metadata +295 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
require_relative '../lib/pdc'
|
|
2
|
+
require_relative '../spec/spec_helper'
|
|
3
|
+
|
|
4
|
+
WebMock.disable! # this uses real server
|
|
5
|
+
|
|
6
|
+
#### service config ####
|
|
7
|
+
def token
|
|
8
|
+
script_path = File.expand_path(File.dirname(__FILE__))
|
|
9
|
+
token_path = File.join(script_path, '.token', 'pdc.prod')
|
|
10
|
+
File.read(token_path).chomp.tap { |x| puts "Using token :#{x.ai}" }
|
|
11
|
+
rescue Errno::ENOENT => e
|
|
12
|
+
puts "Hey! did you forget to create #{token_path.ai} \n".red
|
|
13
|
+
raise e
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
#### server config ####
|
|
17
|
+
|
|
18
|
+
server = {
|
|
19
|
+
local: {
|
|
20
|
+
site: 'http://localhost:8000',
|
|
21
|
+
token: nil
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
prod: {
|
|
25
|
+
site: 'https://pdc.engineering.redhat.com',
|
|
26
|
+
token: ->() { token } # read token only if needed
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# server to use
|
|
31
|
+
pdc = server[:prod]
|
|
32
|
+
pdc = server[:local]
|
|
33
|
+
|
|
34
|
+
PDC.configure do |config|
|
|
35
|
+
config.site = pdc[:site]
|
|
36
|
+
auth_token = pdc[:token]
|
|
37
|
+
config.token = pdc[:token].call if auth_token
|
|
38
|
+
config.requires_token = auth_token.present? # to_bool
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
#### product ####
|
|
42
|
+
|
|
43
|
+
describe PDC::V1::Product do
|
|
44
|
+
let(:product_class) { PDC::V1::Product }
|
|
45
|
+
it 'has_many product_versions' do
|
|
46
|
+
p1 = product_class.scoped.first
|
|
47
|
+
p1.product_versions.must_be_instance_of Array
|
|
48
|
+
# TODO: continue the after release and release-variants
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
#### Arch ####
|
|
53
|
+
|
|
54
|
+
describe PDC::V1::Arch do
|
|
55
|
+
let(:arch_class) { PDC::V1::Arch }
|
|
56
|
+
|
|
57
|
+
describe 'contents!' do
|
|
58
|
+
it 'only returns contents of a page' do
|
|
59
|
+
arches = arch_class.scoped.contents!
|
|
60
|
+
(0..arch_class.count).must_include arches.length
|
|
61
|
+
|
|
62
|
+
arches_pg1 = arch_class.page(1).contents!
|
|
63
|
+
arches_pg1.must_equal arches
|
|
64
|
+
|
|
65
|
+
arches_pg2 = arch_class.page(2).contents!
|
|
66
|
+
arches_pg1.wont_equal arches_pg2
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
describe '#page' do
|
|
71
|
+
it 'can access page(2)' do
|
|
72
|
+
arch_class.page(2).must_be_instance_of PDC::Resource::Relation
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'must return resource on page(2).to_a' do
|
|
76
|
+
arches = arch_class.page(2).to_a
|
|
77
|
+
arches.first.must_be_instance_of arch_class
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe '#each_page' do
|
|
82
|
+
it 'must be able to iterate using map' do
|
|
83
|
+
resource_count = arch_class.count
|
|
84
|
+
|
|
85
|
+
arches = arch_class.scoped.map(&:name)
|
|
86
|
+
arches.must_be_instance_of Array
|
|
87
|
+
arches.first.must_be_instance_of String
|
|
88
|
+
|
|
89
|
+
arches.count.must_equal resource_count
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe '#page_size' do
|
|
94
|
+
it 'must return all records when page_size is -1' do
|
|
95
|
+
all_arches = arch_class.page_size(-1).to_a
|
|
96
|
+
all_arches_count = arch_class.count
|
|
97
|
+
all_arches.must_be_instance_of Array
|
|
98
|
+
all_arches.length.must_equal all_arches_count
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'all! is the same as page_size(-1)' do
|
|
102
|
+
all_arches = arch_class.all!
|
|
103
|
+
all_arches_count = arch_class.count
|
|
104
|
+
all_arches.must_be_instance_of Array
|
|
105
|
+
all_arches.length.must_equal all_arches_count
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
describe '#find' do
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
### TODO: debug - remove this
|
|
114
|
+
A = PDC::V1::Arch
|
|
115
|
+
R = PDC::V1::Release
|
|
116
|
+
RV = PDC::V1::ReleaseVariant
|
|
117
|
+
###
|
|
118
|
+
|
|
119
|
+
describe PDC::V1::Release do
|
|
120
|
+
let(:release_class) { PDC::V1::Release }
|
|
121
|
+
let(:existing_release) { release_class.all!.first }
|
|
122
|
+
|
|
123
|
+
describe '#find' do
|
|
124
|
+
it 'must return a record' do
|
|
125
|
+
found = release_class.find(existing_release.release_id)
|
|
126
|
+
found.must_be_instance_of release_class
|
|
127
|
+
found.release_id.must_equal existing_release.release_id
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
describe '#variants' do
|
|
132
|
+
let(:sat_6_at_rhel_7) { release_class.find('satellite-6.0.4@rhel-7') }
|
|
133
|
+
let(:release_variant_class) { PDC::V1::ReleaseVariant }
|
|
134
|
+
|
|
135
|
+
it 'must be a HasMany association' do
|
|
136
|
+
skip 'TODO implement Assocations'
|
|
137
|
+
variants_relation = sat_6_at_rhel_7.variants
|
|
138
|
+
variants_relation.must_be_instance_of PDC::Resource::HasManyAssociation
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it 'must be able to call all! to return all variants ' do
|
|
142
|
+
variants_relation = sat_6_at_rhel_7.variants
|
|
143
|
+
ap variants_relation.params
|
|
144
|
+
release_id = variants_relation.params[:release]
|
|
145
|
+
release_id.must_equal sat_6_at_rhel_7.id
|
|
146
|
+
|
|
147
|
+
all_variants = variants_relation.all!
|
|
148
|
+
expected = variants_relation.where(page_size: -1).to_a
|
|
149
|
+
|
|
150
|
+
all_variants.must_equal expected
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it 'must return release variants for a release' do
|
|
154
|
+
variants_relation = sat_6_at_rhel_7.variants
|
|
155
|
+
variants = variants_relation.to_a
|
|
156
|
+
variants.must_be_instance_of Array
|
|
157
|
+
variants.length.must_be :>=, 1
|
|
158
|
+
variants.first.must_be_instance_of release_variant_class
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
describe PDC::V1::ReleaseVariant do
|
|
164
|
+
let(:release_class) { PDC::V1::Release }
|
|
165
|
+
let(:release_variant_class) { PDC::V1::ReleaseVariant }
|
|
166
|
+
let(:a_variant) { release_variant_class.all!.first }
|
|
167
|
+
|
|
168
|
+
describe '#release' do
|
|
169
|
+
it 'must fetch the release it belongs to' do
|
|
170
|
+
a_variant.release.must_be_instance_of release_class
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'benchmark'
|
|
2
|
+
require_relative '../lib/pdc'
|
|
3
|
+
require_relative '../spec/spec_helper'
|
|
4
|
+
|
|
5
|
+
WebMock.disable!
|
|
6
|
+
PDC.configure do |config|
|
|
7
|
+
# dev server
|
|
8
|
+
config.site = 'https://pdc.host.dev.eng.pek2.redhat.com/'
|
|
9
|
+
config.cache_store = ActiveSupport::Cache.lookup_store(
|
|
10
|
+
:file_store, [File.join(ENV['TMPDIR'] || '/tmp', 'cache')])
|
|
11
|
+
config.log_level = :info
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
ActiveSupport::Notifications.subscribe "http_cache.faraday" do |*args|
|
|
15
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
16
|
+
puts " >>> ".yellow + "cache: #{event.payload[:cache_status]}"
|
|
17
|
+
ap event.payload
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def benchmark(description, opts = {}, &block)
|
|
21
|
+
puts "Running: #{description}"
|
|
22
|
+
|
|
23
|
+
initial = Benchmark.measure(&block)
|
|
24
|
+
repeat = Benchmark.measure(&block)
|
|
25
|
+
|
|
26
|
+
puts "Initial : #{initial.to_s.chomp} >> #{initial.real.round(2).ai}"
|
|
27
|
+
puts "Repeat : #{repeat.to_s.chomp} >> #{repeat.real.round(2).ai} \n"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def main
|
|
31
|
+
benchmark "fetch all" do
|
|
32
|
+
PDC::V1::Release.all
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
benchmark "fetch with page number" do
|
|
36
|
+
PDC::V1::Release.page(2).contents!
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
benchmark "fetch with page number and size" do
|
|
40
|
+
PDC::V1::Release.page(2).page_size(30).contents!
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
benchmark "fetch with page number and size and query condition" do
|
|
44
|
+
PDC::V1::Release.page(2).page_size(30).where(active: true).contents!
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
main if __FILE__ == $PROGRAM_NAME
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require_relative '../lib/pdc'
|
|
2
|
+
require 'ap'
|
|
3
|
+
|
|
4
|
+
class PDC::V1::NonExistingResource < PDC::Base
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def main
|
|
8
|
+
PDC.configure do |config|
|
|
9
|
+
config.site = 'https://pdc.engineering.redhat.com/'
|
|
10
|
+
# don't even bother fetching the token, this has to
|
|
11
|
+
# fail anyway
|
|
12
|
+
config.requires_token = false
|
|
13
|
+
config.log_level = :debug
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
begin
|
|
17
|
+
resources = PDC::V1::NonExistingResource.all.to_a
|
|
18
|
+
ap resources
|
|
19
|
+
rescue PDC::ResourceNotFound => e
|
|
20
|
+
ap e
|
|
21
|
+
puts "Got resource not found as expected: #{e.response.status}".yellowish
|
|
22
|
+
ap e.message
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
main if __FILE__ == $0
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require './lib/pdc'
|
|
2
|
+
require 'ap'
|
|
3
|
+
|
|
4
|
+
def main
|
|
5
|
+
PDC.configure do |config|
|
|
6
|
+
config.site = 'https://pdc.engineering.redhat.com'
|
|
7
|
+
config.log_level = :debug
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
releases = PDC::V1::Release.all!.to_a
|
|
11
|
+
ap releases
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
main if __FILE__ == $0
|
data/lib/pdc/base.rb
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module PDC
|
|
2
|
+
class Base
|
|
3
|
+
extend ActiveModel::Naming
|
|
4
|
+
|
|
5
|
+
include PDC::Logging
|
|
6
|
+
include PDC::Resource::Identity
|
|
7
|
+
include PDC::Resource::Attributes
|
|
8
|
+
include PDC::Resource::Scopes
|
|
9
|
+
include PDC::Resource::RestApi
|
|
10
|
+
|
|
11
|
+
scope :page, ->(value) { where(:page => value) }
|
|
12
|
+
scope :page_size, ->(value) { where(:page_size => value) }
|
|
13
|
+
end
|
|
14
|
+
end
|
data/lib/pdc/config.rb
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
require 'faraday-http-cache'
|
|
2
|
+
require 'curb'
|
|
3
|
+
|
|
4
|
+
module PDC
|
|
5
|
+
# This class is the main access point for all PDC::Resource instances.
|
|
6
|
+
#
|
|
7
|
+
# The client must be initialized with an options hash containing
|
|
8
|
+
# configuration options. The available options are:
|
|
9
|
+
#
|
|
10
|
+
# :site => 'http://localhost:2990',
|
|
11
|
+
# :ssl_verify_mode => OpenSSL::SSL::VERIFY_PEER,
|
|
12
|
+
# :use_ssl => true,
|
|
13
|
+
# :username => nil,
|
|
14
|
+
# :password => nil,
|
|
15
|
+
# :auth_type => :oauth
|
|
16
|
+
# :proxy_address => nil
|
|
17
|
+
# :proxy_port => nil
|
|
18
|
+
#
|
|
19
|
+
# See the PDC::Base class methods for all of the available methods on these accessor
|
|
20
|
+
# objects.
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
Config = Struct.new(
|
|
24
|
+
:site,
|
|
25
|
+
:api_root,
|
|
26
|
+
|
|
27
|
+
:use_ssl,
|
|
28
|
+
:ssl_verify_mode,
|
|
29
|
+
|
|
30
|
+
:requires_token,
|
|
31
|
+
:token_obtain_path,
|
|
32
|
+
:token,
|
|
33
|
+
|
|
34
|
+
:log_level,
|
|
35
|
+
:enable_logging,
|
|
36
|
+
:logger,
|
|
37
|
+
|
|
38
|
+
:cache_store,
|
|
39
|
+
:disable_caching,
|
|
40
|
+
) do
|
|
41
|
+
def initialize
|
|
42
|
+
# site config
|
|
43
|
+
self.site = 'http://localhost:8000'
|
|
44
|
+
self.api_root = 'rest_api/'
|
|
45
|
+
self.use_ssl = true
|
|
46
|
+
self.ssl_verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
47
|
+
|
|
48
|
+
# token and authentication
|
|
49
|
+
self.requires_token = true
|
|
50
|
+
self.token_obtain_path = 'v1/auth/token/obtain/'
|
|
51
|
+
self.token = nil
|
|
52
|
+
|
|
53
|
+
# logger config
|
|
54
|
+
self.log_level = :warn
|
|
55
|
+
self.enable_logging = true
|
|
56
|
+
self.logger = PDC.logger
|
|
57
|
+
|
|
58
|
+
self.cache_store = nil
|
|
59
|
+
self.disable_caching = false
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def configure
|
|
64
|
+
@config = Config.new
|
|
65
|
+
begin
|
|
66
|
+
yield(@config) if block_given?
|
|
67
|
+
rescue NoMethodError => e
|
|
68
|
+
raise ConfigError, e
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
apply_config
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def config
|
|
75
|
+
@config ||= Config.new
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def config=(new_config)
|
|
79
|
+
@config = new_config
|
|
80
|
+
apply_config
|
|
81
|
+
@config
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def token_url
|
|
85
|
+
URI.join(api_url, config.token_obtain_path)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def api_url
|
|
89
|
+
URI.join(config.site, config.api_root)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def token
|
|
93
|
+
config.token || fetch_token
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def apply_config
|
|
99
|
+
reset_logger
|
|
100
|
+
reset_base_connection
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def reset_logger
|
|
104
|
+
PDC.logger = Logger.new(nil) unless config.enable_logging
|
|
105
|
+
logger.level = Logger.const_get(config.log_level.upcase)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# resets and returns the +Faraday+ +connection+ object
|
|
109
|
+
def reset_base_connection
|
|
110
|
+
headers = PDC::Request.default_headers
|
|
111
|
+
PDC::Base.connection = Faraday.new(url: api_url, headers: headers) do |c|
|
|
112
|
+
c.request :append_slash_to_path
|
|
113
|
+
c.request :authorization, 'Token', token if config.requires_token
|
|
114
|
+
|
|
115
|
+
c.response :logger, config.logger
|
|
116
|
+
c.response :pdc_paginator
|
|
117
|
+
c.response :pdc_json_parser
|
|
118
|
+
c.response :raise_error
|
|
119
|
+
c.response :pdc_raise_error
|
|
120
|
+
|
|
121
|
+
c.use FaradayMiddleware::FollowRedirects
|
|
122
|
+
|
|
123
|
+
unless config.disable_caching
|
|
124
|
+
c.use Faraday::HttpCache, store: cache_store,
|
|
125
|
+
logger: PDC.logger,
|
|
126
|
+
instrumenter: ActiveSupport::Notifications
|
|
127
|
+
end
|
|
128
|
+
c.adapter Faraday.default_adapter
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def cache_store
|
|
133
|
+
config.cache_store || ActiveSupport::Cache.lookup_store(:memory_store)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def fetch_token
|
|
137
|
+
curl = Curl::Easy.new(token_url.to_s) do |request|
|
|
138
|
+
request.headers['Accept'] = 'application/json'
|
|
139
|
+
request.http_auth_types = :gssnegotiate
|
|
140
|
+
|
|
141
|
+
# The curl man page (http://curl.haxx.se/docs/manpage.html)
|
|
142
|
+
# specifes setting a fake username when using Negotiate auth,
|
|
143
|
+
# and use ':' in their example.
|
|
144
|
+
request.username = ':'
|
|
145
|
+
end
|
|
146
|
+
curl.perform
|
|
147
|
+
if curl.response_code != 200
|
|
148
|
+
logger.info "Obtain token from #{token_url} failed: #{curl.body_str}"
|
|
149
|
+
error = { token_url: token_url, body: curl.body, code: curl.response_code }
|
|
150
|
+
raise PDC::TokenFetchFailed, error
|
|
151
|
+
end
|
|
152
|
+
result = JSON.parse(curl.body_str)
|
|
153
|
+
curl.close
|
|
154
|
+
result['token']
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
data/lib/pdc/errors.rb
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'forwardable'
|
|
2
|
+
|
|
3
|
+
module PDC
|
|
4
|
+
class JsonParseError < Error; end
|
|
5
|
+
|
|
6
|
+
class ResponseError < Error
|
|
7
|
+
extend Forwardable
|
|
8
|
+
|
|
9
|
+
def_instance_delegators :response, :status, :body
|
|
10
|
+
attr_reader :response
|
|
11
|
+
|
|
12
|
+
def initialize(args = {})
|
|
13
|
+
@response = args[:response]
|
|
14
|
+
@args = args
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class JsonError < ResponseError
|
|
19
|
+
def message
|
|
20
|
+
summary = detail || response.body
|
|
21
|
+
"Error: #{status} - #{summary}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
# returns details in json response if any, else nil
|
|
27
|
+
def detail
|
|
28
|
+
@detail ||= json[:detail]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# tries to parse response body as a json
|
|
32
|
+
def json
|
|
33
|
+
@json ||= begin
|
|
34
|
+
MultiJson.load(response.body, symbolize_keys: true)
|
|
35
|
+
rescue MultiJson::ParseError
|
|
36
|
+
{}
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
class ResourceNotFound < JsonError; end
|
|
42
|
+
class TokenFetchFailed < JsonError; end
|
|
43
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module PDC
|
|
2
|
+
class Request::AppendSlashToPath < Faraday::Middleware
|
|
3
|
+
include PDC::Logging
|
|
4
|
+
|
|
5
|
+
Faraday::Request.register_middleware :append_slash_to_path => self
|
|
6
|
+
|
|
7
|
+
def call(env)
|
|
8
|
+
logger.debug "\n..... append slash ..........................................".green
|
|
9
|
+
logger.debug self.class
|
|
10
|
+
logger.debug env.url
|
|
11
|
+
|
|
12
|
+
path = env.url.path
|
|
13
|
+
env.url.path = path + '/' unless path.ends_with?('/')
|
|
14
|
+
|
|
15
|
+
logger.debug "... after adding / #{env.url.path}: #{env.url.ai}"
|
|
16
|
+
@app.call(env)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module PDC::Response
|
|
2
|
+
class Paginator < Faraday::Response::Middleware
|
|
3
|
+
include PDC::Logging
|
|
4
|
+
|
|
5
|
+
Faraday::Response.register_middleware :pdc_paginator => self
|
|
6
|
+
|
|
7
|
+
def parse(json)
|
|
8
|
+
logger.debug "\n.....paginate json .....................................".redish
|
|
9
|
+
|
|
10
|
+
metadata = json[:metadata]
|
|
11
|
+
logger.debug metadata
|
|
12
|
+
return json unless paginated?(metadata)
|
|
13
|
+
|
|
14
|
+
metadata[PDC::Resource::PAGINATION] = {
|
|
15
|
+
resource_count: metadata.delete(:count),
|
|
16
|
+
|
|
17
|
+
# TODO: decide if this is okay to discard the
|
|
18
|
+
# schema://host:port/ of the next and previous
|
|
19
|
+
|
|
20
|
+
next_page: request_uri(metadata.delete(:next)),
|
|
21
|
+
previous_page: request_uri(metadata.delete(:previous)),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
logger.debug '... after parsing pagination data:'.green
|
|
25
|
+
logger.debug metadata
|
|
26
|
+
json
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def request_uri(uri)
|
|
32
|
+
return unless uri
|
|
33
|
+
URI(uri).request_uri
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def paginated?(metadata)
|
|
37
|
+
metadata[:count].is_a?(Numeric) &&
|
|
38
|
+
metadata.key?(:next) &&
|
|
39
|
+
metadata.key?(:previous)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'multi_json'
|
|
2
|
+
|
|
3
|
+
module PDC::Response
|
|
4
|
+
# Converts body into JSON data, metadata and errors
|
|
5
|
+
class Parser < Faraday::Response::Middleware
|
|
6
|
+
include PDC::Logging
|
|
7
|
+
|
|
8
|
+
Faraday::Response.register_middleware :pdc_json_parser => self
|
|
9
|
+
|
|
10
|
+
def parse(body)
|
|
11
|
+
logger.debug "\n.....parse to json .....................................".yellow
|
|
12
|
+
logger.debug self.class
|
|
13
|
+
|
|
14
|
+
logger.debug "... parsing #{body.ai.truncate(55)}"
|
|
15
|
+
begin
|
|
16
|
+
json = MultiJson.load(body, symbolize_keys: true)
|
|
17
|
+
rescue MultiJson::ParseError => e
|
|
18
|
+
raise PDC::JsonParseError, e
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
res = {
|
|
22
|
+
data: extract_data(json), # Always an Array
|
|
23
|
+
errors: extract_errors(json), #
|
|
24
|
+
metadata: extract_metadata(json) # a hash
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def extract_data(json)
|
|
31
|
+
return [] if error?(json)
|
|
32
|
+
return json[:results] if has_metadata?(json)
|
|
33
|
+
Array.wrap(json)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def extract_errors(json)
|
|
37
|
+
error?(json) ? json[:details] : []
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def extract_metadata(json)
|
|
41
|
+
return json.except(:details, :results) if has_metadata?(json)
|
|
42
|
+
data_only?(json) ? { count: json.length, next: nil, previous: nil } : {}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def has_metadata?(json)
|
|
46
|
+
return false if data_only?(json) || error?(json)
|
|
47
|
+
|
|
48
|
+
json[:results].is_a?(Array) &&
|
|
49
|
+
json[:count].is_a?(Numeric) &&
|
|
50
|
+
json.key?(:next) &&
|
|
51
|
+
json.key?(:previous)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def error?(json)
|
|
55
|
+
json.is_a?(Hash) && json.keys == [:detail]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def data_only?(json)
|
|
59
|
+
json.is_a? Array
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module PDC
|
|
2
|
+
class Response::RaiseError < Faraday::Response::Middleware
|
|
3
|
+
Faraday::Response.register_middleware pdc_raise_error: self
|
|
4
|
+
|
|
5
|
+
def on_complete(env)
|
|
6
|
+
raise PDC::ResourceNotFound, response_values(env) if env[:status] == 404
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def response_values(env)
|
|
10
|
+
{ response: env.response, request: env.request }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module PDC::Http
|
|
2
|
+
# encapsulates +http+ response
|
|
3
|
+
class Result
|
|
4
|
+
attr_reader :url, :body, :status
|
|
5
|
+
|
|
6
|
+
def initialize(response)
|
|
7
|
+
@body = HashWithIndifferentAccess.new(response.body)
|
|
8
|
+
@status = response.status
|
|
9
|
+
@url = response.env.url
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def data
|
|
13
|
+
body[:data]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def metadata
|
|
17
|
+
body[:metadata] || {}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def pagination
|
|
21
|
+
metadata[PDC::Resource::PAGINATION] || {}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def errors
|
|
25
|
+
body[:errors] || []
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|