crystal_sdk 0.0.3 → 0.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fd4e7ecf961055dfbc0bf3b455c2783b86a2eb67
4
- data.tar.gz: e86384b92510c84344039a3e3c25d3eb21d55b2d
3
+ metadata.gz: 5bacb9a91dadcdcc60eb24dd15eedcd61d25c5f3
4
+ data.tar.gz: 33b610a788337ff076b128d651bf1a7150c8c2ef
5
5
  SHA512:
6
- metadata.gz: c47c7d9b915132174469e933a0ff2a0cdff481293aa26a7845fc589bedaa9f79a10d9d2345fa2fb6c8d96fc87ae19bff5ddc02af06e77485408832e7172b1520
7
- data.tar.gz: 737e642ecf5b811cde11a799af584c587bfd9af5fe515fcb89c05a1eadb23914393a09421720a6a10b834cfdf8e1dfa3347ea105753694fb42f1ac3684c19470
6
+ metadata.gz: 966cc07acc85cfd463c7f33b021294519c4d92f0d6cebe7bc1db209c036c1091c53b74cce502405c8e862295fe0db41385f822d7d1475373317227171f5d77e2
7
+ data.tar.gz: c75662264040de042eca6e8034e054eb2da837c5be5f948aa4c4727e59be4551a206b5c65240d7f2d3b2f49eb415fd9802c78ea62f29595c35239f461109c704
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- crystal_sdk (0.0.2)
4
+ crystal_sdk (0.0.3)
5
5
  nestful (~> 1.1)
6
+ recursive-open-struct (~> 1.0)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
@@ -15,6 +16,7 @@ GEM
15
16
  hashdiff (0.3.2)
16
17
  nestful (1.1.1)
17
18
  public_suffix (2.0.5)
19
+ recursive-open-struct (1.0.2)
18
20
  rspec (3.5.0)
19
21
  rspec-core (~> 3.5.0)
20
22
  rspec-expectations (~> 3.5.0)
data/crystal_sdk.gemspec CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.require_paths = ['lib']
20
20
 
21
21
  s.add_dependency 'nestful', '~> 1.1'
22
+ s.add_dependency 'recursive-open-struct', '~> 1.0'
22
23
  s.add_development_dependency 'rspec'
23
24
  s.add_development_dependency 'webmock'
24
25
  end
data/lib/crystal_sdk.rb CHANGED
@@ -3,6 +3,7 @@ require 'crystal_sdk/version'
3
3
 
4
4
  module CrystalSDK
5
5
  autoload :Base, 'crystal_sdk/base'
6
+ autoload :Api, 'crystal_sdk/api'
6
7
  autoload :Profile, 'crystal_sdk/profile'
7
8
 
8
9
  crystal_env_key = ENV['CRYSTAL_KEY']
@@ -1,11 +1,22 @@
1
+ require 'timeout'
2
+ require_relative 'profile/request'
3
+
1
4
  module CrystalSDK
2
5
  class Profile
3
6
  class NotFoundError < StandardError; end
4
- class NotFoundYetError < StandardError; end
5
7
  class NotAuthedError < StandardError; end
6
8
  class RateLimitHitError < StandardError; end
7
9
  class UnexpectedError < StandardError; end
8
10
 
11
+ class NotFoundYetError < StandardError
12
+ attr_reader :request
13
+
14
+ def initialize(request, msg = 'Profile not found in time')
15
+ @request = request
16
+ super(msg)
17
+ end
18
+ end
19
+
9
20
  attr_reader :info, :recommendations
10
21
 
11
22
  def initialize(info, recommendations)
@@ -14,46 +25,40 @@ module CrystalSDK
14
25
  end
15
26
 
16
27
  class << self
17
- def search(query)
28
+ def from_request(req)
29
+ return nil unless req.did_find_profile?
30
+
31
+ profile_info = req.profile_info
32
+ Profile.new(profile_info[:info], profile_info[:recommendations])
33
+ end
34
+
35
+ def search(query, timeout: 30)
36
+ request = nil
37
+
18
38
  begin
19
- resp = make_request(:post, 'person_search', params: query)
20
- body = resp.body ? JSON.parse(resp.body, symbolize_names: true) : nil
39
+ Timeout.timeout(timeout) do
40
+ request = Profile::Request.from_search(query)
41
+
42
+ loop do
43
+ sleep(2) && next unless request.did_finish?
44
+
45
+ raise NotFoundError unless request.did_find_profile?
46
+ return Profile.from_request(request)
47
+ end
48
+ end
49
+ rescue Timeout::Error
50
+ raise NotFoundYetError.new(request)
21
51
 
22
52
  rescue Nestful::ResponseError => e
23
53
  check_for_error(e.response)
24
54
  raise e
25
55
  end
26
-
27
- check_for_error(resp)
28
- new(body[:info], body[:recommendations])
29
56
  end
30
57
 
31
58
  def check_for_error(resp)
32
- body = resp.body ? JSON.parse(resp.body, symbolize_names: true) : nil
33
- not_found = body && body[:status] == 'profile_not_found'
34
- not_found_yet = body && body[:status] == 'profile_not_found_yet'
35
-
36
59
  raise RateLimitHitError if resp.code == '429'
37
60
  raise NotAuthedError if resp.code == '401'
38
- raise NotFoundError if resp.code == '404' || not_found
39
- raise NotFoundYetError if resp.code == '202' || not_found_yet
40
- raise UnexpectedError unless resp.code == '200'
41
- end
42
-
43
- def make_request(type, endpoint, params: {}, headers: {})
44
- headers = headers.merge(
45
- 'X-Org-Token' => Base.key!,
46
- 'X-Sdk-Version' => VERSION
47
- )
48
-
49
- opts = {
50
- method: type,
51
- headers: headers,
52
- params: params,
53
- format: :json
54
- }
55
-
56
- Nestful::Request.new("#{Base::API_URL}/#{endpoint}", opts).execute
61
+ raise NotFoundError if resp.code == '404'
57
62
  end
58
63
  end
59
64
  end
@@ -1,3 +1,3 @@
1
1
  module CrystalSDK
2
- VERSION = '0.0.3'.freeze
2
+ VERSION = '0.0.4'.freeze
3
3
  end
@@ -6,26 +6,27 @@ describe CrystalSDK::Profile do
6
6
  describe '.search' do
7
7
  subject { CrystalSDK::Profile.search(query) }
8
8
  let(:query) { { some_param: 'a_param' } }
9
- let(:endpoint) { 'person_search' }
10
- let(:request_type) { :post }
11
9
 
12
10
  it 'should use the correct request' do
13
- expect(CrystalSDK::Profile).to receive(:make_request)
14
- .with(request_type, endpoint, params: query)
15
- .and_return(double(code: '200', body: {
16
- info: 'info',
17
- recommendations: 'recs'
18
- }.to_json))
11
+ resp = double(
12
+ did_finish?: true,
13
+ did_find_profile?: true,
14
+ profile_info: { info: 'info', recommendations: 'recs' }
15
+ )
16
+
17
+ expect(CrystalSDK::Profile::Request).to receive(:from_search)
18
+ .with(query)
19
+ .and_return(resp)
19
20
 
20
21
  expect(subject.info).to eql('info')
21
22
  expect(subject.recommendations).to eql('recs')
22
23
  end
23
24
 
24
- context 'make_request raised unexpected error' do
25
+ context 'request creation raised unexpected error' do
25
26
  before(:each) do
26
- allow(CrystalSDK::Profile).to receive(:make_request)
27
- .with(request_type, endpoint, params: query)
28
- .and_raise("SomeRandomError")
27
+ expect(CrystalSDK::Profile::Request).to receive(:from_search)
28
+ .with(query)
29
+ .and_raise('SomeRandomError')
29
30
  end
30
31
 
31
32
  it 'should not suppress the error' do
@@ -33,10 +34,15 @@ describe CrystalSDK::Profile do
33
34
  end
34
35
  end
35
36
 
36
- context 'make_request raised expected error' do
37
+ context 'request info raised expected error' do
38
+ let(:req) { double() }
39
+
37
40
  before(:each) do
38
- allow(CrystalSDK::Profile).to receive(:make_request)
39
- .with(request_type, endpoint, params: query)
41
+ allow(CrystalSDK::Profile::Request).to receive(:from_search)
42
+ .with(query)
43
+ .and_return(req)
44
+
45
+ allow(req).to receive(:did_finish?)
40
46
  .and_raise(Nestful::ResponseError.new(nil, double()))
41
47
  end
42
48
 
@@ -55,23 +61,37 @@ describe CrystalSDK::Profile do
55
61
  end
56
62
  end
57
63
 
58
- context 'make_request raised no error' do
64
+ context 'request did not find profile' do
59
65
  before(:each) do
60
- allow(CrystalSDK::Profile).to receive(:make_request)
61
- .with(request_type, endpoint, params: query)
66
+ allow(CrystalSDK::Profile::Request).to receive(:from_search)
67
+ .with(query)
62
68
  .and_return(response)
63
69
  end
64
70
 
65
71
  let(:response) do
66
- double(code: '200', body: { resp: 'some_resp' }.to_json)
72
+ double(did_finish?: true, did_find_profile?: false)
67
73
  end
68
74
 
69
- it 'should still pass through check_for_error' do
70
- expect(CrystalSDK::Profile).to receive(:check_for_error)
71
- .with(response)
72
- .and_raise('CheckForErrorCalled')
75
+ it 'should raise NotFoundError' do
76
+ expect { subject }.to raise_error(CrystalSDK::Profile::NotFoundError)
77
+ end
78
+ end
73
79
 
74
- expect { subject }.to raise_error('CheckForErrorCalled')
80
+ context 'request did not finish before timeout expired' do
81
+ subject { CrystalSDK::Profile.search(query, timeout: 1) }
82
+
83
+ before(:each) do
84
+ allow(CrystalSDK::Profile::Request).to receive(:from_search)
85
+ .with(query)
86
+ .and_return(response)
87
+ end
88
+
89
+ let(:response) do
90
+ double(did_finish?: false)
91
+ end
92
+
93
+ it 'should raise NotFoundYetError' do
94
+ expect { subject }.to raise_error(CrystalSDK::Profile::NotFoundYetError)
75
95
  end
76
96
  end
77
97
  end
@@ -81,8 +101,7 @@ describe CrystalSDK::Profile do
81
101
 
82
102
  context '200' do
83
103
  let(:resp) do
84
- body = { status: 'profile_found', info: nil, recommendations: nil }
85
- double(body: body.to_json, code: '200')
104
+ double(code: '200')
86
105
  end
87
106
 
88
107
  it 'should raise no error' do
@@ -91,15 +110,15 @@ describe CrystalSDK::Profile do
91
110
  end
92
111
 
93
112
  context '202' do
94
- let(:resp) { double(body: nil, code: '202') }
113
+ let(:resp) { double(code: '202') }
95
114
 
96
- it 'should raise NotFoundYetError' do
97
- expect { subject }.to raise_error(CrystalSDK::Profile::NotFoundYetError)
115
+ it 'should raise no error' do
116
+ expect { subject }.to_not raise_error
98
117
  end
99
118
  end
100
119
 
101
120
  context '401' do
102
- let(:resp) { double(body: nil, code: '401') }
121
+ let(:resp) { double(code: '401') }
103
122
 
104
123
  it 'should raise NotAuthedError' do
105
124
  expect { subject }.to raise_error(CrystalSDK::Profile::NotAuthedError)
@@ -107,7 +126,7 @@ describe CrystalSDK::Profile do
107
126
  end
108
127
 
109
128
  context '404' do
110
- let(:resp) { double(body: nil, code: '404') }
129
+ let(:resp) { double(code: '404') }
111
130
 
112
131
  it 'should raise NotFoundError' do
113
132
  expect { subject }.to raise_error(CrystalSDK::Profile::NotFoundError)
@@ -115,101 +134,11 @@ describe CrystalSDK::Profile do
115
134
  end
116
135
 
117
136
  context '429' do
118
- let(:resp) { double(body: nil, code: '429') }
137
+ let(:resp) { double(code: '429') }
119
138
 
120
139
  it 'should raise RateLimitHitError' do
121
140
  expect { subject }.to raise_error(CrystalSDK::Profile::RateLimitHitError)
122
141
  end
123
142
  end
124
143
  end
125
-
126
- describe '.make_request' do
127
- context 'without api key' do
128
- it 'should raise an error' do
129
- expect { subject.make_request(:post, 'test') }
130
- .to raise_error(CrystalSDK::Base::ApiKeyNotSet)
131
- end
132
- end
133
-
134
- context 'with api key' do
135
- subject { CrystalSDK::Profile.make_request(:post, 'test_endpoint') }
136
-
137
- let(:headers) do
138
- {
139
- 'X-Org-Token' => 'SomeToken',
140
- 'X-Sdk-Version' => CrystalSDK::VERSION
141
- }
142
- end
143
-
144
- let(:stubbed_req) do
145
- stub_request(:post, "#{CrystalSDK::Base::API_URL}/test_endpoint")
146
- .with(headers: headers)
147
- end
148
-
149
- before(:each) do
150
- allow(CrystalSDK::Base).to receive(:key).and_return('SomeToken')
151
- end
152
-
153
- context 'got 4xx response code' do
154
- it 'should raise an error' do
155
- stubbed_req
156
- .to_return(status: 404, body: '{}', headers: {})
157
-
158
- expect { subject }.to raise_error
159
- end
160
- end
161
-
162
- context 'got 5xx response code' do
163
- it 'should raise error on 5xx responses' do
164
- stubbed_req
165
- .to_return(status: 500, body: '{}', headers: {})
166
-
167
- expect { subject }.to raise_error
168
- end
169
- end
170
-
171
- context 'got 2xx response code' do
172
- it 'should return correct response' do
173
- stubbed_req
174
- .to_return(status: 200, body: 'stubbed', headers: {})
175
-
176
- expect(subject.code).to eql(200)
177
- expect(subject.body).to eql('stubbed')
178
- end
179
- end
180
-
181
- context 'given params' do
182
- subject do
183
- CrystalSDK::Profile
184
- .make_request(:post, 'test_endpoint', params: params)
185
- end
186
- let(:params) { { some_param: '123'} }
187
-
188
- it 'should turn params into json body' do
189
- stubbed_req
190
- .with(body: params.to_json)
191
- .to_return(status: 200, body: 'stubbed', headers: {})
192
-
193
- expect { subject }.to_not raise_error
194
- end
195
- end
196
-
197
-
198
- context 'given headers' do
199
- subject do
200
- CrystalSDK::Profile
201
- .make_request(:post, 'test_endpoint', headers: headers)
202
- end
203
- let(:headers) { { 'X-Some-Header' => '123' } }
204
-
205
- it 'should turn params into json body' do
206
- stubbed_req
207
- .with(headers: headers)
208
- .to_return(status: 200, body: 'stubbed', headers: {})
209
-
210
- expect { subject }.to_not raise_error
211
- end
212
- end
213
- end
214
- end
215
144
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: crystal_sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cory Finger
@@ -25,6 +25,20 @@ dependencies:
25
25
  - - "~>"
26
26
  - !ruby/object:Gem::Version
27
27
  version: '1.1'
28
+ - !ruby/object:Gem::Dependency
29
+ name: recursive-open-struct
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.0'
28
42
  - !ruby/object:Gem::Dependency
29
43
  name: rspec
30
44
  requirement: !ruby/object:Gem::Requirement