pdc 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|