Permalink
Browse files

initial commit

  • Loading branch information...
0 parents commit 461dc98509b3282bbc112b5307ff1e50c1bc5db4 @joshovest committed Oct 4, 2012
Showing with 363 additions and 0 deletions.
  1. +6 −0 .gitignore
  2. +4 −0 Gemfile
  3. +22 −0 LICENSE.txt
  4. +56 −0 README.md
  5. +9 −0 Rakefile
  6. +16 −0 lib/romniture.rb
  7. +153 −0 lib/romniture/client.rb
  8. +13 −0 lib/romniture/exceptions.rb
  9. +3 −0 lib/romniture/version.rb
  10. +23 −0 romniture.gemspec
  11. +58 −0 test/client_test.rb
@@ -0,0 +1,6 @@
+test/config.yml
+*.gem
+.bundle
+Gemfile.lock
+pkg/*
+.DS_STORE
@@ -0,0 +1,4 @@
+source "http://rubygems.org"
+
+# Specify your gem's dependencies in romniture.gemspec
+gemspec
@@ -0,0 +1,22 @@
+Copyright (c) 2012 Josh West
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,56 @@
+# romniture
+To be pronounced...RAWWWRROMNITURE
+
+## what is it
+romniture is a minimal Ruby wrapper to [Omniture's REST API](http://developer.omniture.com). It follows a design policy similar to that of [sucker](https://rubygems.org/gems/sucker) built for Amazon's API.
+
+Omniture's API is closed, you have to be a paying customer in order to access the data.
+
+## installation
+ [sudo] gem install romniture
+
+## initialization and authentication
+romniture requires you supply the `username`, `shared_secret` and `environment` which you can access within the Company > Web Services section of the Admin Console. The environment you'll use to connect to Omniture's API depends on which data center they're using to store your traffic data and will be one of:
+
+* San Jose (https://api.omniture.com/admin/1.3/rest/)
+* Dallas (https://api2.omniture.com/admin/1.3/rest/)
+* London (https://api3.omniture.com/admin/1.3/rest/)
+* San Jose Beta (https://beta-api.omniture.com/admin/1.3/rest/)
+* Dallas (beta) (https://beta-api2.omniture.com/admin/1.3/rest/)
+* Sandbox (https://api-sbx1.omniture.com/admin/1.3/rest/)
+
+Here's an example of initializing with a few configuration options.
+
+ client = ROmniture::Client.new(
+ username,
+ shared_secret,
+ :san_jose,
+ :verify_mode => nil # Optionaly change the ssl verify mode.
+ :log => false, # Optionally turn off logging if it ticks you off
+ :wait_time => 1 # Amount of seconds to wait in between pinging
+ # Omniture's servers to see if a report is done processing (BEWARE OF TOKENS!)
+ )
+
+## usage
+There are only two core methods for the client which doesn't try to "over architect a spaghetti API":
+
+* `get_report` - used to...while get reports and
+* `request` - more generic used to make any kind of request
+
+For reference, I'd recommend keeping [Omniture's Developer Portal](http://developer.omniture.com) open as you code . It's not the easiest to navigate but most of what you need is there.
+
+The response returned by either of these requests Ruby (parsed JSON).
+
+## examples
+ # Find all the company report suites
+ client.request('Company.GetReportSuites')
+
+ # Get an overtime report
+ client.get_report "Report.QueueOvertime", {
+ "reportDescription" => {
+ "reportSuiteID" => "#{@config["report_suite_id"]}",
+ "dateFrom" => "2011-01-01",
+ "dateTo" => "2011-01-10",
+ "metrics" => [{"id" => "pageviews"}]
+ }
+ }
@@ -0,0 +1,9 @@
+require "rubygems"
+require "bundler/gem_tasks"
+
+desc "Run all tests in /test"
+task :test do
+ Dir["test/**/*_test.rb"].each do |test_path|
+ system "ruby #{test_path}"
+ end
+end
@@ -0,0 +1,16 @@
+require "romniture/version"
+require "romniture/client"
+require "romniture/exceptions"
+
+require "rubygems"
+
+require "logger"
+require "httpi"
+require "digest/md5"
+require "digest/sha1"
+require "base64"
+require "json"
+
+module ROmniture
+
+end
@@ -0,0 +1,153 @@
+module ROmniture
+
+ class Client
+
+ DEFAULT_REPORT_WAIT_TIME = 0.25
+
+ ENVIRONMENTS = {
+ :san_jose => "https://api.omniture.com/admin/1.3/rest/",
+ :dallas => "https://api2.omniture.com/admin/1.3/rest/",
+ :london => "https://api3.omniture.com/admin/1.3/rest/",
+ :san_jose_beta => "https://beta-api.omniture.com/admin/1.3/rest/",
+ :dallas_beta => "https://beta-api2.omniture.com/admin/1.3/rest/",
+ :sandbox => "https://api-sbx1.omniture.com/admin/1.3/rest/"
+ }
+
+ def initialize(username, shared_secret, environment, options={})
+ @username = username
+ @shared_secret = shared_secret
+ @environment = environment.is_a?(Symbol) ? ENVIRONMENTS[environment] : environment.to_s
+
+ @wait_time = options[:wait_time] ? options[:wait_time] : DEFAULT_REPORT_WAIT_TIME
+ @log = options[:log] ? options[:log] : false
+ @verify_mode = options[:verify_mode] ? options[:verify_mode] : false
+ HTTPI.log = false
+ end
+
+ def request(method, parameters = {})
+ response = send_request(method, parameters)
+
+ parsed_response = nil
+ begin
+ parsed_response = JSON.parse(response.body)
+ rescue
+ parsed_response = response.body
+ end
+
+ parsed_response
+ end
+
+ def get_report(method, report_description)
+ response = send_request(method, report_description)
+
+ json = JSON.parse response.body
+ if json["status"] == "queued"
+ log(Logger::INFO, "Report with ID (" + json["reportID"].to_s + ") queued. Now fetching report...")
+ return get_queued_report json["reportID"]
+ else
+ log(Logger::ERROR, "Could not queue report. Omniture returned with error:\n#{response.body}")
+ raise "Could not queue report. Omniture returned with error:\n#{response.body}"
+ end
+ end
+
+ attr_writer :log
+
+ def log?
+ @log != false
+ end
+
+ def logger
+ @logger ||= ::Logger.new(STDOUT)
+ end
+
+ def log_level
+ @log_level ||= ::Logger::INFO
+ end
+
+ def log(*args)
+ level = args.first.is_a?(Numeric) || args.first.is_a?(Symbol) ? args.shift : log_level
+ logger.log(level, args.join(" ")) if log?
+ end
+
+ private
+
+ def send_request(method, data)
+ log(Logger::INFO, "Requesting #{method}...")
+ generate_nonce
+
+ log(Logger::INFO, "Created new nonce: #{@password}")
+
+ request = HTTPI::Request.new
+
+ if @verify_mode
+ request.auth.ssl.verify_mode = @verify_mode
+ end
+
+ request.url = @environment + "?method=#{method}"
+ request.headers = request_headers
+ request.body = data.to_json
+
+ response = HTTPI.post(request)
+
+ if response.code >= 400
+ log(:error, "Request failed and returned with response code: #{response.code}\n\n#{response.body}")
+ raise "Request failed and returned with response code: #{response.code}\n\n#{response.body}"
+ end
+
+ log(Logger::INFO, "Server responded with response code #{response.code}.")
+
+ response
+ end
+
+ def generate_nonce
+ @nonce = Digest::MD5.new.hexdigest(rand().to_s)
+ @created = Time.now.strftime("%Y-%m-%dT%H:%M:%SZ")
+ combined_string = @nonce + @created + @shared_secret
+ sha1_string = Digest::SHA1.new.hexdigest(combined_string)
+ @password = Base64.encode64(sha1_string).to_s.chomp("\n")
+ end
+
+ def request_headers
+ {
+ "X-WSSE" => "UsernameToken Username=\"#{@username}\", PasswordDigest=\"#{@password}\", Nonce=\"#{@nonce}\", Created=\"#{@created}\""
+ }
+ end
+
+ def get_queued_report(report_id)
+ done = false
+ error = false
+ status = nil
+ start_time = Time.now
+ end_time = nil
+
+ begin
+ response = send_request("Report.GetStatus", {"reportID" => "#{report_id}"})
+ log(Logger::INFO, "Checking on status of report #{report_id}...")
+
+ json = JSON.parse(response.body)
+ status = json["status"]
+
+ if status == "done"
+ done = true
+ elsif status == "failed"
+ error = true
+ end
+
+ sleep @wait_time if !done && !error
+ end while !done && !error
+
+ if error
+ msg = "Unable to get data for report #{report_id}. Status: #{status}. Error Code: #{json["error_code"]}. #{json["error_msg"]}."
+ log(:error, msg)
+ raise ROmniture::Exceptions::OmnitureReportException.new(json), msg
+ end
+
+ response = send_request("Report.GetReport", {"reportID" => "#{report_id}"})
+
+ end_time = Time.now
+ log(Logger::INFO, "Report with ID #{report_id} has finished processing in #{((end_time - start_time)*1000).to_i} ms")
+
+ JSON.parse(response.body)
+ end
+ end
+end
@@ -0,0 +1,13 @@
+module ROmniture
+ module Exceptions
+
+ class OmnitureReportException < StandardError
+ attr_reader :data
+ def initialize(data)
+ @data = data
+ super
+ end
+ end
+
+ end
+end
@@ -0,0 +1,3 @@
+module ROmniture
+ VERSION = "0.0.4"
+end
@@ -0,0 +1,23 @@
+$:.push File.expand_path("../lib", __FILE__)
+require "romniture/version"
+
+Gem::Specification.new do |s|
+ s.name = "romniture"
+ s.version = ROmniture::VERSION
+ s.authors = ["Josh West"]
+ s.email = ["joshovest@gmail.com"]
+ s.homepage = "http://github.com/joshovest/romniture"
+ s.summary = "Use Omniture's REST API with ease."
+ s.description = "A library that allows access to Omniture's REST API libraries (developer.omniture.com)"
+
+ s.rubyforge_project = s.name
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ s.require_paths = ["lib"]
+
+ s.add_runtime_dependency("httpi")
+ s.add_runtime_dependency("json")
+
+end
@@ -0,0 +1,58 @@
+require 'rubygems'
+require 'test/unit'
+require 'yaml'
+
+require 'romniture'
+
+class ClientTest < Test::Unit::TestCase
+
+ def setup
+ config = YAML::load(File.open("test/config.yml"))
+ @config = config["omniture"]
+
+ @client = ROmniture::Client.new(
+ @config["username"],
+ @config["shared_secret"],
+ @config["environment"],
+ :verify_mode => @config['verify_mode'],
+ :wait_time => @config["wait_time"]
+ )
+ end
+
+ def test_simple_request
+ response = @client.request('Company.GetReportSuites')
+
+ assert_instance_of Hash, response, "Returned object is not a hash."
+ assert(response.has_key?("report_suites"), "Returned hash does not contain any report suites.")
+ end
+
+ def test_report_request
+ response = @client.get_report "Report.QueueOvertime", {
+ "reportDescription" => {
+ "reportSuiteID" => "#{@config["report_suite_id"]}",
+ "dateFrom" => "2011-01-01",
+ "dateTo" => "2011-01-10",
+ "metrics" => [{"id" => "pageviews"}]
+ }
+ }
+
+ assert_instance_of Hash, response, "Returned object is not a hash."
+ assert(response["report"].has_key?("data"), "Returned hash has no data!")
+ end
+
+ def test_a_bad_request
+ # Bad request, mixing commerce and traffic variables
+ assert_raise(ROmniture::Exceptions::OmnitureReportException) do
+ response = @client.get_report("Report.QueueTrended", {
+ "reportDescription" => {
+ "reportSuiteID" => @config["report_suite_id"],
+ "dateFrom" => "2011-01-01",
+ "dateTo" => "2011-01-11",
+ "metrics" => [{"id" => "pageviews"}, {"id" => "event11"}],
+ "elements" => [{"id" => "siteSection"}]
+ }
+ })
+ end
+ end
+
+end

0 comments on commit 461dc98

Please sign in to comment.