crapi 0.1.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|