flatfile_api 0.1.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 +7 -0
- data/.editorconfig +12 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +9 -0
- data/README.md +264 -0
- data/Rakefile +3 -0
- data/lib/flatfile_api/paginated_response.rb +46 -0
- data/lib/flatfile_api/response.rb +12 -0
- data/lib/flatfile_api/string_tools.rb +16 -0
- data/lib/flatfile_api/version.rb +5 -0
- data/lib/flatfile_api.rb +447 -0
- data/scripts/code_gen.rb +54 -0
- data/scripts/doc_gen.rb +38 -0
- data/scripts/parse_docs.rb +83 -0
- data/scripts/spec_2022-12-02.yaml +372 -0
- data/sig/flatfile_api.rbs +115 -0
- metadata +62 -0
data/lib/flatfile_api.rb
ADDED
@@ -0,0 +1,447 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "net/http"
|
5
|
+
|
6
|
+
require_relative "flatfile_api/version"
|
7
|
+
require_relative "flatfile_api/response"
|
8
|
+
require_relative "flatfile_api/paginated_response"
|
9
|
+
require_relative "flatfile_api/string_tools"
|
10
|
+
|
11
|
+
class FlatfileApi
|
12
|
+
class Error < StandardError; end
|
13
|
+
|
14
|
+
using StringTools
|
15
|
+
|
16
|
+
PROTO = "https"
|
17
|
+
HOST = "api.us.flatfile.io"
|
18
|
+
|
19
|
+
def initialize(access_key_id: ENV['FLATFILE_ACCESS_KEY_ID'], secret_access_key: ENV['FLATFILE_SECRET_ACCESS_KEY'], debug: false)
|
20
|
+
@access_key_id = access_key_id
|
21
|
+
@secret_access_key = secret_access_key
|
22
|
+
@debug = debug
|
23
|
+
@redirect_limit = 5
|
24
|
+
|
25
|
+
@base_uri = URI "#{PROTO}://#{HOST}"
|
26
|
+
@agent = Net::HTTP.new @base_uri.host, @base_uri.port
|
27
|
+
@agent.use_ssl = PROTO == 'https'
|
28
|
+
@agent.keep_alive_timeout = 10
|
29
|
+
@agent.set_debug_output $stdout if @debug
|
30
|
+
end
|
31
|
+
|
32
|
+
def exchange_access_key_for_jwt(
|
33
|
+
access_key_id:,
|
34
|
+
expires_in:nil,
|
35
|
+
secret_access_key:
|
36
|
+
)
|
37
|
+
|
38
|
+
request(
|
39
|
+
:post,
|
40
|
+
"/auth/access-key-exchange",
|
41
|
+
body_params: {
|
42
|
+
accessKeyId: access_key_id, # Access Key generated in app
|
43
|
+
expiresIn: expires_in, # Sets an expiration (in seconds)
|
44
|
+
secretAccessKey: secret_access_key, # Secret Access Key generated in app
|
45
|
+
},
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def download_an_upload(
|
50
|
+
batch_id:,
|
51
|
+
type:
|
52
|
+
)
|
53
|
+
|
54
|
+
request(
|
55
|
+
:get,
|
56
|
+
"/batch/%<batchId>s/export.csv",
|
57
|
+
path_params: {
|
58
|
+
batchId: batch_id, # A valid UUID
|
59
|
+
},
|
60
|
+
query_params: {
|
61
|
+
type: type, # File to download
|
62
|
+
},
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
def delete_an_upload(
|
67
|
+
batch_id:
|
68
|
+
)
|
69
|
+
|
70
|
+
request(
|
71
|
+
:delete,
|
72
|
+
"/batch/%<batchId>s",
|
73
|
+
path_params: {
|
74
|
+
batchId: batch_id, # A valid UUID
|
75
|
+
},
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
def bulk_delete_uploads(
|
80
|
+
older_than_quantity:,
|
81
|
+
older_than_unit:,
|
82
|
+
send_email:,
|
83
|
+
team_id:
|
84
|
+
)
|
85
|
+
|
86
|
+
request(
|
87
|
+
:get,
|
88
|
+
"/delete/batches",
|
89
|
+
query_params: {
|
90
|
+
olderThanQuantity: older_than_quantity,
|
91
|
+
olderThanUnit: older_than_unit,
|
92
|
+
sendEmail: send_email,
|
93
|
+
teamId: team_id,
|
94
|
+
},
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
def list_workspace_uploads(
|
99
|
+
end_user_id:nil,
|
100
|
+
environment_id:nil,
|
101
|
+
license_key:,
|
102
|
+
search:nil,
|
103
|
+
skip:nil,
|
104
|
+
take:nil,
|
105
|
+
workspace_id:nil
|
106
|
+
)
|
107
|
+
|
108
|
+
request(
|
109
|
+
:get,
|
110
|
+
"/rest/batches",
|
111
|
+
query_params: {
|
112
|
+
endUserId: end_user_id, # Valid endUserId for the Workspace
|
113
|
+
environmentId: environment_id, # Valid environmentId for the Workspace
|
114
|
+
licenseKey: license_key, # A valid licenseKey for the Workspace
|
115
|
+
search: search, # Searches fileName, originalFile, memo
|
116
|
+
skip: skip, # The rows to skip before listing
|
117
|
+
take: take, # The maximum number of rows to return
|
118
|
+
workspaceId: workspace_id, # Valid workspaceId for the Workspace
|
119
|
+
},
|
120
|
+
)
|
121
|
+
end
|
122
|
+
|
123
|
+
def file_upload_meta_data(
|
124
|
+
batch_id:
|
125
|
+
)
|
126
|
+
|
127
|
+
request(
|
128
|
+
:get,
|
129
|
+
"/rest/batch/%<batchId>s",
|
130
|
+
path_params: {
|
131
|
+
batchId: batch_id, # A valid UUID
|
132
|
+
},
|
133
|
+
)
|
134
|
+
end
|
135
|
+
|
136
|
+
def sheet_name_for_file_upload(
|
137
|
+
upload_id:,
|
138
|
+
license_key:nil
|
139
|
+
)
|
140
|
+
|
141
|
+
request(
|
142
|
+
:get,
|
143
|
+
"/upload/%<uploadId>s/dataSources",
|
144
|
+
path_params: {
|
145
|
+
uploadId: upload_id, # A valid UUID
|
146
|
+
},
|
147
|
+
query_params: {
|
148
|
+
licenseKey: license_key, # A valid licenseKey for the Workspace
|
149
|
+
},
|
150
|
+
)
|
151
|
+
end
|
152
|
+
|
153
|
+
def records_for_file_upload(
|
154
|
+
batch_id:,
|
155
|
+
created_at_end_date:nil,
|
156
|
+
created_at_start_date:nil,
|
157
|
+
deleted:nil,
|
158
|
+
skip:nil,
|
159
|
+
take:nil,
|
160
|
+
updated_at_end_date:nil,
|
161
|
+
updated_at_start_date:nil,
|
162
|
+
valid:nil
|
163
|
+
)
|
164
|
+
|
165
|
+
request(
|
166
|
+
:get,
|
167
|
+
"/rest/batch/%<batchId>s/rows",
|
168
|
+
path_params: {
|
169
|
+
batchId: batch_id, # A valid UUID
|
170
|
+
},
|
171
|
+
query_params: {
|
172
|
+
createdAtEndDate: created_at_end_date, # The maximum createdAt date to return
|
173
|
+
createdAtStartDate: created_at_start_date, # The minimum createdAt date to return
|
174
|
+
deleted: deleted, # Return only deleted rows
|
175
|
+
skip: skip, # The rows to skip before listing
|
176
|
+
take: take, # The maximum number of rows to return
|
177
|
+
updatedAtEndDate: updated_at_end_date, # The maximum updatedAt date to return
|
178
|
+
updatedAtStartDate: updated_at_start_date, # The minimum updatedAt date to return
|
179
|
+
valid: valid, # Return only valid rows
|
180
|
+
},
|
181
|
+
)
|
182
|
+
end
|
183
|
+
|
184
|
+
def upload_to_workspace_sheet(
|
185
|
+
sheet_id:,
|
186
|
+
workspace_id:
|
187
|
+
)
|
188
|
+
|
189
|
+
request(
|
190
|
+
:post,
|
191
|
+
"/workspace/%<workspaceId>s/sheet/%<sheetId>s/data",
|
192
|
+
path_params: {
|
193
|
+
sheetId: sheet_id, # A valid UUID
|
194
|
+
workspaceId: workspace_id, # A valid UUID
|
195
|
+
},
|
196
|
+
)
|
197
|
+
end
|
198
|
+
|
199
|
+
def fetch_workspace_sheet_records(
|
200
|
+
sheet_id:,
|
201
|
+
workspace_id:,
|
202
|
+
filter:nil,
|
203
|
+
merge_id:nil,
|
204
|
+
nested:nil,
|
205
|
+
record_ids:nil,
|
206
|
+
skip:nil,
|
207
|
+
take:nil,
|
208
|
+
valid:nil
|
209
|
+
)
|
210
|
+
|
211
|
+
request(
|
212
|
+
:get,
|
213
|
+
"/workspace/%<workspaceId>s/sheet/%<sheetId>s/records",
|
214
|
+
path_params: {
|
215
|
+
sheetId: sheet_id, # A valid UUID
|
216
|
+
workspaceId: workspace_id, # A valid UUID
|
217
|
+
},
|
218
|
+
query_params: {
|
219
|
+
filter: filter, # Return only the filtered rows
|
220
|
+
mergeId: merge_id,
|
221
|
+
nested: nested,
|
222
|
+
recordIds: record_ids,
|
223
|
+
skip: skip, # The rows to skip before listing
|
224
|
+
take: take, # The maximum number of rows to return
|
225
|
+
valid: valid, # Return only valid rows
|
226
|
+
},
|
227
|
+
)
|
228
|
+
end
|
229
|
+
|
230
|
+
def list_team_workspaces(
|
231
|
+
team_id:,
|
232
|
+
environment_id:nil,
|
233
|
+
skip:nil,
|
234
|
+
take:nil
|
235
|
+
)
|
236
|
+
|
237
|
+
request(
|
238
|
+
:get,
|
239
|
+
"/rest/teams/%<teamId>s/workspaces",
|
240
|
+
path_params: {
|
241
|
+
teamId: team_id,
|
242
|
+
},
|
243
|
+
query_params: {
|
244
|
+
environmentId: environment_id, # Valid environmentId for the Workspace
|
245
|
+
skip: skip, # The rows to skip before listing
|
246
|
+
take: take, # The maximum number of rows to return
|
247
|
+
},
|
248
|
+
)
|
249
|
+
end
|
250
|
+
|
251
|
+
def detail_workspace(
|
252
|
+
workspace_id:
|
253
|
+
)
|
254
|
+
|
255
|
+
request(
|
256
|
+
:get,
|
257
|
+
"/rest/workspace/%<workspaceId>s",
|
258
|
+
path_params: {
|
259
|
+
workspaceId: workspace_id, # A valid UUID
|
260
|
+
},
|
261
|
+
)
|
262
|
+
end
|
263
|
+
|
264
|
+
def invite_workspace_collaborator(
|
265
|
+
team_id:,
|
266
|
+
workspace_id:,
|
267
|
+
email:
|
268
|
+
)
|
269
|
+
|
270
|
+
request(
|
271
|
+
:post,
|
272
|
+
"/rest/teams/%<teamId>s/workspaces/%<workspaceId>s/invitations",
|
273
|
+
path_params: {
|
274
|
+
teamId: team_id,
|
275
|
+
workspaceId: workspace_id, # A valid UUID
|
276
|
+
},
|
277
|
+
body_params: {
|
278
|
+
email: email, # Email address of invited collaborator
|
279
|
+
},
|
280
|
+
)
|
281
|
+
end
|
282
|
+
|
283
|
+
def list_workspace_invitations(
|
284
|
+
team_id:,
|
285
|
+
workspace_id:
|
286
|
+
)
|
287
|
+
|
288
|
+
request(
|
289
|
+
:get,
|
290
|
+
"/rest/teams/%<teamId>s/workspaces/%<workspaceId>s/invitations",
|
291
|
+
path_params: {
|
292
|
+
teamId: team_id,
|
293
|
+
workspaceId: workspace_id, # A valid UUID
|
294
|
+
},
|
295
|
+
)
|
296
|
+
end
|
297
|
+
|
298
|
+
def list_workspace_collaborators(
|
299
|
+
team_id:,
|
300
|
+
workspace_id:
|
301
|
+
)
|
302
|
+
|
303
|
+
request(
|
304
|
+
:get,
|
305
|
+
"/rest/teams/%<teamId>s/workspaces/%<workspaceId>s/collaborators",
|
306
|
+
path_params: {
|
307
|
+
teamId: team_id,
|
308
|
+
workspaceId: workspace_id, # A valid UUID
|
309
|
+
},
|
310
|
+
)
|
311
|
+
end
|
312
|
+
|
313
|
+
def revoke_workspace_invitation(
|
314
|
+
team_id:,
|
315
|
+
workspace_id:,
|
316
|
+
email:
|
317
|
+
)
|
318
|
+
|
319
|
+
request(
|
320
|
+
:delete,
|
321
|
+
"/rest/teams/%<teamId>s/workspaces/%<workspaceId>s/invitations",
|
322
|
+
path_params: {
|
323
|
+
teamId: team_id,
|
324
|
+
workspaceId: workspace_id, # A valid UUID
|
325
|
+
},
|
326
|
+
body_params: {
|
327
|
+
email: email, # Email address of invited collaborator
|
328
|
+
},
|
329
|
+
)
|
330
|
+
end
|
331
|
+
|
332
|
+
def remove_workspace_collaborator(
|
333
|
+
team_id:,
|
334
|
+
user_id:,
|
335
|
+
workspace_id:,
|
336
|
+
email:
|
337
|
+
)
|
338
|
+
|
339
|
+
request(
|
340
|
+
:delete,
|
341
|
+
"/rest/teams/%<teamId>s/workspaces/%<workspaceId>s/collaborators/%<userId>s",
|
342
|
+
path_params: {
|
343
|
+
teamId: team_id,
|
344
|
+
userId: user_id,
|
345
|
+
workspaceId: workspace_id, # A valid UUID
|
346
|
+
},
|
347
|
+
body_params: {
|
348
|
+
email: email, # Email address of collaborator
|
349
|
+
},
|
350
|
+
)
|
351
|
+
end
|
352
|
+
|
353
|
+
# def inspect
|
354
|
+
# "#<#{self.class}:#{object_id}>"
|
355
|
+
# end
|
356
|
+
|
357
|
+
private
|
358
|
+
|
359
|
+
def ouroboros(node)
|
360
|
+
case node
|
361
|
+
when Array
|
362
|
+
node.map { |v| ouroboros(v) }
|
363
|
+
when Hash
|
364
|
+
node.each_with_object({}) { |(k, v), o| o[k.underscore.to_sym] = ouroboros(v) }
|
365
|
+
else
|
366
|
+
node
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
def request(method, path, path_params: {}, body_params: {}, query_params: nil)
|
371
|
+
uri = URI("#{PROTO}://#{HOST}")
|
372
|
+
uri.path = path % path_params
|
373
|
+
uri.query = URI.encode_www_form(query_params.compact) if query_params
|
374
|
+
request_uri(
|
375
|
+
method,
|
376
|
+
uri,
|
377
|
+
path_params: path_params,
|
378
|
+
body_params: body_params,
|
379
|
+
query_params: query_params
|
380
|
+
)
|
381
|
+
end
|
382
|
+
|
383
|
+
def request_uri(method, uri, path_params: {}, body_params: {}, query_params: nil, redirects: 0)
|
384
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
385
|
+
http.use_ssl = true
|
386
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
387
|
+
|
388
|
+
case method
|
389
|
+
when :post
|
390
|
+
request = Net::HTTP::Post.new uri
|
391
|
+
request['Content-Type'] = 'application/json; charset=UTF-8'
|
392
|
+
request.body = JSON.generate body_params.compact
|
393
|
+
when :get
|
394
|
+
request = Net::HTTP::Get.new uri
|
395
|
+
when :delete
|
396
|
+
request = Net::HTTP::Delete.new uri
|
397
|
+
else
|
398
|
+
raise NotImplementedError.new "Bad http method: #{method}"
|
399
|
+
end
|
400
|
+
|
401
|
+
# puts "\e[1;32mDespatching request to #{uri.path}\e[0m" if @debug
|
402
|
+
|
403
|
+
request["X-Api-Key"] = "#{@access_key_id}+#{@secret_access_key}"
|
404
|
+
|
405
|
+
@session = @agent.start unless @agent.started?
|
406
|
+
response = @session.request request
|
407
|
+
|
408
|
+
# puts "\e[1;32mResponse received\e[0m" if @debug
|
409
|
+
|
410
|
+
case response
|
411
|
+
when Net::HTTPOK
|
412
|
+
# Continue
|
413
|
+
when Net::HTTPRedirection
|
414
|
+
raise "Too many redirects" if redirects > @redirect_limit
|
415
|
+
# It's probably a file
|
416
|
+
next_uri = URI response['location']
|
417
|
+
return request_uri(method, next_uri, redirects: redirects + 1)
|
418
|
+
else
|
419
|
+
raise Error.new "Error response from #{uri} -> #{response.read_body}"
|
420
|
+
end
|
421
|
+
|
422
|
+
case response['content-type']
|
423
|
+
when /^application\/json/
|
424
|
+
data = ouroboros JSON.parse response.read_body
|
425
|
+
when /^text\/csv/, /^application\//
|
426
|
+
return Response.new(response.body)
|
427
|
+
else
|
428
|
+
raise NotImplementedError.new "Don't know how to parse #{response['content-type']}"
|
429
|
+
end
|
430
|
+
|
431
|
+
if data.key?(:pagination)
|
432
|
+
PaginatedResponse.new(
|
433
|
+
data,
|
434
|
+
self,
|
435
|
+
method,
|
436
|
+
uri.path,
|
437
|
+
path_params: path_params,
|
438
|
+
body_params: body_params,
|
439
|
+
query_params: query_params
|
440
|
+
)
|
441
|
+
else
|
442
|
+
Response.new data
|
443
|
+
end
|
444
|
+
|
445
|
+
end
|
446
|
+
|
447
|
+
end
|
data/scripts/code_gen.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
endpoints = YAML.load_file('spec_2022-12-02.yaml')
|
4
|
+
|
5
|
+
def camel_to_snake(key)
|
6
|
+
key.gsub(/(?<=[a-z0-9])([A-Z])/) { "_#{$1}" }.downcase
|
7
|
+
end
|
8
|
+
|
9
|
+
def snake_to_camel(key)
|
10
|
+
key.gsub(/_([a-z0-9])/) { $1.upcase }
|
11
|
+
end
|
12
|
+
|
13
|
+
endpoints.each do |endpoint|
|
14
|
+
method_name = endpoint[:description].gsub(/[^a-z0-9_]/i, '_').downcase
|
15
|
+
combined_params = endpoint.values_at(
|
16
|
+
:path_params,
|
17
|
+
:query_params,
|
18
|
+
:body_params
|
19
|
+
).compact.flatten.each_with_object({}) do |param, o|
|
20
|
+
key = camel_to_snake param['Name']
|
21
|
+
puts "\e[31mOverwriting key: #{key}\e[0m" if o.key? key
|
22
|
+
o[camel_to_snake param['Name']] = /true/i === param['Required']
|
23
|
+
end
|
24
|
+
|
25
|
+
print "def #{method_name}("
|
26
|
+
puts "" if combined_params.any?
|
27
|
+
puts(combined_params.map do |k, v|
|
28
|
+
" #{k}:#{v ? '' : 'nil'}"
|
29
|
+
end.join(",\n"))
|
30
|
+
puts ")"
|
31
|
+
puts ""
|
32
|
+
|
33
|
+
path = endpoint[:path].gsub(/\/:([a-z]+)\b/i) { ?/ + "\%<#{$1}>s" }
|
34
|
+
|
35
|
+
puts " request("
|
36
|
+
puts " :#{endpoint[:method].downcase},"
|
37
|
+
puts " \"#{path}\","
|
38
|
+
|
39
|
+
[:path_params, :query_params, :body_params].each do |fields|
|
40
|
+
next unless endpoint.key? fields
|
41
|
+
puts " #{fields}: {"
|
42
|
+
endpoint[fields].each do |param|
|
43
|
+
key = camel_to_snake param["Name"]
|
44
|
+
print " #{param["Name"]}: #{key},"
|
45
|
+
print " # #{param["Description"]}" if param["Description"].length > 1
|
46
|
+
puts ""
|
47
|
+
end
|
48
|
+
puts " },"
|
49
|
+
end
|
50
|
+
puts " )"
|
51
|
+
puts "end"
|
52
|
+
puts ""
|
53
|
+
|
54
|
+
end
|
data/scripts/doc_gen.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
endpoints = YAML.load_file('spec_2022-12-02.yaml')
|
4
|
+
|
5
|
+
def camel_to_snake(key)
|
6
|
+
key.gsub(/(?<=[a-z0-9])([A-Z])/) { "_#{$1}" }.downcase
|
7
|
+
end
|
8
|
+
|
9
|
+
def snake_to_camel(key)
|
10
|
+
key.gsub(/_([a-z0-9])/) { $1.upcase }
|
11
|
+
end
|
12
|
+
|
13
|
+
endpoints.each do |endpoint|
|
14
|
+
method_name = endpoint[:description].gsub(/[^a-z0-9_]/i, '_').downcase
|
15
|
+
|
16
|
+
puts "\n\n### #{method_name}\n"
|
17
|
+
combined_params = endpoint.values_at(
|
18
|
+
:path_params,
|
19
|
+
:query_params,
|
20
|
+
:body_params
|
21
|
+
).compact.flatten
|
22
|
+
|
23
|
+
combined_params.sort_by { |param|
|
24
|
+
camel_to_snake param['Name']
|
25
|
+
}.tap { |params|
|
26
|
+
if params.any?
|
27
|
+
puts "| Name | Required | Type | Value | Description |"
|
28
|
+
puts "| ---- | -------- | ---- | ----- | ----------- |"
|
29
|
+
end
|
30
|
+
}.each do |param, o|
|
31
|
+
key = camel_to_snake param['Name']
|
32
|
+
puts "| #{key} | #{param['Required']} | #{param['Type']} | #{param['Value']} | #{param['Description']} |"
|
33
|
+
end
|
34
|
+
|
35
|
+
paginated = combined_params.map { |x| x['Name'] }.include? 'take'
|
36
|
+
puts "\nResponse: `FlatfileApi::" + ( paginated ? 'Paginated' : '' ) + 'Response`'
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'yaml'
|
3
|
+
require 'open-uri'
|
4
|
+
|
5
|
+
def body_params?(pointer)
|
6
|
+
/Request Body:/i === pointer.text
|
7
|
+
end
|
8
|
+
|
9
|
+
def example_response?(pointer)
|
10
|
+
/Example Reponse:/i === pointer.text
|
11
|
+
end
|
12
|
+
|
13
|
+
def extract_header(pointer)
|
14
|
+
/(POST|GET|DELETE|PATCH|PUT)\s+(.+)/ === pointer.text.strip.gsub(/[\u200B-\u200D\uFEFF]/, '')
|
15
|
+
$~.captures
|
16
|
+
end
|
17
|
+
|
18
|
+
def extract_params(pointer)
|
19
|
+
header, *rows = pointer.css('tr')
|
20
|
+
headers = header.css('th').map(&:text)
|
21
|
+
rows.each_with_object([]) do |row, o|
|
22
|
+
o << Hash[headers.zip(row.css('td').map(&:text))]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def extract_path(pointer)
|
27
|
+
/Endpoint:\s+(.*)/i === pointer.text&.strip
|
28
|
+
$~.captures.first
|
29
|
+
end
|
30
|
+
|
31
|
+
def header?(pointer)
|
32
|
+
/POST|GET|DELETE|PATCH|PUT/ === pointer.at_css('code')&.text
|
33
|
+
end
|
34
|
+
|
35
|
+
def param_table?(pointer)
|
36
|
+
/Name/i === pointer.at_css('th')&.text
|
37
|
+
end
|
38
|
+
|
39
|
+
def path?(pointer)
|
40
|
+
/Endpoint:\s+(.*)/i === pointer.text
|
41
|
+
end
|
42
|
+
|
43
|
+
def path_params?(pointer)
|
44
|
+
/Path Variables:/i === pointer.text
|
45
|
+
end
|
46
|
+
|
47
|
+
def query_params?(pointer)
|
48
|
+
/Query Params:/i === pointer.text
|
49
|
+
end
|
50
|
+
|
51
|
+
url = "https://flatfile.com/docs/api-reference/"
|
52
|
+
doc = Nokogiri::HTML(URI.open(url))
|
53
|
+
pointer = doc.at_css('#endpoints')
|
54
|
+
|
55
|
+
endpoints = []
|
56
|
+
context = nil
|
57
|
+
spec = nil
|
58
|
+
while pointer = pointer.next_sibling
|
59
|
+
case
|
60
|
+
when header?(pointer)
|
61
|
+
context = nil
|
62
|
+
spec = {}
|
63
|
+
endpoints << spec
|
64
|
+
spec[:method], spec[:description] = extract_header(pointer)
|
65
|
+
when path?(pointer)
|
66
|
+
spec[:path] = extract_path(pointer)
|
67
|
+
when body_params?(pointer)
|
68
|
+
context = :body_params
|
69
|
+
when path_params?(pointer)
|
70
|
+
context = :path_params
|
71
|
+
when query_params?(pointer)
|
72
|
+
context = :query_params
|
73
|
+
when param_table?(pointer)
|
74
|
+
spec[context] = extract_params(pointer)
|
75
|
+
context = nil
|
76
|
+
when example_response?(pointer)
|
77
|
+
context = nil
|
78
|
+
else
|
79
|
+
# puts "Don't know how to handle: #{pointer.text}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
puts endpoints.to_yaml
|