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.
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: []