flatfile_api 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|