api_gears 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/api_gears.rb +267 -0
  3. 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: []