openaustralia/atdis

View on GitHub
lib/atdis/feed.rb

Summary

Maintainability
A
1 hr
Test Coverage
# frozen_string_literal: true

require "rest-client"

module ATDIS
  class Feed
    attr_reader :base_url, :timezone, :ignore_ssl_certificate

    VALID_OPTIONS = %i[page street suburb postcode lodgement_date_start
                       lodgement_date_end last_modified_date_start last_modified_date_end].freeze

    # base_url - the base url from which the urls for all atdis urls are made
    # It should be of the form:
    # http://www.council.nsw.gov.au/atdis/1.0
    # timezone - a string (e.g. "Sydney") for the timezone in which times are returned
    # (Note: times in the feeds that have timezones specified get converted to the
    # timezone given while times in the feed which don't have a timezone specified
    # get interpreted in the given timezone)
    # See https://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html for the
    # list of possible timezone strings
    def initialize(base_url, timezone, ignore_ssl_certificate = false)
      @base_url = base_url
      @timezone = timezone
      @ignore_ssl_certificate = ignore_ssl_certificate
    end

    def applications_url(options = {})
      invalid_options = options.keys - VALID_OPTIONS

      raise "Unexpected options used: #{invalid_options.join(',')}" unless invalid_options.empty?

      options[:street] = options[:street].join(",") if options[:street].respond_to?(:join)
      options[:suburb] = options[:suburb].join(",") if options[:suburb].respond_to?(:join)
      options[:postcode] = options[:postcode].join(",") if options[:postcode].respond_to?(:join)

      q = Feed.options_to_query(options)
      "#{base_url}/applications.json" + (q ? "?#{q}" : "")
    end

    def application_url(id)
      "#{base_url}/#{CGI.escape(id)}.json"
    end

    def self.base_url_from_url(url)
      u = URI.parse(url)
      options = query_to_options(u.query)
      VALID_OPTIONS.each do |o|
        options.delete(o)
      end
      u.query = options_to_query(options)
      u.fragment = nil
      u.path = "/" + u.path.split("/")[1..-2]&.join("/").to_s
      u.to_s
    end

    def self.options_from_url(url)
      u = URI.parse(url)
      options = query_to_options(u.query)
      %i[lodgement_date_start lodgement_date_end last_modified_date_start
         last_modified_date_end].each do |k|
        options[k] = Date.parse(options[k]) if options[k]
      end
      options[:page] = options[:page].to_i if options[:page]
      # Remove invalid options
      options.each_key do |key|
        options.delete(key) unless VALID_OPTIONS.include?(key)
      end
      options
    end

    def applications(options = {})
      Models::Page.read_url(applications_url(options), timezone, ignore_ssl_certificate)
    end

    def application(id)
      Models::Application.read_url(application_url(id), timezone, ignore_ssl_certificate)
    end

    # Turn a query string of the form "foo=bar&hello=sir" to {foo: "bar", hello: sir"}
    def self.query_to_options(query)
      options = {}
      (query || "").split("&").each do |t|
        key, value = t.split("=")
        options[key.to_sym] = (CGI.unescape(value) if value)
      end
      options
    end

    # Escape but leave commas unchanged (which are valid in query strings)
    def self.escape(value)
      CGI.escape(value.to_s).gsub("%2C", ",")
    end

    # Turn an options hash of the form {foo: "bar", hello: "sir"} into a query
    # string of the form "foo=bar&hello=sir"
    def self.options_to_query(options)
      if options.empty?
        nil
      else
        options.sort { |a, b| a.first.to_s <=> b.first.to_s }
               .map { |k, v| "#{k}=#{escape(v)}" }
               .join("&")
      end
    end
  end
end