|
| 1 | +require 'uri' |
| 2 | +require 'net/http' |
| 3 | +require 'json' |
| 4 | +require 'yaml' |
| 5 | + |
| 6 | +def slugify(str) |
| 7 | + str.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '') |
| 8 | +end |
| 9 | + |
| 10 | +# Used to standardize the description format. |
| 11 | +def add_period_if_missing(string) |
| 12 | + terminal_punctuation = [".", "!", "?"].freeze |
| 13 | + string << "." unless string.end_with?(*terminal_punctuation) |
| 14 | + string |
| 15 | +end |
| 16 | + |
| 17 | +# Removes Ruby symbol colons from keys (for YAML) |
| 18 | +def clear_keys(hash) |
| 19 | + hash.map do |k, v| |
| 20 | + k = k.to_s |
| 21 | + if v.is_a?(Hash) |
| 22 | + v = clear_keys(v) |
| 23 | + elsif v.is_a?(Array) |
| 24 | + v = v.map do |item| |
| 25 | + i = clear_keys(item) if item.is_a?(Hash) |
| 26 | + i |
| 27 | + end |
| 28 | + end |
| 29 | + |
| 30 | + [k, v] |
| 31 | + end.to_h |
| 32 | +end |
| 33 | + |
| 34 | +# Check for ENV vars |
| 35 | +["NOTION_TOKEN", "NOTION_DB_ID", "TARGET_PATH"].each do |v| |
| 36 | + raise "Need to provide #{v} as an environment variable for this to work." if ENV[v].nil? |
| 37 | +end |
| 38 | + |
| 39 | +# Preps the request |
| 40 | +uri = URI("https://api.notion.com/v1/databases/#{ENV['NOTION_DB_ID']}/query") |
| 41 | +http = Net::HTTP.new(uri.host, uri.port) |
| 42 | +http.use_ssl = true |
| 43 | + |
| 44 | +request = Net::HTTP::Post.new(uri.request_uri) |
| 45 | +request["Authorization"] = "Bearer #{ENV['NOTION_TOKEN']}" |
| 46 | +request["Notion-Version"] = "2022-06-28" |
| 47 | +request["Accept"] = "application/json" |
| 48 | +request["Content-Type "] = "application/json" |
| 49 | +response = http.request(request) |
| 50 | + |
| 51 | +# If it's not a failure |
| 52 | +if response.is_a?(Net::HTTPSuccess) |
| 53 | + puts "Roadmap data loaded. Processing..." |
| 54 | + |
| 55 | + # Parses the response |
| 56 | + content = JSON.parse(response.body) |
| 57 | + |
| 58 | + # Preps the object to be serialized later |
| 59 | + output = { |
| 60 | + :now => [], |
| 61 | + :soon => [], |
| 62 | + :later => [] |
| 63 | + } |
| 64 | + |
| 65 | + # Maps each row to a column, with properly transformed properties |
| 66 | + content["results"].each do |row| |
| 67 | + group = nil |
| 68 | + entry = {} |
| 69 | + |
| 70 | + row["properties"].each_pair do |name, val| |
| 71 | + case name |
| 72 | + when "Status" then group = slugify(row["properties"]["Status"]["select"]["name"]).to_sym # Target for later |
| 73 | + when "Title" then entry[:title] = val["title"][0]["text"]["content"] |
| 74 | + when "Description" then entry[:description] = add_period_if_missing(val["rich_text"][0]["text"]["content"]) unless val["rich_text"].empty? |
| 75 | + when "Release" then entry[:release] = val["select"]["name"] unless val["select"].empty? |
| 76 | + when "Paid" then entry[:paid] = val["checkbox"] |
| 77 | + end |
| 78 | + end |
| 79 | + |
| 80 | + output[group] = [] if output[group].nil? |
| 81 | + output[group] << entry |
| 82 | + end |
| 83 | + |
| 84 | + # Only select the statuses we need for the roadmap |
| 85 | + output.select! { |key, _| [:now, :soon, :later].include?(key) } |
| 86 | + |
| 87 | + # Sort by paid, then alphabetically by title |
| 88 | + [:now, :soon, :later].each do |group| |
| 89 | + output[group].sort_by! { |entry| [entry[:paid] ? 0 : 1, entry[:title]] } |
| 90 | + end |
| 91 | + |
| 92 | + # Add one-by-two class to now, soon, and later |
| 93 | + [:now, :soon, :later].each do |group| |
| 94 | + # The `one-by-two` class is used to style these two sections |
| 95 | + output[group].each do |entry| |
| 96 | + entry[:class] = "one-by-two" |
| 97 | + end |
| 98 | + end |
| 99 | + |
| 100 | + # Emtpy the target file and write the serialized YAML to it |
| 101 | + output = clear_keys(output) |
| 102 | + File.open(ENV['TARGET_PATH'], 'w') do |file| |
| 103 | + file.truncate 0 |
| 104 | + file.write output.to_yaml |
| 105 | + end |
| 106 | + |
| 107 | + # Call it a day |
| 108 | + puts "Roadmap data written to #{ENV['TARGET_PATH']}." |
| 109 | +else |
| 110 | + # This should prevent a broken roadmap version to be checked in |
| 111 | + raise "Could not get database from Notion" |
| 112 | +end |
0 commit comments