api-blueprint 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +31 -0
- data/lib/api-blueprint/blueprint.rb +26 -4
- data/lib/api-blueprint/builder.rb +7 -1
- data/lib/api-blueprint/model.rb +7 -0
- data/lib/api-blueprint/parser.rb +2 -0
- data/lib/api-blueprint/response_middleware.rb +20 -0
- data/lib/api-blueprint/version.rb +1 -1
- data/lib/api-blueprint.rb +4 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 690bfec6246875178ddab86cb4b39c78543a689c
|
4
|
+
data.tar.gz: ea236a3bdd43e5ab463573d072f43d6bca9751a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9f5f9c9bba8fe2d8081cc2fbea82d8e8bb3e9fc48d6a720d1df88ec424fb3e5e70a4462e2a2d2d60ca41b97fdbd5725cc8cdcdbe41479bbd484ac0d67dbf620
|
7
|
+
data.tar.gz: f7b52ca2dac76e595625a3e4aec9e9ed68a9cf8f144c7979ae699e8a832e9e32380fdda2935ef6b0d1e43f46836d0eb09213599cfa155a699419e08db0166df5
|
data/README.md
CHANGED
@@ -118,6 +118,37 @@ Astronaut.send_to_space(nil) # => <ActiveModel::Errors ...>
|
|
118
118
|
|
119
119
|
Behind the scenes, ApiBlueprint uses the body hash to initialize a new instance of your model, and then runs validations. If there are any errors, the API request is not run and the errors object is returned.
|
120
120
|
|
121
|
+
## Error handling
|
122
|
+
|
123
|
+
If an API response includes an `errors` object, ApiBlueprint uses it to assign `ActiveModel::Errors` instances on the class which is built. This way, validation errors which come an the API behave exactly the same as validation errors set locally through validations on the model.
|
124
|
+
|
125
|
+
Certain response statuses will also cause ApiBlueprint to behave in different ways:
|
126
|
+
|
127
|
+
| HTTP Status range | Behavior |
|
128
|
+
| ----------------- | -------- |
|
129
|
+
| 200 - 400 | Objects are built normally, no errors raised |
|
130
|
+
| 401 | raises `ApiBlueprint::UnauthenticatedError` |
|
131
|
+
| 402 - 499 | raises `ApiBlueprint::ClientError` |
|
132
|
+
| 500 - 599 | raises `ApiBlueprint::ServerError` |
|
133
|
+
|
134
|
+
## Access to response headers and status codes
|
135
|
+
|
136
|
+
By default, ApiBlueprint tries to set `response_headers` and `response_status` on the model which is created from an API response. `ApiBlueprint::Model` also has a convenience method `api_request_success?` which can be used to easily assert whether a response was in the 200-399 range. This makes it simple to render different responses in controllers. For example:
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
# app/controllers/astronauts_controller.rb
|
140
|
+
class AstronautsController < ApplicationController
|
141
|
+
def index
|
142
|
+
@astronauts = api.run AstronautsInSpace.fetch
|
143
|
+
if @astronauts.api_request_success?
|
144
|
+
render json: @astronauts
|
145
|
+
else
|
146
|
+
render json: @astronauts.errors, status: :bad_request
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
```
|
151
|
+
|
121
152
|
## Blueprint options
|
122
153
|
|
123
154
|
When defining a blueprint in a model, you can pass it a number of options to set request headers, params, body, or to run code after an instance of the model has been initialized. Here's some examples:
|
@@ -32,7 +32,7 @@ module ApiBlueprint
|
|
32
32
|
response = call_api all_request_options(options)
|
33
33
|
|
34
34
|
if creates.present?
|
35
|
-
created = build from: response.body, headers: response.headers
|
35
|
+
created = build from: response.body, headers: response.headers, status: response.status
|
36
36
|
else
|
37
37
|
created = response
|
38
38
|
end
|
@@ -42,15 +42,18 @@ module ApiBlueprint
|
|
42
42
|
|
43
43
|
private
|
44
44
|
|
45
|
-
def build(from:, headers: {})
|
45
|
+
def build(from:, headers: {}, status: nil)
|
46
46
|
builder_options = {
|
47
47
|
body: parser.parse(from),
|
48
48
|
headers: headers,
|
49
|
+
status: status,
|
49
50
|
replacements: replacements,
|
50
51
|
creates: creates
|
51
52
|
}
|
52
53
|
|
53
|
-
builder.new(builder_options).build
|
54
|
+
builder.new(builder_options).build.tap do |built|
|
55
|
+
set_errors built, builder_options[:body]
|
56
|
+
end
|
54
57
|
end
|
55
58
|
|
56
59
|
def call_api(options)
|
@@ -64,8 +67,8 @@ module ApiBlueprint
|
|
64
67
|
|
65
68
|
def connection
|
66
69
|
Faraday.new do |conn|
|
70
|
+
conn.use ApiBlueprint::ResponseMiddleware
|
67
71
|
conn.response :json, content_type: /\bjson$/
|
68
|
-
conn.response :raise_error
|
69
72
|
# conn.response :logger
|
70
73
|
|
71
74
|
conn.adapter Faraday.default_adapter
|
@@ -75,5 +78,24 @@ module ApiBlueprint
|
|
75
78
|
end
|
76
79
|
end
|
77
80
|
|
81
|
+
def set_errors(obj, body)
|
82
|
+
if obj.respond_to?(:errors) && body.is_a?(Hash)
|
83
|
+
errors = body.with_indifferent_access.fetch :errors, {}
|
84
|
+
errors.each do |field, messages|
|
85
|
+
if messages.is_a? Array
|
86
|
+
messages.each do |message|
|
87
|
+
set_error obj, field, message
|
88
|
+
end
|
89
|
+
else
|
90
|
+
set_error obj, field, messages
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def set_error(obj, field, messages)
|
97
|
+
obj.errors.add field.to_sym, messages
|
98
|
+
end
|
99
|
+
|
78
100
|
end
|
79
101
|
end
|
@@ -4,6 +4,7 @@ module ApiBlueprint
|
|
4
4
|
|
5
5
|
attribute :body, Types::Hash.default(Hash.new)
|
6
6
|
attribute :headers, Types::Hash.default(Hash.new)
|
7
|
+
attribute :status, Types::Int.optional
|
7
8
|
attribute :replacements, Types::Hash.default(Hash.new)
|
8
9
|
attribute :creates, Types::Any
|
9
10
|
|
@@ -18,7 +19,12 @@ module ApiBlueprint
|
|
18
19
|
end
|
19
20
|
|
20
21
|
def prepare_item(item)
|
21
|
-
|
22
|
+
meta = {
|
23
|
+
response_headers: headers,
|
24
|
+
response_status: status
|
25
|
+
}
|
26
|
+
|
27
|
+
meta.merge with_replacements(item.deep_symbolize_keys)
|
22
28
|
end
|
23
29
|
|
24
30
|
def build_item(item)
|
data/lib/api-blueprint/model.rb
CHANGED
@@ -14,6 +14,9 @@ module ApiBlueprint
|
|
14
14
|
setting :builder, Builder.new
|
15
15
|
setting :replacements, {}
|
16
16
|
|
17
|
+
attribute :response_headers, Types::Hash.optional
|
18
|
+
attribute :response_status, Types::Int.optional
|
19
|
+
|
17
20
|
def self.blueprint(http_method, url, options = {}, &block)
|
18
21
|
blueprint_opts = {
|
19
22
|
http_method: http_method,
|
@@ -35,5 +38,9 @@ module ApiBlueprint
|
|
35
38
|
Collection.new blueprints, self
|
36
39
|
end
|
37
40
|
|
41
|
+
def api_request_success?
|
42
|
+
response_status.present? && (200...299).include?(response_status)
|
43
|
+
end
|
44
|
+
|
38
45
|
end
|
39
46
|
end
|
data/lib/api-blueprint/parser.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
module ApiBlueprint
|
2
|
+
class ResponseMiddleware < Faraday::Response::Middleware
|
3
|
+
|
4
|
+
def on_complete(env)
|
5
|
+
case env[:status]
|
6
|
+
when 401
|
7
|
+
raise ApiBlueprint::UnauthenticatedError, response_values(env)
|
8
|
+
when 402..499
|
9
|
+
raise ApiBlueprint::ClientError, response_values(env)
|
10
|
+
when 500...599
|
11
|
+
raise ApiBlueprint::ServerError, response_values(env)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def response_values(env)
|
16
|
+
{ status: env.status, headers: env.response_headers, body: env.body }
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
data/lib/api-blueprint.rb
CHANGED
@@ -7,6 +7,7 @@ require 'active_model'
|
|
7
7
|
require 'active_support/core_ext/hash'
|
8
8
|
require 'addressable'
|
9
9
|
|
10
|
+
require 'api-blueprint/response_middleware'
|
10
11
|
require 'api-blueprint/cache'
|
11
12
|
require 'api-blueprint/types'
|
12
13
|
require 'api-blueprint/url'
|
@@ -20,4 +21,7 @@ require 'api-blueprint/collection'
|
|
20
21
|
module ApiBlueprint
|
21
22
|
class DefinitionError < StandardError; end
|
22
23
|
class BuilderError < StandardError; end
|
24
|
+
class ServerError < StandardError; end
|
25
|
+
class UnauthenticatedError < StandardError; end
|
26
|
+
class ClientError < StandardError; end
|
23
27
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: api-blueprint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Damien Timewell
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-04-
|
11
|
+
date: 2018-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-types
|
@@ -210,6 +210,7 @@ files:
|
|
210
210
|
- lib/api-blueprint/collection.rb
|
211
211
|
- lib/api-blueprint/model.rb
|
212
212
|
- lib/api-blueprint/parser.rb
|
213
|
+
- lib/api-blueprint/response_middleware.rb
|
213
214
|
- lib/api-blueprint/runner.rb
|
214
215
|
- lib/api-blueprint/types.rb
|
215
216
|
- lib/api-blueprint/url.rb
|