crapi 0.1.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/crapi/proxy.rb CHANGED
@@ -1,120 +1,120 @@
1
1
  require 'active_support/all'
2
2
 
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
- ##
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
- ## 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
- ##
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
- ## @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
- ##
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
- ## 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
- ##
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
- ## CRUD methods ...
61
+ # CRUD methods ...
62
62
 
63
- ## CRUD method: DELETE
64
- ##
65
- ##
66
- ## @return [Object]
67
- ##
68
- ## @see Crapi::Client#delete Crapi::Client#delete
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
- ## CRUD method: GET
76
- ##
77
- ##
78
- ## @return [Object]
79
- ##
80
- ## @see Crapi::Client#get Crapi::Client#get
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
- ## CRUD method: PATCH
88
- ##
89
- ##
90
- ## @return [Object]
91
- ##
92
- ## @see Crapi::Client#patch Crapi::Client#patch
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
- heades: @default_headers.merge(headers), query: query, payload: payload)
96
+ headers: @default_headers.merge(headers), query: query, payload: payload)
97
97
  end
98
98
 
99
- ## CRUD method: POST
100
- ##
101
- ##
102
- ## @return [Object]
103
- ##
104
- ## @see Crapi::Client#post Crapi::Client#post
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
- ## CRUD method: PUT
112
- ##
113
- ##
114
- ## @return [Object]
115
- ##
116
- ## @see Crapi::Client#put Crapi::Client#put
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
- ## 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 = '0.1.3'.freeze
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
@@ -2,5 +2,7 @@ require 'crapi/client'
2
2
  require 'crapi/proxy'
3
3
  require 'crapi/version'
4
4
 
5
- module Crapi
5
+ # Parent module for the various CrAPI classes.
6
+ #
7
+ module CrAPI
6
8
  end
@@ -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
@@ -0,0 +1,5 @@
1
+ RSpec.describe CrAPI do
2
+ it 'has a version number' do
3
+ expect(CrAPI::VERSION).not_to be nil
4
+ end
5
+ end
@@ -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