Skip to content

Commit 84e1701

Browse files
committed
Fix: crawn the new docusaurus site.
1 parent 632f481 commit 84e1701

File tree

4 files changed

+175
-83
lines changed

4 files changed

+175
-83
lines changed

Rakefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ require 'fileutils'
1010
Bundler.setup :default, :development
1111

1212
load 'tasks/update.rake'
13+
load 'tasks/download.rake'

tasks/download.rake

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# frozen_string_literal: true
2+
3+
require_relative 'lib/docs/downloader'
4+
5+
namespace :api do
6+
namespace :ref do
7+
desc 'Download JSON reference.'
8+
task :download do
9+
Rake::Task['api:ref:clean_files'].invoke('docs.slack.dev')
10+
downloader = SlackApi::Docs::Downloader.new
11+
downloader.download!
12+
puts "\nFinished downloading reference."
13+
end
14+
15+
desc 'Delete all generated files except undocumented ones.'
16+
task :clean_files, :dirs do |_t, args|
17+
files = Dir["./{#{Array(args[:dirs]).join(',')}}/*"].grep_v(%r{/undocumented\b})
18+
FileUtils.rm_rf files
19+
end
20+
end
21+
end

tasks/lib/docs/downloader.rb

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
module SlackApi
2+
module Docs
3+
class Downloader
4+
def target_path
5+
@target_path ||= File.expand_path('../../../docs.slack.dev', __dir__)
6+
end
7+
8+
def events_dir
9+
File.join(target_path, 'events')
10+
end
11+
12+
def events_path
13+
File.join(events_dir, 'events.json')
14+
end
15+
16+
def methods_dir
17+
File.join(target_path, 'methods')
18+
end
19+
20+
def methods_path
21+
File.join(methods_dir, 'methods.json')
22+
end
23+
24+
def events_url
25+
'https://docs.slack.dev/reference/events.json'
26+
end
27+
28+
def methods_url
29+
'https://docs.slack.dev/reference/methods.json'
30+
end
31+
32+
def method_url(method)
33+
"https://docs.slack.dev/reference/methods/#{method}.json"
34+
end
35+
36+
def method_target_path(method_name)
37+
File.join(methods_dir, "#{method_name}.json")
38+
end
39+
40+
def download!
41+
download_methods!
42+
download_events!
43+
end
44+
45+
def download_methods!
46+
puts "#{methods_url} => #{methods_path}"
47+
FileUtils.mkdir_p(methods_dir)
48+
URI.open(methods_url) do |file|
49+
json = JSON.parse(file.read)
50+
File.write(methods_path, JSON.pretty_generate(json))
51+
json.each do |method|
52+
method_name = method['name']
53+
method_url = method_url(method_name)
54+
method_target_path = method_target_path(method_name)
55+
puts "#{method_url} => #{method_target_path}"
56+
URI.open(method_url) do |method_file|
57+
method_json = JSON.parse(method_file.read)
58+
File.write(method_target_path, JSON.pretty_generate(method_json))
59+
end
60+
end
61+
end
62+
end
63+
64+
def download_events!
65+
puts "#{events_url} => #{events_path}"
66+
URI.open(events_url) do |file|
67+
json = JSON.parse(file.read)
68+
FileUtils.mkdir_p(events_dir)
69+
File.write(events_path, JSON.pretty_generate(json))
70+
end
71+
end
72+
end
73+
end
74+
end

tasks/lib/slack_api/methods_spider.rb

Lines changed: 79 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,26 @@ module SlackApi
55
class MethodsSpider < BaseSpider
66
handle 'https://api.slack.com/methods', :process_list
77

8+
def downloader
9+
@downloader ||= SlackApi::Docs::Downloader.new
10+
end
11+
812
def process_list(page, _default_data = {})
9-
methods_page = ensure!(page, '.apiMethodPage')
10-
list = methods_page.search('.apiMethodPage__methodList')
11-
ref = list.search('[data-automount-component=ApiDocsFilterableReferenceList]')
12-
data = JSON.parse(ref.attribute('data-automount-props'))
13-
raise(ElementNotFound, 'Could not parse methods reference') unless data['items'].any?
13+
methods = JSON.load_file(downloader.methods_path)
1414

1515
groups = Set.new
16-
data['items'].each do |method|
17-
next unless method['isPublic']
18-
next if method['isDeprecated']
1916

20-
groups += method['groups']
17+
methods.each do |method|
18+
groups += method['family']
2119
method_name = method['name']
22-
method_group = method['groups'].first.split('.').first
20+
method_group = method['family'].first.split('.').first
21+
downloaded_filename = downloader.method_target_path(method_name)
2322
file_name = "methods/#{method_group}/#{method_name}.json"
24-
method_url = resolve_url(method['link'], page)
23+
method_url = resolve_url(downloader.method_url(method_name), page)
2524
handle method_url,
2625
:process_method,
2726
filename: file_name,
27+
downloaded_filename: downloaded_filename,
2828
method_name: method_name,
2929
method_group: method_group,
3030
method_url: method_url
@@ -37,14 +37,15 @@ def process_list(page, _default_data = {})
3737
end
3838
end
3939

40-
def process_method(page, default_data = {})
41-
method_page = ensure!(page, '.apiMethodPage', default_data[:method_name])
42-
desc = method_page.search('.apiReference__mainDescription').text.gsub('’', "'")
43-
return if desc.downcase.start_with? 'deprecated:'
40+
def process_method(method_page, default_data = {})
41+
method_data = JSON.load_file(default_data[:downloaded_filename])
42+
43+
desc = method_data['desc'].gsub('’', "'")
44+
return if desc.downcase.start_with? 'Deprecated:'
4445

45-
args, arg_groups, fields = parse_args(method_page, default_data)
46-
errors = parse_errors(method_page, default_data)
47-
response = parse_response(method_page, default_data)
46+
args, arg_groups, fields = parse_args(method_page, method_data)
47+
errors = parse_errors(method_page, method_data)
48+
response = parse_response(method_page, method_data)
4849

4950
json_hash = {
5051
'group' => default_data[:method_group],
@@ -62,24 +63,28 @@ def process_method(page, default_data = {})
6263

6364
private
6465

65-
def parse_args(api_page, default_data = {})
66-
args_wrapper = ensure!(api_page, '.apiReference__arguments', default_data[:method_name])
67-
rows = args_wrapper.search('.apiMethodPage__argumentRow')
66+
def parse_args(_api_page, method_data = {})
6867
args = {}
6968
fields = {}
70-
rows.each do |row|
71-
name = row.search('.apiMethodPage__argument code').text
72-
type = massage_type(name,
73-
row.search('.apiMethodPage__argumentType').text,
74-
default_data)
75-
required = row.search('.apiMethodPage__argumentOptionality--required').any?
76-
77-
desc = row.search('.apiMethodPage__argumentDesc p')
78-
.text
69+
method_data['args']['properties'].each_pair do |name, arg|
70+
arg['anyOf']&.each do |coll|
71+
all = []
72+
k = coll.first.key
73+
coll.each_pair do |_k, v|
74+
all << v
75+
end
76+
arg[k] = all
77+
end
78+
arg.delete('anyOf')
79+
80+
type = massage_type(name, arg, method_data)
81+
required = method_data['args']['required'].include?(name)
82+
83+
desc = arg['desc']
7984
.tap { |t| t.slice!("\n") }
8085
.tap { |t| t << '.' unless t.end_with?('.') }
8186
.gsub('’', "'")
82-
example = row.search('.apiReference__exampleCode code').first&.text
87+
example = arg['example'] || arg['default']
8388

8489
case name
8590
when 'token'
@@ -98,89 +103,80 @@ def parse_args(api_page, default_data = {})
98103
end
99104
end
100105

101-
arg_groups = parse_arg_groups(args_wrapper)
106+
arg_groups = parse_arg_groups(_api_page, method_data)
102107

103-
[args, arg_groups, fields]
108+
[args.sort.to_h, arg_groups, fields]
104109
end
105110

106-
def parse_arg_groups(args_wrapper)
107-
# Look for groups of args that are interdependent
108-
groups = args_wrapper.search('.apiMethodPage__argumentGroup')
109-
groups = groups.map do |group|
110-
# "At least one of" or "One of"
111-
requirement = group.search('.apiMethodPage__argument em').text
112-
mutually_exclusive = requirement.downcase == 'one of'
113-
114-
desc = group.search('.apiMethodPage__argumentGroupDesc p')
115-
.text
116-
.tap { |t| t.slice!("\n") }
117-
.tap { |t| t << '.' unless t.end_with?('.') }
118-
.gsub('’', "'")
119-
120-
rows = group.search('.apiMethodPage__argumentRow')
121-
names = rows.map do |row|
122-
row.search('.apiMethodPage__argument code').text
123-
end
124-
125-
{
126-
'args' => names,
127-
'desc' => desc,
128-
'mutually_exclusive' => mutually_exclusive
129-
}
130-
end
111+
def parse_arg_groups(_api_page, _method_data = {})
112+
groups = {}
113+
114+
# # Look for groups of args that are interdependent
115+
# groups = args_wrapper.search('.apiMethodPage__argumentGroup')
116+
# groups = groups.map do |group|
117+
# # "At least one of" or "One of"
118+
# requirement = group.search('.apiMethodPage__argument em').text
119+
# mutually_exclusive = requirement.downcase == 'one of'
120+
121+
# desc = group.search('.apiMethodPage__argumentGroupDesc p')
122+
# .text
123+
# .tap { |t| t&.slice!("\n") }
124+
# .tap { |t| t << '.' unless t.end_with?('.') }
125+
# .gsub('’', "'")
126+
127+
# rows = group.search('.apiMethodPage__argumentRow')
128+
# names = rows.map do |row|
129+
# row.search('.apiMethodPage__argument code').text
130+
# end
131+
132+
# {
133+
# 'args' => names,
134+
# 'desc' => desc,
135+
# 'mutually_exclusive' => mutually_exclusive
136+
# }
137+
# end
131138

132139
groups unless groups.empty?
133140
end
134141

135-
def massage_type(name, detected, default_data = {})
142+
def massage_type(name, arg, data = {})
143+
return 'enum' if arg.key?('enum')
144+
136145
case name
137146
when 'date' then 'date'
138147
when 'latest', 'oldest', 'ts' then 'timestamp'
139148
when 'file' then 'file'
140149
when 'bot', 'user' then 'user'
141150
when 'channel'
142-
case default_data[:method_group]
151+
case data[:method_group]
143152
when 'im' then 'im'
144153
when 'mpim' then 'mpim'
145154
when 'groups' then 'group'
146155
else 'channel'
147156
end
148157
else
149-
case detected
158+
case detected = arg['type']
150159
when '', 'null' then nil
151160
else detected
152161
end
153162
end
154163
end
155164

156-
def parse_response(api_page, default_data = {})
157-
response_wrapper = ensure!(api_page, '.apiReference__response', default_data[:method_name])
158-
responses = response_wrapper.search('.apiReference__example')
165+
def parse_response(_api_page, data = {})
159166
examples = []
160-
responses.each do |response|
161-
response.search('pre').each do |pre|
162-
text = pre.text.strip
163-
next unless text =~ /^\{.*}$/m
164-
165-
examples.push(text)
166-
end
167+
data['examples']&.each_pair do |_example_type, response|
168+
example = JSON.pretty_generate(response['example'], indent: ' ')
169+
example.gsub!(/\{\n\s*\}/, '{}')
170+
example.gsub!(/\[\n\s*\]/, '[]')
171+
examples.push example
167172
end
168173
{ 'examples' => examples }
169174
end
170175

171-
def parse_errors(api_page, default_data = {})
172-
errors_wrapper = ensure!(api_page, '.apiReference__errors', default_data[:method_name])
173-
rows = errors_wrapper.search('.apiDocsTable tr')
176+
def parse_errors(_api_page, data = {})
174177
errors = {}
175-
rows.each do |row|
176-
next if row.search('th').any?
177-
178-
name = row.search('[data-label=Error]').text
179-
desc = row.search('[data-label=Description]').text
180-
.tap { |t| t.slice!("\n") }
181-
.tap { |t| t << '.' unless t.end_with?('.') }
182-
183-
errors[name] = desc
178+
data['errors']&.each_pair do |name, desc|
179+
errors[name] = desc['desc']
184180
end
185181
errors
186182
end

0 commit comments

Comments
 (0)