api_gears 1.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 +7 -0
- data/lib/api_gears.rb +267 -0
- metadata +43 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 0f507faa2482f5d973b16cccbd1e571f3475251cd2bd905cccc3e56557c4eeb1
|
|
4
|
+
data.tar.gz: 16052d258d0e726758d00185999c6063b4ef9e05440406cdc4ff24d74714c9cc
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 053a41ef2a760d158b8130b775357b32642b25e0bf5b5a61cb8c8dfc70f55b6925d3ce9af3e91b8e2238eb7013269f6059df87fb81e372535bd354c2d2b4216c
|
|
7
|
+
data.tar.gz: d48f6cfd3fb8e7a6d58d9917309bfaced0f51352269a37560d505b986ca5f48d20386753b53e8e2bb405c6f77d1a0930ad4da53dd6943e1eb6775a64fb2be40f
|
data/lib/api_gears.rb
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
require 'uri'
|
|
2
|
+
require 'net/http'
|
|
3
|
+
require 'date'
|
|
4
|
+
require 'json'
|
|
5
|
+
# Base class for API client development, specify endpoints with `endpoint "shortcut_name", path:"api/subpath/"` syntax and method calls to valid shortcut_names will be caught with method_missing
|
|
6
|
+
class ApiGears
|
|
7
|
+
# Intializes a new ApiGears instance.
|
|
8
|
+
#
|
|
9
|
+
# @param url [String] the base url for the API being queried
|
|
10
|
+
# @param options [Hash] allows setting :query_interval, global params (such as api-key, and default query_method)
|
|
11
|
+
# @return [ApiGears] a new ApiGears Instance
|
|
12
|
+
def initialize(url, **options)
|
|
13
|
+
@uri = URI.parse(url)
|
|
14
|
+
@endpoints = {}
|
|
15
|
+
@last_query_time = 0
|
|
16
|
+
if(!options[:query_interval].nil?)
|
|
17
|
+
@query_interval = options[:query_interval]
|
|
18
|
+
else
|
|
19
|
+
@query_interval = 0
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if(!options[:params].nil?)
|
|
23
|
+
@params = options[:params]
|
|
24
|
+
else
|
|
25
|
+
@params = {}
|
|
26
|
+
end
|
|
27
|
+
if(!options[:content_type].nil?)
|
|
28
|
+
@content_type = options[:content_type]
|
|
29
|
+
else
|
|
30
|
+
@content_type = "plain"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
if(!options[:query_method].nil?)
|
|
34
|
+
@query_method = options[:query_method].to_sym
|
|
35
|
+
else
|
|
36
|
+
@query_method = :GET
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
# Provides information on the request an endpoint will send.
|
|
40
|
+
#
|
|
41
|
+
# @param endpoint [Symbol] the base url for the API being queried
|
|
42
|
+
# @param args [Hash] params to flesh-out the API call.
|
|
43
|
+
# @return [nil]
|
|
44
|
+
def request_info(endpoint,**args)
|
|
45
|
+
if(args.nil?)
|
|
46
|
+
args = {}
|
|
47
|
+
end
|
|
48
|
+
args[:endpoint] = endpoint.to_sym
|
|
49
|
+
r_info = build_request(args)
|
|
50
|
+
query_method = r_info[:query_method]
|
|
51
|
+
url = r_info[:url]
|
|
52
|
+
puts "this method will #{query_method} #{url}"
|
|
53
|
+
puts "With query params:"+r_info[:params].keys.collect{|k| "#{k}:#{r_info[:params][k]}"}.join("\n")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def request(**args)
|
|
57
|
+
r_info = build_request(args)
|
|
58
|
+
current_elapsed = DateTime.now.strftime('%s').to_i - @last_query_time
|
|
59
|
+
if(current_elapsed < @query_interval)
|
|
60
|
+
cooldown = @query_interval - current_elapsed
|
|
61
|
+
puts "Sleeping #{cooldown} seconds to accomodate query interval."
|
|
62
|
+
sleep(cooldown)
|
|
63
|
+
end
|
|
64
|
+
query_method = r_info[:query_method]
|
|
65
|
+
url = r_info[:url]
|
|
66
|
+
|
|
67
|
+
puts "#{query_method} #{url}"
|
|
68
|
+
http = Net::HTTP.new(url.host, url.port)
|
|
69
|
+
http.use_ssl = true
|
|
70
|
+
if(query_method == :GET)
|
|
71
|
+
request = Net::HTTP::Get.new(url)
|
|
72
|
+
if(!@params.nil?) # Set API-wide header params, such as api-key
|
|
73
|
+
@params.each_pair do |param_name,param|
|
|
74
|
+
request[param_name.to_s] = param
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
if(query_method == :POST)
|
|
79
|
+
request = Net::HTTP::Post.new(url)
|
|
80
|
+
end
|
|
81
|
+
if(query_method == :PATCH)
|
|
82
|
+
request = Net::HTTP::Patch.new(url)
|
|
83
|
+
end
|
|
84
|
+
if(query_method == :PUT)
|
|
85
|
+
request = Net::HTTP::Put.new(url)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
if(query_method != :GET)
|
|
89
|
+
if(@content_type == "json")
|
|
90
|
+
r_info[:params] = JSON.generate(r_info[:params])
|
|
91
|
+
elsif(@content_type == "plain")
|
|
92
|
+
r_info[:params] = URI.encode_www_form(r_info[:params])
|
|
93
|
+
end
|
|
94
|
+
response = http.request(request,r_info[:params])
|
|
95
|
+
else
|
|
96
|
+
response = http.request(request)
|
|
97
|
+
end
|
|
98
|
+
@last_query_time = DateTime.now.strftime('%s').to_i
|
|
99
|
+
data = JSON.parse(response.read_body)
|
|
100
|
+
if(response.code.to_i >= 200 && response.code.to_i < 300)
|
|
101
|
+
puts "success"
|
|
102
|
+
return data
|
|
103
|
+
else
|
|
104
|
+
puts response.code.to_s+" "+response.message
|
|
105
|
+
end
|
|
106
|
+
# if(@endpoint[args[:endpoint][:block]])
|
|
107
|
+
# return @endpoint[args[:endpoint]][:block](data)
|
|
108
|
+
# else
|
|
109
|
+
|
|
110
|
+
# end
|
|
111
|
+
|
|
112
|
+
end
|
|
113
|
+
# Handles calls to any endpoint created during initialization
|
|
114
|
+
#
|
|
115
|
+
# @param m [Symbol] the method name being called
|
|
116
|
+
# @param args [Hash] params passed to method call
|
|
117
|
+
# @param block [Block] As yet unused.
|
|
118
|
+
# @return [Hash] or [Array], depending on what API returns.
|
|
119
|
+
def method_missing(m, **args, &block)
|
|
120
|
+
if(!@endpoints[m.to_sym].nil?)
|
|
121
|
+
request(endpoint:m.to_sym,**args)
|
|
122
|
+
else
|
|
123
|
+
raise NoMethodError.new "endpoint '#{m}' not defined in #{self.class.name}. Call `<#{self.class.name} instance>.endpoints` to get a list of defined endpoints."
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
end
|
|
127
|
+
# Provides a list of endpoints that can be called as method names on the ApiGears object. Api calls are caught with #method_missing
|
|
128
|
+
#
|
|
129
|
+
# @return [Array]
|
|
130
|
+
def endpoints
|
|
131
|
+
@endpoints.keys
|
|
132
|
+
end
|
|
133
|
+
# Provides a list of args that a particular endpoint needs
|
|
134
|
+
# @param e [Symbol] or [String]
|
|
135
|
+
# @return [Array] of argument symbols sought during endpoint call.
|
|
136
|
+
def args_for(e)
|
|
137
|
+
@endpoints[e.to_sym][:args].map{|arg| arg.to_sym}
|
|
138
|
+
end
|
|
139
|
+
def query_params_for(e)
|
|
140
|
+
@endpoints[e.to_sym][:query_params].map{|arg| arg.to_sym}.concat(@endpoints[e.to_sym][:set_query_params].keys)
|
|
141
|
+
end
|
|
142
|
+
# Can be called during override of #request to modify API response data before return
|
|
143
|
+
# @param data [Hash] or [Array]
|
|
144
|
+
# @return [Hash] or [Array] depending on the data in the API response
|
|
145
|
+
def prepare_data(data, depth=0,&block)
|
|
146
|
+
if(!block.nil?)
|
|
147
|
+
yield(data,depth)
|
|
148
|
+
end
|
|
149
|
+
depth = depth + 1
|
|
150
|
+
if(data.is_a? Array)
|
|
151
|
+
data.each_index do |i|
|
|
152
|
+
if(!block.nil?)
|
|
153
|
+
data[i] = self.prepare_data(data[i],depth, &block)
|
|
154
|
+
else
|
|
155
|
+
data[i] = self.prepare_data(data[i],depth)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
if(data.is_a? Hash)
|
|
160
|
+
data.symbolize_keys!
|
|
161
|
+
data.each_pair do |k,v|
|
|
162
|
+
if(!block.nil?)
|
|
163
|
+
data[k] = self.prepare_data(v,depth, &block)
|
|
164
|
+
else
|
|
165
|
+
data[k] = self.prepare_data(v,depth)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
return data
|
|
170
|
+
end
|
|
171
|
+
private
|
|
172
|
+
def build_request(args)
|
|
173
|
+
if(args[:endpoint])
|
|
174
|
+
endpoint_data = @endpoints[args[:endpoint].to_sym]
|
|
175
|
+
end
|
|
176
|
+
if(args[:query_method])
|
|
177
|
+
args[:query_method] = args[:query_method].to_s.upcase.to_sym
|
|
178
|
+
elsif(endpoint_data[:query_method])
|
|
179
|
+
args[:query_method] = endpoint_data[:query_method].to_s.upcase.to_sym
|
|
180
|
+
else
|
|
181
|
+
args[:query_method] = @query_method.to_s.upcase.to_sym
|
|
182
|
+
end
|
|
183
|
+
uf = url_for(args[:endpoint],args)
|
|
184
|
+
return {query_method:args[:query_method],**uf}
|
|
185
|
+
end
|
|
186
|
+
# Allows implementer to specify an API endpoint to be queried by a specific method name.
|
|
187
|
+
# @param name [String] the method name that will execute the query
|
|
188
|
+
# @param args [Hash] accepts path (subpath to query), query_params (query parameters to be sent when method is called) and set_query_params (query parameters that are specified ahead of time for each execution of the method being defined)
|
|
189
|
+
# @return [nil]
|
|
190
|
+
def endpoint(name, **args, &block)
|
|
191
|
+
if(args[:path] == nil)
|
|
192
|
+
args[:path] = "/#{name.to_s}"
|
|
193
|
+
end
|
|
194
|
+
if(args[:args].nil?)
|
|
195
|
+
args[:args] = []
|
|
196
|
+
else
|
|
197
|
+
args[:args] = args[:args].map do |arg_name|
|
|
198
|
+
arg_name.to_sym
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
if(args[:path].match(/\{([a-zA-Z_0-9\-]*)\}/))
|
|
202
|
+
path_args = args[:path].scan(/\{([a-zA-Z_0-9\-]*)\}/).flatten
|
|
203
|
+
path_args.each do |name|
|
|
204
|
+
args[:args] << name.to_sym
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
if(args[:query_params].nil?)
|
|
208
|
+
args[:query_params] = {}
|
|
209
|
+
end
|
|
210
|
+
if(args[:set_query_params].nil?)
|
|
211
|
+
args[:set_query_params] = {}
|
|
212
|
+
end
|
|
213
|
+
@endpoints[name.to_sym] = {path:args[:path],args:path_args,query_params:args[:query_params],query_method:args[:query_method] ,set_query_params:args[:set_query_params], block:block}
|
|
214
|
+
end
|
|
215
|
+
# Builds URL and args for a specific endpoint call
|
|
216
|
+
# @param endpoint [String] or [Symbol] the method name that has been called
|
|
217
|
+
# @param args [Hash] the args passed in during the method call, when the required arg has 2 parts, (eg "book_id"), the first part of the arg name can be used (in this case "book" though this should only be used when "book" is unique in the query)
|
|
218
|
+
# @return [Hash] with url and query params
|
|
219
|
+
def url_for(endpoint, args)
|
|
220
|
+
url = @uri.dup
|
|
221
|
+
|
|
222
|
+
e_path = @endpoints[endpoint.to_sym][:path]
|
|
223
|
+
|
|
224
|
+
if(!@endpoints[endpoint.to_sym][:args].nil? && @endpoints[endpoint.to_sym][:args].length > 0)
|
|
225
|
+
args_for(endpoint.to_sym).each do |arg|
|
|
226
|
+
specific_arg_not_found = (args[arg].nil?)
|
|
227
|
+
arg_has_two_parts = (arg.to_s.split("_").length == 2)
|
|
228
|
+
arg_shortcut_found = (!args[arg.to_s.split("_")[0].to_sym].nil?)
|
|
229
|
+
if(specific_arg_not_found && arg_has_two_parts && arg_shortcut_found )
|
|
230
|
+
value = args[arg.to_s.split("_")[0].to_sym]
|
|
231
|
+
else
|
|
232
|
+
value = args[arg]
|
|
233
|
+
end
|
|
234
|
+
e_path = e_path.gsub("{#{arg.to_s}}", value.to_s)
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
if(e_path.include? @uri.path)
|
|
239
|
+
url.path = e_path
|
|
240
|
+
else
|
|
241
|
+
url.path = url.path + e_path
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
if(@endpoints[endpoint.to_sym][:query_params])
|
|
246
|
+
query_set = {}
|
|
247
|
+
query_params_for(endpoint.to_sym).each do |param|
|
|
248
|
+
if(args[param.to_sym])
|
|
249
|
+
query_set[param.to_sym] = args[param.to_sym]
|
|
250
|
+
elsif @endpoints[endpoint.to_sym][:set_query_params][param.to_sym]
|
|
251
|
+
query_set[param.to_sym] = @endpoints[endpoint.to_sym][:set_query_params][param.to_sym]
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
if(args[:query_method] == :GET)
|
|
257
|
+
url.query = URI.encode_www_form(query_set)
|
|
258
|
+
return {url:url,params:{}}
|
|
259
|
+
else
|
|
260
|
+
return {url:url,params:query_set}
|
|
261
|
+
end
|
|
262
|
+
else
|
|
263
|
+
|
|
264
|
+
return {url:url,params:{}}
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: api_gears
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.4
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Greg Mikeska
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2020-06-18 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: ApiGears enables rapid deployment of API clients.
|
|
14
|
+
email: digialintaglio@lavabit.com
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- lib/api_gears.rb
|
|
20
|
+
homepage: https://github.com/gmikeska/api_gears
|
|
21
|
+
licenses:
|
|
22
|
+
- MIT
|
|
23
|
+
metadata: {}
|
|
24
|
+
post_install_message:
|
|
25
|
+
rdoc_options: []
|
|
26
|
+
require_paths:
|
|
27
|
+
- lib
|
|
28
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
34
|
+
requirements:
|
|
35
|
+
- - ">="
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: '0'
|
|
38
|
+
requirements: []
|
|
39
|
+
rubygems_version: 3.0.3
|
|
40
|
+
signing_key:
|
|
41
|
+
specification_version: 4
|
|
42
|
+
summary: Rapid API client deployment library
|
|
43
|
+
test_files: []
|