callapi 0.8

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.
@@ -0,0 +1,72 @@
1
+ class Callapi::Routes::Metadata
2
+ extend Memoist
3
+
4
+ def initialize(http_method_namespace, *args)
5
+ @http_method_namespace = http_method_namespace
6
+ @call_name, @call_options = args.shift, *args
7
+
8
+ call_name_with_all_namespaces.size.times do |i|
9
+ metadata = create_metadata(i)
10
+ Callapi::Routes.send(:save_class, metadata) if metadata
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def create_metadata(i)
17
+ new_class_name = call_name_with_all_namespaces[i + 1]
18
+
19
+ return unless new_class_name
20
+
21
+ is_call_class = new_class_name.eql? call_name_with_all_namespaces.last
22
+
23
+ OpenStruct.new.tap do |data|
24
+ data.class_name = full_class_name(i)
25
+ data.call_class = is_call_class
26
+ data.http_method_namespace = @http_method_namespace
27
+ if is_call_class
28
+ data.class_options = @call_options
29
+ data.call_name_with_namespaces = call_name_with_namespaces
30
+ end
31
+ end
32
+ end
33
+
34
+ def full_class_name(i)
35
+ call_name_with_all_namespaces[0..i + 1].join('::')
36
+ end
37
+
38
+ def call_name_with_namespaces
39
+ namespaces.push(@call_name).map do |class_name|
40
+ if class_name_with_param_key?(class_name)
41
+ class_name_to_class_name_with_param_key(class_name)
42
+ else
43
+ class_name
44
+ end.camelize
45
+ end
46
+ end
47
+ memoize :call_name_with_namespaces
48
+
49
+ def call_name_with_all_namespaces
50
+ [@http_method_namespace.to_s] + call_name_with_namespaces
51
+ end
52
+ memoize :call_name_with_all_namespaces
53
+
54
+ def namespaces
55
+ Callapi::Routes.send(:namespaces).dup
56
+ end
57
+ memoize :namespaces
58
+
59
+ def class_name_with_param_key?(class_name)
60
+ class_name[0] == ':' || class_name.match('/:')
61
+ end
62
+
63
+ def class_name_to_class_name_with_param_key(class_name)
64
+ class_name.scan(/(:\w+)\/?/).map(&:first).each do |pattern|
65
+ replacement = pattern.dup
66
+ replacement[0] = ''
67
+ replacement << '_param'
68
+ class_name.sub!(pattern, replacement)
69
+ end
70
+ class_name
71
+ end
72
+ end
@@ -0,0 +1,3 @@
1
+ module Callapi
2
+ VERSION = '0.8'
3
+ end
@@ -0,0 +1,25 @@
1
+ class DeepStruct < OpenStruct
2
+ def initialize(hash = nil)
3
+ @table = {}
4
+ @hash_table = {}
5
+
6
+ if hash
7
+ hash.each do |key, value|
8
+ @table[key.to_sym] = process_value(value)
9
+ @hash_table[key.to_sym] = value
10
+
11
+ new_ostruct_member(key)
12
+ end
13
+ end
14
+ end
15
+
16
+ def process_value(value)
17
+ if value.is_a?(Hash)
18
+ self.class.new(value)
19
+ elsif value.is_a?(Array)
20
+ value.map { |element| process_value(element) }
21
+ else
22
+ value
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ require 'webmock/rspec'
2
+
3
+ require_relative '../lib/callapi'
4
+
5
+ RSpec.configure do |config|
6
+ config.before(:suite) do
7
+ Get = Module.new
8
+ Get::Users = Class.new(Callapi::Call::Base)
9
+
10
+ Callapi::Config.configure do |config|
11
+ config.log_level = :off
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe Callapi::Call::Base do
4
+ let(:config) { Callapi::Config }
5
+ let(:call) { described_class.new }
6
+
7
+ context '#initialize' do
8
+ it 'should take params as an argument' do
9
+ expect(described_class.new(some_param: :some_value).params).to eql(some_param: :some_value)
10
+ end
11
+ end
12
+
13
+ context '#strategy' do
14
+ before { allow(config).to receive(:request_strategy).and_return(Callapi::Call::Request::Api) }
15
+
16
+ it 'should be taken from config' do
17
+ expect(subject.strategy).to eql(Callapi::Call::Request::Api)
18
+ end
19
+ end
20
+
21
+ context '.strategy' do
22
+ before { described_class.strategy = Callapi::Call::Request::Mock }
23
+
24
+ it 'should overwrite default strategy for each instance' do
25
+ expect(subject.strategy).to eql(Callapi::Call::Request::Mock)
26
+ end
27
+ end
28
+
29
+ context '#response_parser' do
30
+ subject { call.response_parser }
31
+
32
+ before { call.with_response_parser(Callapi::Call::Parser::Json::AsObject) }
33
+
34
+ it 'should be accessible' do
35
+ expect(subject).to eql(Callapi::Call::Parser::Json::AsObject)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Callapi::Call::Request::Api do
4
+ context '#host' do
5
+ subject { described_class.new(nil).host }
6
+ let(:config) { Callapi::Config }
7
+
8
+ context 'when API host is not set' do
9
+ it 'should raise ApiHostNotSet error' do
10
+ expect { subject }.to raise_error { Callapi::ApiHostNotSetError }
11
+ end
12
+ end
13
+
14
+ context 'when API host is set' do
15
+ before { allow(config).to receive(:api_host).and_return('http://api.org') }
16
+
17
+ it 'should take API host from config' do
18
+ expect( subject ).to eql 'http://api.org'
19
+ end
20
+ end
21
+ end
22
+
23
+ context '#response' do
24
+ before do
25
+ config = Callapi::Config
26
+ allow(config).to receive(:request_strategy).and_return(described_class)
27
+ allow(config).to receive(:api_host).and_return('http://api.org')
28
+ stub_request(:get, 'http://api.org/users').to_return(status: 200, body: '')
29
+
30
+ call_class = Get::Users
31
+ @call = call_class.new
32
+ end
33
+
34
+ subject { described_class.new(@call).response }
35
+
36
+ it 'should get response from server' do
37
+ expect( subject.body ).to eql(nil)
38
+ expect( subject.code ).to eql('200')
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe Callapi::Call::Request::Mock do
4
+ context '#response' do
5
+ before do
6
+ call_class = Get::Users
7
+ @call = call_class.new
8
+ end
9
+
10
+ subject { described_class.new(@call).response }
11
+
12
+ context 'when there is no file with mocked response' do
13
+ it 'should raise CouldNotFoundRequestMockFile error' do
14
+ expect{ subject }.to raise_error { Callapi::CouldNotFoundMockRequestFileError }
15
+ end
16
+ end
17
+
18
+ context '#body' do
19
+ subject { described_class.new(@call).response.body }
20
+
21
+ before do
22
+ allow(File).to receive(:read).and_return('body from file')
23
+ end
24
+
25
+ it 'should be taken from file' do
26
+ expect(subject).to eql('body from file')
27
+ end
28
+ end
29
+
30
+ context '#filepath' do
31
+ subject { described_class.new(@call).send(:file_path) }
32
+
33
+ it 'should be created based on call HTTP method, request path and response format ' do
34
+ expect(subject).to eql 'mocked_calls/get/users.json'
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe Callapi::Call::RequestMetadata do
4
+ before(:all) do
5
+ Get::Users::Details = Class.new(Callapi::Call::Base)
6
+ Get::Users::IdParam = Class.new(Callapi::Call::Base)
7
+ Get::Users::IdParam::Posts = Class.new(Callapi::Call::Base)
8
+ Get::Users::IdParam::Posts::PostIdParam = Class.new(Callapi::Call::Base)
9
+ ClassWithoutHttpNamespace = Class.new(Callapi::Call::Base)
10
+ end
11
+
12
+ let(:metadata) { described_class.new(@call_class.new) }
13
+
14
+ context '#request_method' do
15
+ subject { metadata.request_method }
16
+
17
+ context 'when call class has namespace equal to some HTTP method' do
18
+ let(:metadata) { described_class.new(Get::Users.new) }
19
+
20
+ it 'should take HTTP method from this namespace' do
21
+ expect(subject).to eql(:get)
22
+ end
23
+ end
24
+
25
+ context 'when call class does not have namespace equal to some HTTP method' do
26
+ let(:metadata) { described_class.new(ClassWithoutHttpNamespace.new) }
27
+
28
+ it 'should raise UnknownHttpMethod error' do
29
+ expect{ subject }.to raise_error { Callapi::UnknownHttpMethodError }
30
+ end
31
+ end
32
+ end
33
+
34
+ context '#request_path' do
35
+ subject { metadata.request_path }
36
+ let(:metadata) { described_class.new(Get::Users::Details.new) }
37
+
38
+ it 'should be equal to underscored class name with its namespaces' do
39
+ expect(subject).to eql('/users/details')
40
+ end
41
+
42
+ context 'when class name contain "Param" string' do
43
+ let(:metadata) { described_class.new(Get::Users::IdParam::Posts::PostIdParam.new(id: 123, post_id: 456)) }
44
+
45
+ it 'should use this param value to create path' do
46
+ expect(subject).to eql('/users/123/posts/456')
47
+ end
48
+
49
+ context 'but required param is not provided' do
50
+ let(:metadata) { described_class.new(Get::Users::IdParam::Posts::PostIdParam.new(post_id: 456)) }
51
+
52
+ it 'should raise MissingParam error' do
53
+ expect{ subject }.to raise_error{Callapi::MissingParamError}
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe Callapi::Call::Parser::Json::AsObject do
4
+ context '#data' do
5
+ before do
6
+ config = Callapi::Config
7
+ allow(config).to receive(:api_host).and_return('http://api.org')
8
+ allow(config).to receive(:default_response_parser).and_return(described_class)
9
+
10
+ @call = Get::Users.new
11
+ end
12
+
13
+ subject { @call.response.data }
14
+
15
+ context 'when request body is "{"valid": true, "score": 59}"' do
16
+ before { stub_request(:get, 'http://api.org/users').to_return(status: 200, body: '{"valid": true, "score": 59}') }
17
+
18
+ it 'should return object with #valid and #score methods' do
19
+ expect(subject.valid).to eq (true)
20
+ expect(subject.score).to eq (59)
21
+ end
22
+ end
23
+
24
+ context 'when request body is "[{"score": 59}, {"score": 60}]"' do
25
+ before { stub_request(:get, 'http://api.org/users').to_return(status: 200, body: '[{"score": 59}, {"score": 60}]') }
26
+
27
+ it 'should return array of objects with #score method' do
28
+ expect(subject[0].score).to eq (59)
29
+ expect(subject[1].score).to eq (60)
30
+ end
31
+ end
32
+
33
+ context '#keys_excluded_from_parsing' do
34
+ context 'when set to ["translations"]' do
35
+ before { described_class.keys_excluded_from_parsing = ['translations'] }
36
+
37
+ context 'and request body is "{"valid": true, "translations": {"a": "A"}}"' do
38
+ before { stub_request(:get, 'http://api.org/users').to_return(status: 200, body: '{"valid": true, "translations": {"a": "A"}}') }
39
+
40
+ it 'should should not create object from #translations' do
41
+ expect(subject.valid).to eq (true)
42
+ expect(subject.translations).to eq ({'a' => 'A'})
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Callapi::Call::Parser::Json do
4
+ context '#data' do
5
+ before do
6
+ config = Callapi::Config
7
+ allow(config).to receive(:api_host).and_return('http://api.org')
8
+
9
+ call_class = Get::Users
10
+ @call = call_class.new
11
+ end
12
+
13
+ subject { @call.response.data }
14
+
15
+ context 'when request body is "{"valid": true, "score": 59}"' do
16
+ before { stub_request(:get, 'http://api.org/users').to_return(status: 200, body: '{"valid": true, "score": 59}') }
17
+
18
+ it { expect(subject).to eq ({'valid' => true, 'score' => 59}) }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Callapi::Call::Parser::Plain do
4
+ context '#data' do
5
+ before do
6
+ Callapi::Config.configure do |config|
7
+ config.api_host = 'http://api.org'
8
+ end
9
+
10
+ Callapi::Routes.draw do
11
+ get 'plain', parser: Callapi::Call::Parser::Plain
12
+ end
13
+
14
+ stub_request(:get, 'http://api.org/plain').to_return(status: 200, body: 'some response')
15
+ end
16
+
17
+ subject { get_plain_call.response.data }
18
+
19
+ it 'should return response body' do
20
+ expect(subject).to eql ( 'some response' )
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe Callapi::Call::Parser do
4
+ before do
5
+ config = Callapi::Config
6
+ allow(config).to receive(:api_host).and_return('http://api.org')
7
+
8
+ @call = Get::Users.new
9
+ end
10
+
11
+ context 'caching response' do
12
+ before do
13
+ stub_request(:get, 'http://api.org/users').to_return(status: 200, body: '{"json": true}')
14
+ end
15
+
16
+ it 'should not parse response twice unless call is reloaded' do
17
+ cached_response = {'json' => true}
18
+ expect(@call.response.data).to eql cached_response
19
+ @call.response_parser = Callapi::Call::Parser::Plain
20
+ expect(@call.response.data).to eql cached_response
21
+ @call.reload
22
+ expect(@call.response.data).to eql '{"json": true}'
23
+ end
24
+
25
+ end
26
+
27
+ context '#data' do
28
+ subject { @call.response.data }
29
+
30
+ context 'when API returned 5xx' do
31
+ before do
32
+ stub_request(:get, 'http://api.org/users').to_return(status: 500)
33
+ end
34
+
35
+ it 'should raise ServerError error' do
36
+ expect{ subject }.to raise_error { Callapi::ServerError }
37
+ end
38
+ end
39
+
40
+ context 'when API returned 4xx' do
41
+ before do
42
+ stub_request(:get, 'http://api.org/users').to_return(status: 400)
43
+ end
44
+
45
+ it 'should raise ClientError error' do
46
+ expect{ subject }.to raise_error { Callapi::ClientError }
47
+ end
48
+ end
49
+
50
+ context 'when API returned 401' do
51
+ before do
52
+ stub_request(:get, 'http://api.org/users').to_return(status: 401)
53
+ end
54
+
55
+ it 'should raise NotAuthorized error' do
56
+ expect{ subject }.to raise_error { Callapi::NotAuthorizedError }
57
+ end
58
+ end
59
+
60
+ end
61
+ end