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 +4 -4
- data/Gemfile.lock +3 -1
- data/crystal_sdk.gemspec +1 -0
- data/lib/crystal_sdk.rb +1 -0
- data/lib/crystal_sdk/profile.rb +35 -30
- data/lib/crystal_sdk/version.rb +1 -1
- data/spec/crystal_sdk/profile_spec.rb +51 -122
- metadata +15 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5bacb9a91dadcdcc60eb24dd15eedcd61d25c5f3
|
4
|
+
data.tar.gz: 33b610a788337ff076b128d651bf1a7150c8c2ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
data/lib/crystal_sdk.rb
CHANGED
data/lib/crystal_sdk/profile.rb
CHANGED
@@ -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
|
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
|
-
|
20
|
-
|
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'
|
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
|
data/lib/crystal_sdk/version.rb
CHANGED
@@ -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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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 '
|
25
|
+
context 'request creation raised unexpected error' do
|
25
26
|
before(:each) do
|
26
|
-
|
27
|
-
.with(
|
28
|
-
.and_raise(
|
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 '
|
37
|
+
context 'request info raised expected error' do
|
38
|
+
let(:req) { double() }
|
39
|
+
|
37
40
|
before(:each) do
|
38
|
-
allow(CrystalSDK::Profile).to receive(:
|
39
|
-
.with(
|
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 '
|
64
|
+
context 'request did not find profile' do
|
59
65
|
before(:each) do
|
60
|
-
allow(CrystalSDK::Profile).to receive(:
|
61
|
-
.with(
|
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(
|
72
|
+
double(did_finish?: true, did_find_profile?: false)
|
67
73
|
end
|
68
74
|
|
69
|
-
it 'should
|
70
|
-
expect(CrystalSDK::Profile)
|
71
|
-
|
72
|
-
|
75
|
+
it 'should raise NotFoundError' do
|
76
|
+
expect { subject }.to raise_error(CrystalSDK::Profile::NotFoundError)
|
77
|
+
end
|
78
|
+
end
|
73
79
|
|
74
|
-
|
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
|
-
|
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(
|
113
|
+
let(:resp) { double(code: '202') }
|
95
114
|
|
96
|
-
it 'should raise
|
97
|
-
expect { subject }.
|
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(
|
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(
|
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(
|
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.
|
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
|