api_recipes 0.6.2 → 0.7.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/Gemfile +5 -9
- data/Guardfile +0 -1
- data/api_recipes.gemspec +3 -3
- data/lib/api_recipes.rb +92 -41
- data/lib/api_recipes/configuration.rb +33 -0
- data/lib/api_recipes/endpoint.rb +26 -0
- data/lib/api_recipes/exceptions.rb +12 -20
- data/lib/api_recipes/resource.rb +7 -15
- data/lib/api_recipes/version.rb +1 -1
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 409a786b8eff1b1fd48bdbb1f5c8a14cea860056581f86f0c994ce8436874f53
|
4
|
+
data.tar.gz: d8923466ea1f2109a8528e2d7e55d402c5ed2e45b714612081bda920d3e03fe4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70aa364742f8dc27d38c1fa0f5d9fec9a0c6b294421fac1413ed34379b39f2c28ae4cb993d51f67bfa4ddda16fe6b8074fe6646680b7a3fcb45c8d98e3f75d5b
|
7
|
+
data.tar.gz: fd4885cd695a4356c4f57417f77bc914a9b0104372d8eb37fb6d0531831348e9da38f2d99a394fbdc9802cd9a20bc99b3289cf4c18f92683b0fd9f9644d5f555
|
data/Gemfile
CHANGED
@@ -1,16 +1,12 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
# Specify your gem's dependencies in api_recipes.gemspec
|
4
3
|
gemspec
|
5
4
|
|
6
|
-
gem 'rake', '~>
|
7
|
-
|
8
|
-
group :development do
|
9
|
-
gem 'guard', '~> 2.13.0'
|
10
|
-
gem 'guard-rspec', '~> 4.6.4'
|
11
|
-
gem 'guard-bundler', '~> 2.1.0'
|
12
|
-
end
|
5
|
+
gem 'rake', '~> 12.3'
|
13
6
|
|
14
7
|
group :test do
|
15
|
-
gem 'rspec', '~> 3.
|
8
|
+
gem 'rspec', '~> 3.8.0'
|
9
|
+
gem 'guard', '~> 2.15.0'
|
10
|
+
gem 'guard-rspec', '~> 4.7.3'
|
11
|
+
gem 'guard-bundler', '~> 2.2.1'
|
16
12
|
end
|
data/Guardfile
CHANGED
data/api_recipes.gemspec
CHANGED
@@ -16,8 +16,8 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
17
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
18
|
spec.require_paths = ['lib']
|
19
|
-
spec.required_ruby_version = '
|
19
|
+
spec.required_ruby_version = '> 2.2.0'
|
20
20
|
|
21
|
-
spec.add_dependency 'oj', '~> 3.
|
22
|
-
spec.add_dependency 'http', '~>
|
21
|
+
spec.add_dependency 'oj', '~> 3.7.0'
|
22
|
+
spec.add_dependency 'http', '~> 4.1.1'
|
23
23
|
end
|
data/lib/api_recipes.rb
CHANGED
@@ -10,16 +10,46 @@ require 'api_recipes/settings'
|
|
10
10
|
|
11
11
|
module ApiRecipes
|
12
12
|
|
13
|
-
def self.included(
|
13
|
+
def self.included(receiver)
|
14
14
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
15
|
+
def receiver.endpoint(endpoint_name, configs = {})
|
16
|
+
unless endpoint_name.is_a?(String) || endpoint_name.is_a?(Symbol)
|
17
|
+
raise ArgumentError, "endpoint name must be a Symbol or String"
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
if configs && !configs.is_a?(Hash)
|
21
|
+
raise ApiRecipes::EndpointConfigIsNotAnHash.new(endpoint_name)
|
22
|
+
end
|
23
|
+
|
24
|
+
endpoint_name = endpoint_name.to_sym
|
25
|
+
configs = ApiRecipes._aprcps_merge_endpoints_configs(endpoint_name, configs.deep_symbolize_keys)
|
26
|
+
if self.respond_to? endpoint_name
|
27
|
+
raise EndpointNameClashError.new(self, endpoint_name)
|
28
|
+
else
|
29
|
+
ep = Endpoint.new(endpoint_name, configs)
|
30
|
+
ApiRecipes.copy_global_authorizations_to_endpoint ep
|
31
|
+
ApiRecipes._aprcps_thread_storage[endpoint_name] = {}
|
32
|
+
ApiRecipes._aprcps_thread_storage[endpoint_name][self] = ep
|
33
|
+
|
34
|
+
define_method endpoint_name do
|
35
|
+
unless ApiRecipes._aprcps_thread_storage[endpoint_name]
|
36
|
+
ApiRecipes._aprcps_thread_storage[endpoint_name] = {}
|
37
|
+
end
|
38
|
+
unless ApiRecipes._aprcps_thread_storage[endpoint_name][self.class]
|
39
|
+
ApiRecipes._aprcps_thread_storage[endpoint_name][self.class] = ep.clone
|
40
|
+
end
|
41
|
+
ApiRecipes._aprcps_thread_storage[endpoint_name][self.class]
|
42
|
+
end
|
43
|
+
define_singleton_method endpoint_name do
|
44
|
+
unless ApiRecipes._aprcps_thread_storage[endpoint_name]
|
45
|
+
ApiRecipes._aprcps_thread_storage[endpoint_name] = {}
|
46
|
+
end
|
47
|
+
unless ApiRecipes._aprcps_thread_storage[endpoint_name][self]
|
48
|
+
ApiRecipes._aprcps_thread_storage[endpoint_name][self] = ep.clone
|
49
|
+
end
|
50
|
+
ApiRecipes._aprcps_thread_storage[endpoint_name][self]
|
51
|
+
end
|
52
|
+
end
|
23
53
|
end
|
24
54
|
end
|
25
55
|
|
@@ -33,65 +63,86 @@ module ApiRecipes
|
|
33
63
|
def self.configure
|
34
64
|
if block_given?
|
35
65
|
yield(configuration)
|
36
|
-
_aprcps_define_global_endpoints
|
37
66
|
else
|
38
67
|
configuration
|
39
68
|
end
|
40
69
|
end
|
41
70
|
|
42
|
-
def self.
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
_aprcps_storage[endpoint_name]
|
50
|
-
end
|
71
|
+
def self.copy_global_authorizations_to_endpoint(endpoint)
|
72
|
+
if _aprcps_global_storage[endpoint.name]
|
73
|
+
if auth = _aprcps_global_storage[endpoint.name].basic_auth
|
74
|
+
endpoint.authorization = auth
|
75
|
+
end
|
76
|
+
if auth = _aprcps_global_storage[endpoint.name].authorization
|
77
|
+
endpoint.authorization = auth
|
51
78
|
end
|
52
79
|
end
|
53
80
|
end
|
54
81
|
|
82
|
+
def self.set_authorization_for_endpoint(authorization, endpoint_name)
|
83
|
+
endpoint_name = endpoint_name.to_sym
|
55
84
|
|
56
|
-
|
57
|
-
|
58
|
-
|
85
|
+
# Set authorization on thread storage
|
86
|
+
if _aprcps_thread_storage[endpoint_name]
|
87
|
+
_aprcps_thread_storage[endpoint_name].each do |_, endpoint|
|
88
|
+
endpoint.authorization = authorization
|
89
|
+
end
|
59
90
|
end
|
60
|
-
Thread.current[:api_recipes]
|
61
91
|
end
|
62
92
|
|
93
|
+
def self.set_basic_auth_for_endpoint(basic_auth, endpoint_name)
|
94
|
+
endpoint_name = endpoint_name.to_sym
|
63
95
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
else
|
69
|
-
ep = _aprcps_storage[ep_name]
|
70
|
-
end
|
71
|
-
obj.define_singleton_method ep_name do
|
72
|
-
ep
|
96
|
+
# Set authorization on thread storage
|
97
|
+
if _aprcps_thread_storage[endpoint_name]
|
98
|
+
_aprcps_thread_storage[endpoint_name].each do |_, endpoint|
|
99
|
+
endpoint.authorization = basic_auth
|
73
100
|
end
|
74
101
|
end
|
75
102
|
end
|
76
103
|
|
77
|
-
def self.
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
104
|
+
def self._aprcps_define_global_endpoints
|
105
|
+
configuration.endpoints_configs.each do |endpoint_name, endpoint_configs|
|
106
|
+
endpoint_name = endpoint_name.to_sym
|
107
|
+
_aprcps_global_storage[endpoint_name] = Endpoint.new endpoint_name, endpoint_configs
|
108
|
+
define_singleton_method endpoint_name do
|
109
|
+
_aprcps_global_storage[endpoint_name]
|
83
110
|
end
|
84
111
|
end
|
85
112
|
end
|
86
113
|
|
87
|
-
def self.
|
88
|
-
|
89
|
-
|
114
|
+
def self._aprcps_global_storage
|
115
|
+
unless @storage
|
116
|
+
@storage = {}
|
117
|
+
end
|
118
|
+
@storage
|
119
|
+
end
|
120
|
+
|
121
|
+
def self._aprcps_thread_storage
|
122
|
+
unless Thread.current[:api_recipes]
|
123
|
+
Thread.current[:api_recipes] = {}
|
124
|
+
end
|
125
|
+
Thread.current[:api_recipes]
|
126
|
+
end
|
127
|
+
|
128
|
+
def self._aprcps_merge_endpoints_configs(endpoint_name, configs = nil)
|
129
|
+
unless endpoint_name.is_a?(String) || endpoint_name.is_a?(Symbol)
|
130
|
+
raise ArgumentError, "no enpoint_name provided. Given: #{endpoint_name.inspect}"
|
90
131
|
end
|
91
132
|
unless ApiRecipes.configuration.endpoints_configs[endpoint_name]
|
92
133
|
ApiRecipes.configuration.endpoints_configs[endpoint_name] = {}
|
93
134
|
end
|
94
|
-
|
135
|
+
if configs
|
136
|
+
ApiRecipes.configuration.endpoints_configs[endpoint_name].merge(configs) do |_, old_val, new_val|
|
137
|
+
if new_val.nil?
|
138
|
+
old_val
|
139
|
+
else
|
140
|
+
new_val
|
141
|
+
end
|
142
|
+
end
|
143
|
+
else
|
144
|
+
ApiRecipes.configuration.endpoints_configs[endpoint_name]
|
145
|
+
end
|
95
146
|
end
|
96
147
|
end
|
97
148
|
|
@@ -1,9 +1,12 @@
|
|
1
1
|
module ApiRecipes
|
2
2
|
class Configuration
|
3
3
|
|
4
|
+
attr_accessor :log_to, :log_level
|
5
|
+
|
4
6
|
def endpoints_configs=(configs = {})
|
5
7
|
raise ArgumentError, 'endpoints_configs must be an Hash' unless configs.is_a? Hash
|
6
8
|
@endpoints_configs = configs.deep_symbolize_keys
|
9
|
+
ApiRecipes._aprcps_define_global_endpoints
|
7
10
|
end
|
8
11
|
|
9
12
|
def endpoints_configs
|
@@ -12,5 +15,35 @@ module ApiRecipes
|
|
12
15
|
end
|
13
16
|
@endpoints_configs
|
14
17
|
end
|
18
|
+
|
19
|
+
def logger=(logger)
|
20
|
+
@logger = logger
|
21
|
+
end
|
22
|
+
|
23
|
+
def logger
|
24
|
+
unless @logger
|
25
|
+
log = ::Logger.new(log_to)
|
26
|
+
log.level = normalize_log_level
|
27
|
+
log.progname = 'ApiRecipes'
|
28
|
+
@logger = log
|
29
|
+
end
|
30
|
+
|
31
|
+
@logger
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# @private
|
37
|
+
def normalize_log_level
|
38
|
+
case @log_level
|
39
|
+
when :debug, ::Logger::DEBUG, 'debug' then ::Logger::DEBUG
|
40
|
+
when :info, ::Logger::INFO, 'info' then ::Logger::INFO
|
41
|
+
when :warn, ::Logger::WARN, 'warn' then ::Logger::WARN
|
42
|
+
when :error, ::Logger::ERROR, 'error' then ::Logger::ERROR
|
43
|
+
when :fatal, ::Logger::FATAL, 'fatal' then ::Logger::FATAL
|
44
|
+
else
|
45
|
+
Logger::ERROR
|
46
|
+
end
|
47
|
+
end
|
15
48
|
end
|
16
49
|
end
|
data/lib/api_recipes/endpoint.rb
CHANGED
@@ -21,5 +21,31 @@ module ApiRecipes
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
24
|
+
|
25
|
+
def authorization=(auth)
|
26
|
+
@authorization = auth
|
27
|
+
|
28
|
+
# Check if I'm the global endpoint
|
29
|
+
if global?
|
30
|
+
# Set authorization also on every "children" (classes that define the same endpoint)
|
31
|
+
ApiRecipes.set_authorization_for_endpoint auth, name
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def basic_auth=(auth)
|
36
|
+
@basic_auth = auth
|
37
|
+
|
38
|
+
# Check if I'm the global endpoint
|
39
|
+
if global?
|
40
|
+
# Set authorization also on every "children" (classes that define the same endpoint)
|
41
|
+
ApiRecipes.set_basic_auth_for_endpoint auth, name
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def global?
|
48
|
+
ApiRecipes._aprcps_global_storage[name] == self
|
49
|
+
end
|
24
50
|
end
|
25
51
|
end
|
@@ -1,16 +1,15 @@
|
|
1
1
|
module ApiRecipes
|
2
|
-
class RouteNameClashError < Exception
|
3
|
-
attr_reader :route, :resource
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
3
|
+
class EndpointNameClashError < Exception
|
4
|
+
def initialize(object, endpoint_name)
|
5
|
+
message = "#{object.class} already defines a method called '#{endpoint_name}'. Tip: change endpoint name"
|
6
|
+
super(message)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class RouteAndResourceNamesClashError < Exception
|
11
|
+
def initialize(route_name, resource_name)
|
12
|
+
message = "route name (#{route_name}) can't be equal to resource name (#{resource_name}). Please change route or resource name."
|
14
13
|
super(message)
|
15
14
|
end
|
16
15
|
end
|
@@ -44,15 +43,8 @@ module ApiRecipes
|
|
44
43
|
end
|
45
44
|
|
46
45
|
class EndpointConfigIsNotAnHash < Exception
|
47
|
-
|
48
|
-
|
49
|
-
def initialize(message = nil, endpoint = nil)
|
50
|
-
@endpoint = endpoint
|
51
|
-
if message
|
52
|
-
# Nothing to do
|
53
|
-
else
|
54
|
-
message = "provided config for endpoint '#{@endpoint}' must be an Hash"
|
55
|
-
end
|
46
|
+
def initialize(endpoint)
|
47
|
+
message = "provided config for endpoint '#{endpoint}' must be an Hash"
|
56
48
|
super(message)
|
57
49
|
end
|
58
50
|
end
|
data/lib/api_recipes/resource.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
module ApiRecipes
|
2
2
|
class Resource
|
3
3
|
|
4
|
-
attr_accessor :authorization, :basic_auth
|
5
|
-
|
6
4
|
def initialize(name, endpoint, routes = {})
|
7
5
|
@name = name
|
8
6
|
@routes = routes
|
@@ -81,8 +79,9 @@ module ApiRecipes
|
|
81
79
|
# e.g. webapp.alarms.index
|
82
80
|
def generate_routes
|
83
81
|
@routes.each do |route, attrs|
|
82
|
+
# Check if route name clashes with resource name
|
84
83
|
if route.eql? @name
|
85
|
-
raise
|
84
|
+
raise RouteAndResourceNamesClashError.new(route, @name)
|
86
85
|
end
|
87
86
|
unless respond_to? route.to_sym
|
88
87
|
define_singleton_method route.to_sym do |*params, &block|
|
@@ -95,8 +94,8 @@ module ApiRecipes
|
|
95
94
|
self
|
96
95
|
end
|
97
96
|
|
98
|
-
def
|
99
|
-
settings.fetch(:timeout, ApiRecipes::Settings::GLOBAL_TIMEOUT)
|
97
|
+
def timeout
|
98
|
+
settings.fetch(:timeout, ApiRecipes::Settings::GLOBAL_TIMEOUT)
|
100
99
|
end
|
101
100
|
|
102
101
|
def port
|
@@ -111,22 +110,15 @@ module ApiRecipes
|
|
111
110
|
def request_with_auth
|
112
111
|
req = HTTP
|
113
112
|
req = req.headers(extract_headers)
|
114
|
-
.timeout(
|
115
|
-
:global,
|
116
|
-
write: per_kind_timeout,
|
117
|
-
connect: per_kind_timeout,
|
118
|
-
read: per_kind_timeout
|
119
|
-
)
|
113
|
+
.timeout(timeout)
|
120
114
|
|
115
|
+
basic_auth = @endpoint.basic_auth
|
121
116
|
if basic_auth
|
122
117
|
req = req.basic_auth basic_auth
|
123
|
-
elsif ba = @endpoint.basic_auth
|
124
|
-
req = req.basic_auth ba
|
125
118
|
end
|
119
|
+
authorization = @endpoint.authorization
|
126
120
|
if authorization
|
127
121
|
req = req.auth authorization
|
128
|
-
elsif auth = @endpoint.authorization
|
129
|
-
req = req.auth auth
|
130
122
|
end
|
131
123
|
req
|
132
124
|
end
|
data/lib/api_recipes/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: api_recipes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alessandro Verlato
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-07-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oj
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 3.
|
19
|
+
version: 3.7.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 3.
|
26
|
+
version: 3.7.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: http
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 4.1.1
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 4.1.1
|
41
41
|
description:
|
42
42
|
email:
|
43
43
|
- averlato@gmail.com
|
@@ -85,9 +85,9 @@ require_paths:
|
|
85
85
|
- lib
|
86
86
|
required_ruby_version: !ruby/object:Gem::Requirement
|
87
87
|
requirements:
|
88
|
-
- - "
|
88
|
+
- - ">"
|
89
89
|
- !ruby/object:Gem::Version
|
90
|
-
version: 2.
|
90
|
+
version: 2.2.0
|
91
91
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
92
|
requirements:
|
93
93
|
- - ">="
|