TimPetricola/TLD-Hunt

View on GitHub
api.rb

Summary

Maintainability
A
0 mins
Test Coverage
require 'sinatra'
require 'json'
require 'mongoid'
require 'uri'
require 'rest_client'

Mongoid.load!('mongoid.yml')

class Product
  include Mongoid::Document

  URLS_BLACKLIST = %w(
    bit.ly
    chrome.google.com/webstore
    github.io
    goo.gl
    herokuapp.com
    itunes.apple.com
    play.google.com
  )

  field :name, type: String
  field :url, type: String
  field :tagline, type: String
  field :product_hunt_id, type: Integer
  field :product_hunt_url, type: String

  validates :name, presence: true
  validates :url, presence: true, uniqueness: true
  validate :url_not_blacklisted

  def hostname
    URI.parse(url).host.gsub('www.', '')
  end

  def self.with_tld(tld)
    tld = sanitize_tld(tld).gsub('.', '\\.')
    where(url: Regexp.new("\\.#{tld}(\\/.*)?$"))
  end

  private

  def url_not_blacklisted
    URLS_BLACKLIST.each do |blacklisted|
      next unless url.downcase.include?(blacklisted)
      return errors.add(:url, "URLs from #{blacklisted} are blacklisted")
    end
  end
end

class Fetcher
  ENDPOINT = 'https://api.producthunt.com/v1/posts'

  attr_reader :products

  def initialize(date = DateTime.now)
    @date = date
    @results = false
  end

  def fetch
    products_from_response(api_call)
  end

  def results?
    @results
  end

  private

  def api_call
    params = {
      access_token: ENV['PRODUCT_HUNT_ACCESS_TOKEN'],
      day: @date.strftime('%Y-%m-%d')
    }
    RestClient::Resource.new(ENDPOINT, verify_ssl: false).get(params: params)
  end

  def products_from_response(response)
    raw = JSON.parse(response.body)['posts']
    @results = raw.length > 0

    @products = raw.map do |detail|
     Product.create(
        name: detail['name'],
        url: url_from_detail(detail),
        tagline: detail['tagline'],
        product_hunt_id: detail['id'],
        product_hunt_url: detail['discussion_url']
      )
    end
    @products = products.select(&:persisted?)
  end

  def url_from_detail(detail)
    screenshot_params = URI.parse(detail['screenshot_url'].values.last).query
    URI::decode_www_form(screenshot_params).to_h['url']
  end
end

def sanitize_tld(tld)
  tld.to_s.downcase.gsub(/\.\.+/, '.').gsub(/^\./, '').gsub(/[^a-z\.]/, '')
end

I18n.enforce_available_locales = true
set :static_cache_control, [:public, max_age: 31536000] # 1 year

get '/tld/:tld' do
  content_type :json
  cache_control :public, max_age: 86400 # 24 hours

  limit = 100
  skip = (params[:offset] || 0).to_i

  tld = sanitize_tld(params[:tld])
  products = Product.with_tld(tld)

  response = {
    tld: tld,
    total_count: products.count
  }

  response[:products] = products.limit(limit).skip(skip).map do |product|
    {
      name: product.name,
      url: product.url,
      hostname: product.hostname
    }
  end

  response.to_json
end

get '/' do
  erb :index
end