uploadcare-api_struct 1.1.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.
- checksums.yaml +7 -0
- data/.circleci/config.yml +25 -0
- data/.gitignore +18 -0
- data/.rspec +3 -0
- data/.rubocop.yml +15 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +123 -0
- data/LICENSE.txt +21 -0
- data/README.md +211 -0
- data/Rakefile +6 -0
- data/api_struct.gemspec +36 -0
- data/api_struct.svg +1 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/api_struct/client.rb +88 -0
- data/lib/api_struct/collection.rb +19 -0
- data/lib/api_struct/concerns/underscore.rb +7 -0
- data/lib/api_struct/entity.rb +93 -0
- data/lib/api_struct/errors/client.rb +43 -0
- data/lib/api_struct/errors/entity.rb +7 -0
- data/lib/api_struct/extensions/api_client.rb +43 -0
- data/lib/api_struct/extensions/dry_monads.rb +22 -0
- data/lib/api_struct/settings.rb +7 -0
- data/lib/api_struct/version.rb +3 -0
- data/lib/api_struct.rb +20 -0
- data/spec/api_struct/client_spec.rb +156 -0
- data/spec/api_struct/entity_spec.rb +188 -0
- data/spec/fixtures/cassettes/posts/1.yml +73 -0
- data/spec/fixtures/cassettes/posts/index_success.yml +1335 -0
- data/spec/fixtures/cassettes/posts/show_failure.yml +67 -0
- data/spec/fixtures/cassettes/posts/show_failure_html.yml +67 -0
- data/spec/fixtures/cassettes/posts/show_success.yml +74 -0
- data/spec/fixtures/cassettes/posts/suffix.yml +73 -0
- data/spec/fixtures/cassettes/posts/update_success.yml +71 -0
- data/spec/fixtures/cassettes/todos.yml +143 -0
- data/spec/fixtures/cassettes/user_todos.yml +189 -0
- data/spec/fixtures/cassettes/users/1/posts/1.yml +797 -0
- data/spec/fixtures/cassettes/users/1/posts.yml +1049 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/stub.rb +9 -0
- metadata +283 -0
@@ -0,0 +1,188 @@
|
|
1
|
+
describe ApiStruct::Entity do
|
2
|
+
extend Support::Stub
|
3
|
+
|
4
|
+
stub_api('https://jsonplaceholder.typicode.com')
|
5
|
+
|
6
|
+
# rubocop:disable Lint/ConstantDefinitionInBlock
|
7
|
+
class StubClient < ApiStruct::Client
|
8
|
+
stub_api 'posts'
|
9
|
+
|
10
|
+
def show(id)
|
11
|
+
get(id)
|
12
|
+
end
|
13
|
+
|
14
|
+
def index(params = {})
|
15
|
+
get(json: params)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class StubNestedEntity < ApiStruct::Entity
|
20
|
+
attr_entity :name
|
21
|
+
end
|
22
|
+
|
23
|
+
class StubEntity < ApiStruct::Entity
|
24
|
+
client_service StubClient
|
25
|
+
client_service StubClient, prefix: true
|
26
|
+
client_service StubClient, prefix: :custom
|
27
|
+
client_service StubClient, prefix: :only, only: :index
|
28
|
+
client_service StubClient, prefix: :except, except: :show
|
29
|
+
|
30
|
+
attr_entity :id, :title, :camel_case
|
31
|
+
|
32
|
+
has_entity :nested_entity, as: StubNestedEntity
|
33
|
+
has_entities :another_nested_entities, as: StubNestedEntity
|
34
|
+
end
|
35
|
+
# rubocop:enable Lint/ConstantDefinitionInBlock
|
36
|
+
|
37
|
+
let(:response) { { title: FFaker::Name.name, 'id' => rand(1..100), another_attributes: FFaker::Name.name } }
|
38
|
+
let(:nested_response) { { name: FFaker::Name.name } }
|
39
|
+
|
40
|
+
it '.new' do
|
41
|
+
entity = StubEntity.new(response)
|
42
|
+
|
43
|
+
expect(entity).to be_success
|
44
|
+
expect(entity.id).to eq(response['id'])
|
45
|
+
expect(entity.title).to eq(response[:title])
|
46
|
+
expect { entity.another_attributes }.to raise_error(NoMethodError)
|
47
|
+
end
|
48
|
+
|
49
|
+
it '.collection' do
|
50
|
+
entities = StubEntity.collection([response, response])
|
51
|
+
|
52
|
+
expect(entities.count).to eq(2)
|
53
|
+
expect(entities.success?).to eq(true)
|
54
|
+
expect(entities.failure?).to eq(false)
|
55
|
+
expect(entities.class).to eq(ApiStruct::Collection)
|
56
|
+
expect(entities.first.title).to eq(response[:title])
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'Nested entity' do
|
60
|
+
it 'when response is valid' do
|
61
|
+
response[:nested_entity] = nested_response
|
62
|
+
entity = StubEntity.new(response)
|
63
|
+
nested_entity = entity.nested_entity
|
64
|
+
|
65
|
+
expect(nested_entity.class).to eq(StubNestedEntity)
|
66
|
+
expect(nested_entity.name).to eq(nested_response[:name])
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'when response is invalid' do
|
70
|
+
response[:nested_entity] = [nested_response]
|
71
|
+
entity = StubEntity.new(response)
|
72
|
+
|
73
|
+
expect { entity.nested_entity }.to raise_error(ApiStruct::EntityError)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'Nested entities' do
|
78
|
+
it 'when response is valid' do
|
79
|
+
response[:another_nested_entities] = [nested_response]
|
80
|
+
entity = StubEntity.new(response)
|
81
|
+
nested_entity = entity.another_nested_entities.first
|
82
|
+
|
83
|
+
expect(nested_entity.class).to eq(StubNestedEntity)
|
84
|
+
expect(nested_entity.name).to eq(nested_response[:name])
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'when response is invalid' do
|
88
|
+
response[:another_nested_entities] = nested_response
|
89
|
+
entity = StubEntity.new(response)
|
90
|
+
|
91
|
+
expect { entity.another_nested_entities }.to raise_error(ApiStruct::EntityError)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'From monad' do
|
96
|
+
it 'convert to entity', type: :webmock do
|
97
|
+
VCR.use_cassette('posts/show_success') do
|
98
|
+
entity = StubEntity.from_monad(StubClient.new.show(1))
|
99
|
+
|
100
|
+
expect(entity).to be_success
|
101
|
+
expect(entity.id).to eq(1)
|
102
|
+
expect(entity.title).not_to be_empty
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'convert to collection of entities', type: :webmock do
|
107
|
+
VCR.use_cassette('posts/index_success') do
|
108
|
+
entities = StubEntity.from_monad(StubClient.new.index)
|
109
|
+
|
110
|
+
expect(entities.class).to eq(ApiStruct::Collection)
|
111
|
+
expect(entities.first.id).to eq(1)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'From client service' do
|
117
|
+
it 'convert to entity', type: :webmock do
|
118
|
+
VCR.use_cassette('posts/show_success') do
|
119
|
+
entity = StubEntity.show(1)
|
120
|
+
|
121
|
+
expect(entity).to be_success
|
122
|
+
expect(entity.id).to eq(1)
|
123
|
+
expect(entity.title).not_to be_empty
|
124
|
+
expect(entity.camel_case).not_to be_empty
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'convert to collection of entities', type: :webmock do
|
129
|
+
VCR.use_cassette('posts/index_success') do
|
130
|
+
entities = StubEntity.index
|
131
|
+
|
132
|
+
expect(entities.class).to eq(ApiStruct::Collection)
|
133
|
+
expect(entities.first.id).to eq(1)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe '.client_service' do
|
139
|
+
context 'prefix' do
|
140
|
+
context 'empty' do
|
141
|
+
it 'should register client as base' do
|
142
|
+
expect(StubEntity.clients[:base]).to eq StubClient
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should define client methods without prefix' do
|
146
|
+
expect(StubEntity).to be_respond_to(:show)
|
147
|
+
expect(StubEntity).to be_respond_to(:index)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context 'eq true' do
|
152
|
+
it 'should register client as stubclient' do
|
153
|
+
expect(StubEntity.clients[:stub_client]).to eq StubClient
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'should define client methods' do
|
157
|
+
expect(StubEntity).to be_respond_to(:stub_client_show)
|
158
|
+
expect(StubEntity).to be_respond_to(:stub_client_index)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'eq string' do
|
163
|
+
it 'should register client as custom' do
|
164
|
+
expect(StubEntity.clients[:custom]).to eq StubClient
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'should define client methods' do
|
168
|
+
expect(StubEntity).to be_respond_to(:custom_show)
|
169
|
+
expect(StubEntity).to be_respond_to(:custom_index)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context 'only' do
|
175
|
+
it 'should define methods from options[:only]' do
|
176
|
+
expect(StubEntity).to be_respond_to(:only_index)
|
177
|
+
expect(StubEntity).not_to be_respond_to(:only_show)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
context 'except' do
|
182
|
+
it 'should define all methods except options[:except]' do
|
183
|
+
expect(StubEntity).to be_respond_to(:except_index)
|
184
|
+
expect(StubEntity).not_to be_respond_to(:except_show)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://jsonplaceholder.typicode.com/posts/1
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Accept:
|
11
|
+
- application/json
|
12
|
+
Content-Type:
|
13
|
+
- application/json
|
14
|
+
Connection:
|
15
|
+
- close
|
16
|
+
Host:
|
17
|
+
- jsonplaceholder.typicode.com
|
18
|
+
User-Agent:
|
19
|
+
- http.rb/3.0.0
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 200
|
23
|
+
message: OK
|
24
|
+
headers:
|
25
|
+
Date:
|
26
|
+
- Tue, 06 Mar 2018 08:02:15 GMT
|
27
|
+
Content-Type:
|
28
|
+
- application/json; charset=utf-8
|
29
|
+
Content-Length:
|
30
|
+
- '292'
|
31
|
+
Connection:
|
32
|
+
- close
|
33
|
+
Set-Cookie:
|
34
|
+
- __cfduid=db9bcdebf92e32fd92ce9630387d2e0761520323335; expires=Wed, 06-Mar-19
|
35
|
+
08:02:15 GMT; path=/; domain=.typicode.com; HttpOnly
|
36
|
+
X-Powered-By:
|
37
|
+
- Express
|
38
|
+
Vary:
|
39
|
+
- Origin, Accept-Encoding
|
40
|
+
Access-Control-Allow-Credentials:
|
41
|
+
- 'true'
|
42
|
+
Cache-Control:
|
43
|
+
- public, max-age=14400
|
44
|
+
Pragma:
|
45
|
+
- no-cache
|
46
|
+
Expires:
|
47
|
+
- Tue, 06 Mar 2018 12:02:15 GMT
|
48
|
+
X-Content-Type-Options:
|
49
|
+
- nosniff
|
50
|
+
Etag:
|
51
|
+
- W/"124-yiKdLzqO5gfBrJFrcdJ8Yq0LGnU"
|
52
|
+
Via:
|
53
|
+
- 1.1 vegur
|
54
|
+
Cf-Cache-Status:
|
55
|
+
- HIT
|
56
|
+
Expect-Ct:
|
57
|
+
- max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
|
58
|
+
Server:
|
59
|
+
- cloudflare
|
60
|
+
Cf-Ray:
|
61
|
+
- 3f734c8d68fb8d7d-DME
|
62
|
+
body:
|
63
|
+
encoding: UTF-8
|
64
|
+
string: |-
|
65
|
+
{
|
66
|
+
"userId": 1,
|
67
|
+
"id": 1,
|
68
|
+
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
|
69
|
+
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
|
70
|
+
}
|
71
|
+
http_version:
|
72
|
+
recorded_at: Tue, 06 Mar 2018 08:09:11 GMT
|
73
|
+
recorded_with: VCR 3.0.3
|