api-blueprint 0.2.0 → 0.3.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 +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
|