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.
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