class Snitcher::API::Client

Constants

DEFAULT_ENDPOINT

Public Class Methods

new(key, options = {}) click to toggle source

Create a new API Client for taling to Dead Man's Snitch's API.

@param key [String] API access key (available at

https://deadmanssnitch.com/account/keys).

@param [Hash] options advanced options for customizing the client @option options [String] :endpoint URL of the DMS API to connect to @option options [Float, Fixnum] :timeout number of seconds to wait at most

for a response from the API.

@example Creating a new Client with an API key

client = Snitcher::API::Client.new("abc123")
# => #<Snitcher::API::Client...>

@return [Snitcher::API::Client] New API Client.

# File lib/snitcher/api/client.rb, line 31
def initialize(key, options = {})
  endpoint = options[:endpoint] || DEFAULT_ENDPOINT

  @key      = key
  @endpoint = URI.parse(endpoint).freeze
  @timeout  = options.fetch(:timeout, 5.0)
end

Public Instance Methods

add_tags(token, tags = []) click to toggle source

Add one or more tags to an existing snitch, identified by token.

@param token [String] The unique token of the Snitch. @param tags [Array<String>] Tag or tags to add to the list of tags already

on the Snitch.

@example Add tags to an existing snitch.

client.add_tags("c2354d53d2", ["red", "green"])
# => [ "yellow", "red", "green" ]

@example Adding a single tag

client.add_tags("c2354d53d2", "orange")
# => [ "yellow", "orange" ]

@raise [Timeout::Error] if the API request took too long to execute. @raise [Snitcher::API::ResourceNotFoundError] if the Snitch does not exist. @raise [Snitcher::API::Error] if an API errors occur.

@return [Array<String>] full list of tags on the Snitch.

# File lib/snitcher/api/client.rb, line 218
def add_tags(token, tags = [])
  token = CGI.escape(token)

  post("/v1/snitches/#{token}/tags", Array(tags).flatten)
end
create_snitch(attributes = {}) click to toggle source

Create a new Snitch.

@param [Hash] attributes The properties for the new Snitch @option attributes [String] :name The label used for the Snitch @option attributes [String] :interval How often the snitch is expected to

check-in. One of: "15_minute", "30_minute", "hourly", "daily", "weekly",
or "monthly".

@option attributes [optional, String] :notes Additional information about

the Snitch. Useful to put instructions of investigating or fixing any
errors.

@option attributes [optional, Array<String>] :tags List of labels to tag the

Snitch with.

@example Create a new Snitch

client.create_snitch({
  name:  "Daily Backups",
  interval: "hourly",
  notes: "On error check the print tray for paper jams",
  tags:  [ "backups", "maintenance" ],
})

# => #<Snitcher::API::Snitch:...>

@raise [Timeout::Error] if the API request took too long to execute. @raise [Snitcher::API::ResourceInvalidError] if the attributes are not valid

for a Snitch.

@raise [Snitcher::API::Error] if any other API errors occur.

@return [Snitcher::API::Snitch] the new Snitch.

# File lib/snitcher/api/client.rb, line 134
def create_snitch(attributes = {})
  if interval = attributes.delete(:interval)
    type = attributes[:type] ||= {}
    type[:interval] ||= interval
  end

  response = post("/v1/snitches", attributes)
  Snitcher::API::Snitch.new(response)
end
delete_snitch(token) click to toggle source

Deletes a Snitch.

@param token [String] The unique token of the Snitch to delete.

@example Delete a Snitch.

client.delete_snitch("c2354d53d2")
# => { :message => "Response complete" }

@raise [Timeout::Error] if the API request took too long to execute. @raise [Snitcher::API::ResourceNotFoundError] if the Snitch does not exist. @raise [Snitcher::API::Error] if any other API errors occur.

@return [nil]

# File lib/snitcher/api/client.rb, line 280
def delete_snitch(token)
  token = CGI.escape(token)

  delete("/v1/snitches/#{token}")

  nil
end
pause_snitch(token) click to toggle source

Pauses a Snitch if it can be paused. Snitches can only be paused if their status is currently “failing” or “errored”.

@param token [String] The unique token of the Snitch.

@example Pause a Snitch

client.pause_snitch("c2354d53d2")
# => true

@raise [Timeout::Error] if the API request took too long to execute. @raise [Snitcher::API::ResourceNotFoundError] if the Snitch does not exist. @raise [Snitcher::API::Error] if any other API errors occur.

@return [nil]

# File lib/snitcher/api/client.rb, line 259
def pause_snitch(token)
  token = CGI.escape(token)

  post("/v1/snitches/#{token}/pause")

  nil
end
remove_tag(token, tag) click to toggle source

Remove a tag from a Snitch.

@param token [String] The unique token of the Snitch. @param tag [String] The tag to remove from the Snitch.

@example Removing the “production” tag from a Snitch

client.remove_tag("c2354d53d2", "production")
# => [ "critical" ]

@raise [Timeout::Error] if the API request took too long to execute. @raise [Snitcher::API::ResourceNotFoundError] if the Snitch does not exist. @raise [Snitcher::API::Error] if any other API errors occur.

@return [Array<String>] list of the remaining tags on the Snitch.

# File lib/snitcher/api/client.rb, line 238
def remove_tag(token, tag)
  token = CGI.escape(token)
  tag = CGI.escape(tag)

  delete("/v1/snitches/#{token}/tags/#{tag}")
end
snitch(token) click to toggle source

Get a single Snitch by it's unique token.

@param token [String] The unique token of the Snitch to get

@example Get the Snitch with token “c2354d53d2”

client.snitch("c2354d53d2")

# => #<Snitcher::API:: @token="c2354d53d2" ...>

@raise [Timeout::Error] if the API request took too long to execute. @raise [Snitcher::API::ResourceNotFoundError] if a Snitch does not exist

with that token

@raise [Snitcher::API::Error] if any other API errors occur.

@return [Snitcher::API::Snitch] the Snitch

# File lib/snitcher/api/client.rb, line 98
def snitch(token)
  token = CGI.escape(token)

  payload = get("/v1/snitches/#{token}")
  Snitcher::API::Snitch.new(payload)
end
snitches(filters = {}) click to toggle source

Get the list snitches on the account

@param [Hash] filters @option filters [String, Array<String>] tags only return Snitches that are

tagged with _all_ of the given tags. For example, if a Snitch is tagged
with "production" and "critical" it will be returned when filtering by
"production", "critical", or ["production", "critical"] but not
["production", "backups"].

@example List the Snitches on an account

client.snitches
# => [ #<Snitcher::API::Snitch:...>, #<Snitcher::API::Snitch:...> ]

@example List Snitches with a specific tag

client.snitches(tags: "production")
# => [ #<Snitcher::API::Snitch:...>, #<Snitcher::API::Snitch:...> ]

@example List Snitches with multiple tags

client.snitches(tags: ["production", "critical"])
# => [ #<Snitcher::API::Snitch:...>, #<Snitcher::API::Snitch:...> ]

@raise [Timeout::Error] if the API request took too long to execute. @raise [Snitcher::API::Error] if any API errors occur.

@return [Array<Snitcher::API::Snitch>] the snitches on the account.

# File lib/snitcher/api/client.rb, line 64
def snitches(filters = {})
  query = {}

  # Tags allow for labeling Snitches for better categorization. This allows
  # filtering by a set of tags.
  if tags = filters[:tags]
    tags = Array(tags).flatten
    query[:tags] = tags.map(&:strip).compact.uniq.join(",")
  end

  # JSON array of Snitch attributes
  response = get("/v1/snitches", query)

  # Convert the attributes hashes into Objects
  response.map! do |snitch|
    Snitcher::API::Snitch.new(snitch)
  end
end
update_snitch(token, attributes = {}) click to toggle source

Update a snitch, identified by token, using passed-in values. Only changes those values included in the attributes hash; other attributes are not changed.

@param token [String] The unique token of the Snitch. @param [Hash] attributes the set of Snitch attributes to change.

@option attributes [String] :name The label used for the Snitch @option attributes [String] :interval How often the snitch is expected to

check-in. One of: "15_minute", "30_minute", "hourly", "daily", "weekly",
or "monthly".

@option attributes [optional, String] :notes Additional information about

the Snitch. Useful to put instructions of investigating or fixing any
errors.

@option attributes [optional, Array<String>, nil] :tags List of labels to

tag the Snitch with.

@example Update an existing Snitch

client.update_snitch("c2354d53d2", {
  name: "Monthly Backups",
})
# => #<Snitcher::API::Snitch:...>

@example Setting Tags for a Snitch

client.update_snitch("c2354d53d2", tags: ["production", "backup"])
# => #<Snitcher::API::Snitch: @tags=["production", "backup"])

@example Removing Tags from a Snitch

client.update_snitch("c2354d53d2", tags: [])
or
client.update_snitch("c2354d53d2", tags: nil)

@raise [Timeout::Error] if the API request took too long to execute. @raise [Snitcher::API::ResourceInvalidError] if the changes are not valid. @raise [Snitcher::API::ResourceNotFoundError] if the Snitch does not exist. @raise [Snitcher::API::Error] if any other API errors occur.

Raise Timeout::Error if the API request times out

# File lib/snitcher/api/client.rb, line 182
def update_snitch(token, attributes = {})
  if attributes.key?(:tags)
    attributes[:tags] = [attributes[:tags]].flatten.compact
  end

  # Expand the interval key to the full structure required by the API
  if interval = attributes.delete(:interval)
    type = attributes[:type] ||= {}
    type[:interval] ||= interval
  end

  token = CGI.escape(token)
  payload = patch("/v1/snitches/#{token}", attributes)

  Snitcher::API::Snitch.new(payload)
end

Private Instance Methods

delete(path) click to toggle source
# File lib/snitcher/api/client.rb, line 369
def delete(path)
  request = Net::HTTP::Delete.new(path)
  execute_request(request)
end
evaluate_response(response) click to toggle source
# File lib/snitcher/api/client.rb, line 325
def evaluate_response(response)
  case response
  when Net::HTTPNoContent
    nil
  when Net::HTTPSuccess
    JSON.parse(response.body)
  when Net::HTTPInternalServerError
    # InternalServerError does not have a parseable body as the error may not
    # be generated by the application itself.
    raise ::Snitcher::API::InternalServerError.new({
      "type"  => "internal_server_error",
      "error" => response.body,
    })
  else
    error = JSON.parse(response.body)

    raise ::Snitcher::API::Error.new(error)
  end
end
execute_request(request) click to toggle source
# File lib/snitcher/api/client.rb, line 297
def execute_request(request)
  http_options = {
    open_timeout: @timeout,
    read_timeout: @timeout,
    ssl_timeout: @timeout,
    use_ssl: @endpoint.scheme == "https",
  }

  Net::HTTP.start(@endpoint.host, @endpoint.port, http_options) do |http|
    request.basic_auth(@key, "")
    request["User-Agent"] = user_agent

    # All requests (with bodies) are made using JSON.
    if request.body
      request["Content-Type"] = "application/json"

      # Some trickery to allow pushing the JSON rendering down as far as
      # possible.
      if !request.body.is_a?(String)
        request.body = JSON.generate(request.body)
      end
    end

    response = http.request(request)
    evaluate_response(response)
  end
end
get(path, query = {}) click to toggle source
# File lib/snitcher/api/client.rb, line 345
def get(path, query = {})
  # Only add the query param if any valid filters were given.
  if query.any?
    path = "#{path}?#{URI.encode_www_form(query)}"
  end

  request = Net::HTTP::Get.new(path)
  execute_request(request)
end
patch(path, data) click to toggle source
# File lib/snitcher/api/client.rb, line 362
def patch(path, data)
  request = Net::HTTP::Patch.new(path)
  request.body = data

  execute_request(request)
end
post(path, data = nil) click to toggle source
# File lib/snitcher/api/client.rb, line 355
def post(path, data = nil)
  request = Net::HTTP::Post.new(path)
  request.body = data

  execute_request(request)
end
user_agent() click to toggle source
# File lib/snitcher/api/client.rb, line 290
def user_agent
  # RUBY_ENGINE was not added until 1.9.3
  engine = defined?(::RUBY_ENGINE) ? ::RUBY_ENGINE : "Ruby"

  "Snitcher; #{engine}/#{RUBY_VERSION}; #{RUBY_PLATFORM}; v#{::Snitcher::VERSION}"
end