crapi 0.1.3 → 1.0.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 +4 -4
- data/.circleci/config.yml +63 -0
- data/.rubocop.yml +53 -70
- data/.ruby-version +1 -1
- data/Gemfile.lock +66 -42
- data/README.md +29 -27
- data/crapi.gemspec +15 -15
- data/docs/{Crapi → CrAPI}/ArgumentError.html +15 -16
- data/docs/{Crapi → CrAPI}/BadHttpResponseError.html +15 -16
- data/docs/{Crapi → CrAPI}/Client.html +144 -174
- data/docs/{Crapi → CrAPI}/Error.html +14 -15
- data/docs/{Crapi → CrAPI}/Proxy.html +65 -89
- data/docs/CrAPI.html +157 -0
- data/docs/Net/HTTP.html +18 -23
- data/docs/_index.html +19 -19
- data/docs/class_list.html +3 -3
- data/docs/css/style.css +7 -9
- data/docs/file.README.html +68 -87
- data/docs/file_list.html +2 -2
- data/docs/frames.html +2 -2
- data/docs/index.html +68 -87
- data/docs/js/app.js +69 -3
- data/docs/method_list.html +34 -34
- data/docs/top-level-namespace.html +8 -8
- data/lib/crapi/client.rb +224 -228
- data/lib/crapi/errors.rb +7 -7
- data/lib/crapi/proxy.rb +82 -82
- data/lib/crapi/version.rb +10 -10
- data/lib/crapi.rb +3 -1
- data/spec/crapi_client_spec.rb +121 -0
- data/spec/crapi_errors_spec.rb +17 -0
- data/spec/crapi_proxy_spec.rb +121 -0
- data/spec/crapi_spec.rb +5 -0
- data/spec/spec_helper.rb +19 -0
- metadata +66 -52
- data/.travis.yml +0 -5
- data/docs/Crapi.html +0 -153
data/lib/crapi/proxy.rb
CHANGED
@@ -1,120 +1,120 @@
|
|
1
1
|
require 'active_support/all'
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
3
|
+
module CrAPI
|
4
|
+
# Proxies simple CRUD methods ({#delete} / {#get} / {#patch} / {#post} / {#put}) for a
|
5
|
+
# {CrAPI::Client CrAPI::Client} or another {CrAPI::Proxy CrAPI::Proxy}. Like
|
6
|
+
# {CrAPI::Client CrAPI::Client}, it also provides a proxy generator.
|
7
|
+
#
|
8
|
+
# A {CrAPI::Proxy CrAPI::Proxy} has its own set of default headers and has a segment that is
|
9
|
+
# prepended to its CRUD methods' *path* before being passed to the parent
|
10
|
+
# {CrAPI::Client CrAPI::Client}/{CrAPI::Proxy CrAPI::Proxy}. This makes the proxxy functionally
|
11
|
+
# equivalent to a new {CrAPI::Client CrAPI::Client} with a new base path (the parent's base path
|
12
|
+
# plus the proxy's segment) and more default headers, but without the need for a separate
|
13
|
+
# connection to the target system.
|
14
|
+
#
|
15
15
|
class Proxy
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
# A Hash containing headers to send with every request (unless overriden elsewhere). In case of
|
17
|
+
# conflicts, headers set as default for a {CrAPI::Proxy CrAPI::Proxy} override those set by the
|
18
|
+
# parent. As in a {CrAPI::Client CrAPI::Client}, headers set by the user via the CRUD methods'
|
19
|
+
# *headers* still override any conflicting default header.
|
20
|
+
#
|
21
|
+
#
|
22
|
+
# @see CrAPI::Client#default_headers CrAPI::Client#default_headers
|
23
|
+
#
|
24
24
|
attr_accessor :default_headers
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
26
|
+
# @param add [String]
|
27
|
+
# The new base path. This path (added to the parent's base path) will be used as the base
|
28
|
+
# path for the {CrAPI::Proxy CrAPI::Proxy}'s CRUD methods.
|
29
|
+
#
|
30
|
+
# @param to [CrAPI::Client, CrAPI::Proxy]
|
31
|
+
# The parent {CrAPI::Client CrAPI::Client} or {CrAPI::Proxy CrAPI::Proxy} that we'll be
|
32
|
+
# delegating CRUD calls to after proprocessing of headers and paths.
|
33
|
+
#
|
34
|
+
# @param headers [Hash]
|
35
|
+
# The default headers to send with every request (unless overriden elsewhere).
|
36
|
+
#
|
37
37
|
def initialize(add:, to:, headers: nil)
|
38
38
|
@parent = to
|
39
39
|
@segment = add
|
40
40
|
@default_headers = (headers || {}).with_indifferent_access
|
41
41
|
end
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
43
|
+
# Returns a new {CrAPI::Proxy CrAPI::Proxy} for this proxy.
|
44
|
+
#
|
45
|
+
#
|
46
|
+
# @param segment [String]
|
47
|
+
# The segment to add to this proxy's path.
|
48
|
+
#
|
49
|
+
# @param headers [Hash]
|
50
|
+
# The default headers for the new proxy.
|
51
|
+
#
|
52
|
+
#
|
53
|
+
# @return [CrAPI::Proxy]
|
54
|
+
#
|
55
|
+
# @see CrAPI::Client#new_proxy CrAPI::Client#new_proxy
|
56
|
+
#
|
57
57
|
def new_proxy(segment = '/', headers: nil)
|
58
58
|
Proxy.new(add: segment, to: self, headers: headers)
|
59
59
|
end
|
60
60
|
|
61
|
-
|
61
|
+
# CRUD methods ...
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
63
|
+
# CRUD method: DELETE
|
64
|
+
#
|
65
|
+
#
|
66
|
+
# @return [Object]
|
67
|
+
#
|
68
|
+
# @see CrAPI::Client#delete CrAPI::Client#delete
|
69
|
+
#
|
70
70
|
def delete(path, headers: {}, query: {})
|
71
71
|
@parent.delete("/#{@segment}/#{path}".gsub(%r{/+}, '/'),
|
72
72
|
headers: @default_headers.merge(headers), query: query)
|
73
73
|
end
|
74
74
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
75
|
+
# CRUD method: GET
|
76
|
+
#
|
77
|
+
#
|
78
|
+
# @return [Object]
|
79
|
+
#
|
80
|
+
# @see CrAPI::Client#get CrAPI::Client#get
|
81
|
+
#
|
82
82
|
def get(path, headers: {}, query: {})
|
83
83
|
@parent.get("/#{@segment}/#{path}".gsub(%r{/+}, '/'),
|
84
84
|
headers: @default_headers.merge(headers), query: query)
|
85
85
|
end
|
86
86
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
87
|
+
# CRUD method: PATCH
|
88
|
+
#
|
89
|
+
#
|
90
|
+
# @return [Object]
|
91
|
+
#
|
92
|
+
# @see CrAPI::Client#patch CrAPI::Client#patch
|
93
|
+
#
|
94
94
|
def patch(path, headers: {}, query: {}, payload: {})
|
95
95
|
@parent.patch("/#{@segment}/#{path}".gsub(%r{/+}, '/'),
|
96
|
-
|
96
|
+
headers: @default_headers.merge(headers), query: query, payload: payload)
|
97
97
|
end
|
98
98
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
99
|
+
# CRUD method: POST
|
100
|
+
#
|
101
|
+
#
|
102
|
+
# @return [Object]
|
103
|
+
#
|
104
|
+
# @see CrAPI::Client#post CrAPI::Client#post
|
105
|
+
#
|
106
106
|
def post(path, headers: {}, query: {}, payload: {})
|
107
107
|
@parent.post("/#{@segment}/#{path}".gsub(%r{/+}, '/'),
|
108
108
|
headers: @default_headers.merge(headers), query: query, payload: payload)
|
109
109
|
end
|
110
110
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
111
|
+
# CRUD method: PUT
|
112
|
+
#
|
113
|
+
#
|
114
|
+
# @return [Object]
|
115
|
+
#
|
116
|
+
# @see CrAPI::Client#put CrAPI::Client#put
|
117
|
+
#
|
118
118
|
def put(path, headers: {}, query: {}, payload: {})
|
119
119
|
@parent.put("/#{@segment}/#{path}".gsub(%r{/+}, '/'),
|
120
120
|
headers: @default_headers.merge(headers), query: query, payload: payload)
|
data/lib/crapi/version.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
module
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
VERSION = '0.
|
1
|
+
# The CrAPI module houses the {CrAPI::Client CrAPI::Client} and {CrAPI::Proxy CrAPI::Proxy} classes
|
2
|
+
# in this gem.
|
3
|
+
#
|
4
|
+
module CrAPI
|
5
|
+
# The canonical **crapi** gem version.
|
6
|
+
#
|
7
|
+
# This should only ever be updated *immediately* before a release; the commit that updates this
|
8
|
+
# value should be pushed **by** the `rake release` process.
|
9
|
+
#
|
10
|
+
VERSION = '1.0.0'.freeze
|
11
11
|
end
|
data/lib/crapi.rb
CHANGED
@@ -0,0 +1,121 @@
|
|
1
|
+
RSpec.describe CrAPI::Client do
|
2
|
+
subject do
|
3
|
+
client = CrAPI::Client.new API_DOMAIN
|
4
|
+
client.default_headers.merge! client: '1'
|
5
|
+
|
6
|
+
client
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:payload_hash) { { string_value: 'abc', numeric_value: 123, a_list: %w[one two three] } }
|
10
|
+
let(:payload_json) { JSON.generate payload_hash }
|
11
|
+
|
12
|
+
let(:empty_response_spec) { { status: 204, body: nil } }
|
13
|
+
let(:payload_response_spec) { { status: 200, headers: { 'content-type': 'application/json' }, body: payload_json } }
|
14
|
+
|
15
|
+
def stub(method, uri, headers: {})
|
16
|
+
stub_request(method, "#{API_DOMAIN}/#{uri.delete_prefix '/'}").with(headers: full_request_headers(headers))
|
17
|
+
end
|
18
|
+
|
19
|
+
def full_request_headers(custom_values = {})
|
20
|
+
subject.default_headers.merge custom_values
|
21
|
+
end
|
22
|
+
|
23
|
+
def crud_header_test(method, with_header:)
|
24
|
+
uri = '/resource/1'
|
25
|
+
|
26
|
+
if with_header
|
27
|
+
unique_header = { 'X-Test' => SecureRandom.hex }
|
28
|
+
stub(method, uri, headers: unique_header).to_return(status: 204)
|
29
|
+
expect { subject.send(method, uri, headers: unique_header) }.not_to raise_error
|
30
|
+
else
|
31
|
+
stub(method, uri).to_return(status: 204)
|
32
|
+
expect { subject.send(method, uri) }.not_to raise_error
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def crud_parsing_test(method, with_body:)
|
37
|
+
uri = '/resource/1'
|
38
|
+
|
39
|
+
if with_body
|
40
|
+
stub(method, uri).to_return(payload_response_spec)
|
41
|
+
expect(subject.send(method, uri)).to eq(payload_hash)
|
42
|
+
else
|
43
|
+
stub(method, uri).to_return(empty_response_spec)
|
44
|
+
expect(subject.send(method, uri)).to eq(nil)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# ---
|
49
|
+
|
50
|
+
describe '#new_proxy' do
|
51
|
+
it('is defined') { is_expected.to respond_to :new_proxy }
|
52
|
+
it('returns a CrAPI::Proxy') { expect(subject.new_proxy).to be_a(CrAPI::Proxy) }
|
53
|
+
end
|
54
|
+
|
55
|
+
# ---
|
56
|
+
|
57
|
+
describe '#delete' do
|
58
|
+
let(:method) { :delete }
|
59
|
+
|
60
|
+
describe 'sends correctly' do
|
61
|
+
it('w/ headers') { crud_header_test(method, with_header: true) }
|
62
|
+
it('w/o headers') { crud_header_test(method, with_header: false) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#get' do
|
67
|
+
let(:method) { :get }
|
68
|
+
|
69
|
+
describe 'sends correctly' do
|
70
|
+
it('w/ headers') { crud_header_test(method, with_header: true) }
|
71
|
+
it('w/o headers') { crud_header_test(method, with_header: false) }
|
72
|
+
end
|
73
|
+
|
74
|
+
describe 'parses responses' do
|
75
|
+
it('w/ response body') { crud_parsing_test(method, with_body: true) }
|
76
|
+
it('w/o response body') { crud_parsing_test(method, with_body: false) }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#patch' do
|
81
|
+
let(:method) { :patch }
|
82
|
+
|
83
|
+
describe 'sends correctly' do
|
84
|
+
it('w/ headers') { crud_header_test(method, with_header: true) }
|
85
|
+
it('w/o headers') { crud_header_test(method, with_header: false) }
|
86
|
+
end
|
87
|
+
|
88
|
+
describe 'parses responses' do
|
89
|
+
it('w/ response body') { crud_parsing_test(method, with_body: true) }
|
90
|
+
it('w/o response body') { crud_parsing_test(method, with_body: false) }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#post' do
|
95
|
+
let(:method) { :post }
|
96
|
+
|
97
|
+
describe 'sends correctly' do
|
98
|
+
it('w/ headers') { crud_header_test(method, with_header: true) }
|
99
|
+
it('w/o headers') { crud_header_test(method, with_header: false) }
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'parses responses' do
|
103
|
+
it('w/ response body') { crud_parsing_test(method, with_body: true) }
|
104
|
+
it('w/o response body') { crud_parsing_test(method, with_body: false) }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe '#put' do
|
109
|
+
let(:method) { :put }
|
110
|
+
|
111
|
+
describe 'sends correctly' do
|
112
|
+
it('w/ headers') { crud_header_test(method, with_header: true) }
|
113
|
+
it('w/o headers') { crud_header_test(method, with_header: false) }
|
114
|
+
end
|
115
|
+
|
116
|
+
describe 'parses responses' do
|
117
|
+
it('w/ response body') { crud_parsing_test(method, with_body: true) }
|
118
|
+
it('w/o response body') { crud_parsing_test(method, with_body: false) }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
RSpec.describe CrAPI::Error do
|
2
|
+
it 'is defined' do
|
3
|
+
expect(defined? CrAPI::Error).not_to be(nil)
|
4
|
+
end
|
5
|
+
|
6
|
+
describe 'is subclassed as ...' do
|
7
|
+
it 'CrAPI::ArgumentError' do
|
8
|
+
expect(defined? CrAPI::ArgumentError).not_to be(nil)
|
9
|
+
expect(CrAPI::ArgumentError.superclass).to be(CrAPI::Error)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'CrAPI::BadHttpResponseError' do
|
13
|
+
expect(defined? CrAPI::BadHttpResponseError).not_to be(nil)
|
14
|
+
expect(CrAPI::BadHttpResponseError.superclass).to be(CrAPI::Error)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
RSpec.describe CrAPI::Proxy do
|
2
|
+
subject do
|
3
|
+
client = CrAPI::Client.new API_DOMAIN
|
4
|
+
client.default_headers.merge! client: '1'
|
5
|
+
|
6
|
+
client.new_proxy '/proxy_path', headers: { client: '0', proxy: '1' }
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:payload_hash) { { string_value: 'abc', numeric_value: 123, a_list: %w[one two three] } }
|
10
|
+
let(:payload_json) { JSON.generate payload_hash }
|
11
|
+
|
12
|
+
let(:empty_response_spec) { { status: 204, body: nil } }
|
13
|
+
let(:payload_response_spec) { { status: 200, headers: { 'content-type': 'application/json' }, body: payload_json } }
|
14
|
+
|
15
|
+
def stub(method, uri, headers: {})
|
16
|
+
stub_request(method, "#{API_DOMAIN}/proxy_path/#{uri.delete_prefix '/'}").with(headers: full_request_headers(headers))
|
17
|
+
end
|
18
|
+
|
19
|
+
def full_request_headers(custom_values = {})
|
20
|
+
subject.default_headers.merge custom_values
|
21
|
+
end
|
22
|
+
|
23
|
+
def crud_header_test(method, with_header:)
|
24
|
+
uri = '/resource/1'
|
25
|
+
|
26
|
+
if with_header
|
27
|
+
unique_header = { 'X-Test' => SecureRandom.hex }
|
28
|
+
stub(method, uri, headers: unique_header).to_return(status: 204)
|
29
|
+
expect { subject.send(method, uri, headers: unique_header) }.not_to raise_error
|
30
|
+
else
|
31
|
+
stub(method, uri).to_return(status: 204)
|
32
|
+
expect { subject.send(method, uri) }.not_to raise_error
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def crud_parsing_test(method, with_body:)
|
37
|
+
uri = '/resource/1'
|
38
|
+
|
39
|
+
if with_body
|
40
|
+
stub(method, uri).to_return(payload_response_spec)
|
41
|
+
expect(subject.send(method, uri)).to eq(payload_hash)
|
42
|
+
else
|
43
|
+
stub(method, uri).to_return(empty_response_spec)
|
44
|
+
expect(subject.send(method, uri)).to eq(nil)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# ---
|
49
|
+
|
50
|
+
describe '#new_proxy' do
|
51
|
+
it('is defined') { is_expected.to respond_to :new_proxy }
|
52
|
+
it('returns a CrAPI::Proxy') { expect(subject.new_proxy).to be_a(CrAPI::Proxy) }
|
53
|
+
end
|
54
|
+
|
55
|
+
# ---
|
56
|
+
|
57
|
+
describe '#delete' do
|
58
|
+
let(:method) { :delete }
|
59
|
+
|
60
|
+
describe 'sends correctly' do
|
61
|
+
it('w/ headers') { crud_header_test(method, with_header: true) }
|
62
|
+
it('w/o headers') { crud_header_test(method, with_header: false) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#get' do
|
67
|
+
let(:method) { :get }
|
68
|
+
|
69
|
+
describe 'sends correctly' do
|
70
|
+
it('w/ headers') { crud_header_test(method, with_header: true) }
|
71
|
+
it('w/o headers') { crud_header_test(method, with_header: false) }
|
72
|
+
end
|
73
|
+
|
74
|
+
describe 'parses responses' do
|
75
|
+
it('w/ response body') { crud_parsing_test(method, with_body: true) }
|
76
|
+
it('w/o response body') { crud_parsing_test(method, with_body: false) }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#patch' do
|
81
|
+
let(:method) { :patch }
|
82
|
+
|
83
|
+
describe 'sends correctly' do
|
84
|
+
it('w/ headers') { crud_header_test(method, with_header: true) }
|
85
|
+
it('w/o headers') { crud_header_test(method, with_header: false) }
|
86
|
+
end
|
87
|
+
|
88
|
+
describe 'parses responses' do
|
89
|
+
it('w/ response body') { crud_parsing_test(method, with_body: true) }
|
90
|
+
it('w/o response body') { crud_parsing_test(method, with_body: false) }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#post' do
|
95
|
+
let(:method) { :post }
|
96
|
+
|
97
|
+
describe 'sends correctly' do
|
98
|
+
it('w/ headers') { crud_header_test(method, with_header: true) }
|
99
|
+
it('w/o headers') { crud_header_test(method, with_header: false) }
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'parses responses' do
|
103
|
+
it('w/ response body') { crud_parsing_test(method, with_body: true) }
|
104
|
+
it('w/o response body') { crud_parsing_test(method, with_body: false) }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe '#put' do
|
109
|
+
let(:method) { :put }
|
110
|
+
|
111
|
+
describe 'sends correctly' do
|
112
|
+
it('w/ headers') { crud_header_test(method, with_header: true) }
|
113
|
+
it('w/o headers') { crud_header_test(method, with_header: false) }
|
114
|
+
end
|
115
|
+
|
116
|
+
describe 'parses responses' do
|
117
|
+
it('w/ response body') { crud_parsing_test(method, with_body: true) }
|
118
|
+
it('w/o response body') { crud_parsing_test(method, with_body: false) }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
data/spec/crapi_spec.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'crapi'
|
3
|
+
require 'json'
|
4
|
+
require 'securerandom'
|
5
|
+
require 'webmock/rspec'
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
# Enable flags like --only-failures and --next-failure
|
9
|
+
config.example_status_persistence_file_path = '.rspec_status'
|
10
|
+
|
11
|
+
# Disable RSpec exposing methods globally on `Module` and `main`
|
12
|
+
config.disable_monkey_patching!
|
13
|
+
|
14
|
+
config.expect_with :rspec do |c|
|
15
|
+
c.syntax = :expect
|
16
|
+
end
|
17
|
+
|
18
|
+
API_DOMAIN = 'https://fake-domain.lol'.freeze
|
19
|
+
end
|