lib/timber/cli/api.rb in timber-2.0.24 vs lib/timber/cli/api.rb in timber-2.1.0.rc1
- old
+ new
@@ -2,71 +2,135 @@
require "json"
require "net/https"
require "securerandom"
require "uri"
+require "timber/cli/api/application"
+require "timber/cli/io/messages"
+require "timber/version"
+
module Timber
class CLI
+ # The API class provides an interface for all Timber API requests, parsing response
+ # and returning the appropriate objects.
class API
+
+ # Raise when the API key provided is invalid.
class APIKeyInvalidError < StandardError
def message
- "Uh oh! The API key supplied is invalid. Please ensure that you copied the" \
- " key properly.\n\n#{Messages.obtain_key_instructions}"
+ "Uh oh! The API key supplied is invalid. Please ensure that you copied the \n" \
+ "key properly.\n\n#{IO::Messages.obtain_key_instructions}"
end
end
- class NoAPIKeyError < StandardError
+ class LogsNotReceivedError< StandardError
def message
- "Uh oh! You didn't supply an API key.\n\n#{Messages.obtain_key_instructions}"
+ "Bummer, we couldn't confirm log delivery with the Timber API, something is off. " \
+ "If you email support@timber.io, we'll work with you to figure out what's going on. " \
+ "And as a thank you sticking with us, we'll set you up with a 25% indefinite discount."
end
end
- TIMBER_API_URI = URI.parse('https://api.timber.io')
+ # Raised when Timber is returning 500s
+ class ServerError < StandardError
+ def message
+ "Crap, it looks like the Timber API is returning 500s :/. In order to properly " \
+ "install Timber and test integration we need the Timber API to work correctly. " \
+ "Chances are we're aware of the issue and if you try again later the API should " \
+ "be working. \n\n" \
+ "Status updates: http://status.timber.io \n" \
+ "Yell at us via email: support@timber.io \n"
+ end
+ end
+
+ # Raised when the API returns a response that a particular method is not expecting.
+ class UnrecognizedAPIResponse < StandardError
+ def initialize(res)
+ @res = res
+ end
+
+ def message
+ "Uh oh, we received a response from the Timber API that was not recognized " \
+ "(#{res.code}). We've been notified of the issue, but please feel free to " \
+ "yell at us via email to make sure we're aware: support@timber.io"
+ end
+ end
+
+ TIMBER_PRODUCTION_API_URL = "https://api.timber.io".freeze
+ TIMBER_STAGING_API_URL = "https://api.timber-staging.io".freeze
+ TIMBER_API_URL = ENV['TIMBER_STAGING'] ? TIMBER_STAGING_API_URL : TIMBER_PRODUCTION_API_URL
+ TIMBER_API_URI = URI.parse(TIMBER_API_URL)
APPLICATION_PATH = "/installer/application".freeze
EVENT_PATH = "/installer/events".freeze
HAS_LOGS_PATH = "/installer/has_logs".freeze
USER_AGENT = "Timber Ruby/#{Timber::VERSION} (HTTP)".freeze
+ attr_reader :api_key
+
def initialize(api_key)
@api_key = api_key
@session_id = SecureRandom.uuid
end
+ # Returns the application for the given API key.
def application!
- get!(APPLICATION_PATH)
+ res = get!(APPLICATION_PATH)
+ build_application(res)
end
- def event!(name, data = {})
+ # Hits the API to clone the app for the provided API key to the specified environment.
+ def clone_application!(environment)
+ return nil
+ end
+
+ # Sends an event to Timber so that we can understand how the installer is performing
+ # an ensure a top notch user experience. We do not raise here because it is not
+ # critical for the install process.
+ def event(name, data = {})
post!(EVENT_PATH, event: {name: name, data: data})
+ true
+ rescue Exception
+ false
end
+ # After test logs are sent to the Timber API this method waits for them to be
+ # received. This is how we test integration.
def wait_for_logs(iteration = 0, &block)
if block_given?
yield iteration
end
case iteration
when 0
- event!(:waiting_for_logs)
- when 30
- event!(:excessively_waiting_for_logs)
+ event(:waiting_for_logs)
+ when 20
+ event(:excessively_waiting_for_logs)
+ when 60
+ raise LogsNotReceivedError.new
end
sleep 0.5
res = get!(HAS_LOGS_PATH)
case res.code
when "202"
wait_for_logs(iteration + 1, &block)
-
when "204"
true
+ else
+ raise UnrecognizedAPIResponse.new(res)
end
end
private
+ def build_application(res)
+ parsed_body = JSON.parse(res.body)
+ attributes = parsed_body.fetch("data")
+ Application.new(attributes)
+ end
+
def get!(path)
req = Net::HTTP::Get.new(path)
issue!(req)
end
@@ -83,13 +147,15 @@
req['X-Installer-Session-Id'] = @session_id
res = http.start do |http|
http.request(req)
end
- if res.code == "401"
- raise NoAPIKeyError.new
- elsif res.code == "403"
+ code = Integer(res.code)
+
+ if [401, 403].include?(code)
raise APIKeyInvalidError.new
+ elsif code >= 500
+ raise ServerError.new
else
res
end
end
\ No newline at end of file