Skip to content

Commit

Permalink
Merge pull request #4 from wstrinz/statistics-bandwidth-usage
Browse files Browse the repository at this point in the history
Statistics bandwidth usage
  • Loading branch information
willsu committed Oct 27, 2015
2 parents a9a4e97 + bfdc74c commit 35eaa7b
Show file tree
Hide file tree
Showing 10 changed files with 523 additions and 81 deletions.
86 changes: 72 additions & 14 deletions lib/cdnetworks-client.rb
Original file line number Diff line number Diff line change
@@ -1,56 +1,114 @@
require "json"

require_relative "patches"

require "cdnetworks-client/version"
require "cdnetworks-client/cache_purge_api"
require "cdnetworks-client/config_open_api"
require "cdnetworks-client/cache_flush_open_api"
require "cdnetworks-client/open_api_error"
require "cdnetworks-client/auth_open_api"
require "cdnetworks-client/open_api_keys"
require "cdnetworks-client/statistics_open_api"

class CdnetworksClient
include ConfigOpenApi
include CachePurgeApi
include CacheFlushOpenApi
include StatisticsOpenApi
attr_accessor :user, :password, :location

MAX_SESSION_RETRIES = 2

def initialize(credentials={})
@user = credentials[:user]
@password = credentials[:pass]
@location = credentials[:location]
self.user = credentials[:user]
self.password = credentials[:pass]
self.location = credentials[:location]
end

def compose_request(path,options)
request = Net::HTTP::Post.new("#{base_url(@location)}#{path}")
request = Net::HTTP::Post.new("#{base_url(location)}#{path}")
request.set_form_data(options)
request
end

def call(path,options)
begin
response = http.request(compose_request(path,options))
def call(path,options,session_retries=0)
response = http.request(compose_request(path,options))

if location == "Beta"
process_beta_response(path, options, response, session_retries)
else
response_hash = { code: response.code, body: response.body }
rescue StandardError=>e
"An error has occurred connecting to the CDNetworks API (#{e})"
end
end

private

def base_url(location=nil)
def base_url(loc=nil)
case
when location == "Korea"
when loc == "Korea"
"https://openapi.kr.cdnetworks.com"
when location == "Japan"
when loc == "Japan"
"https://openapi.jp.cdnetworks.com"
when location == "China"
when loc == "China"
"https://openapi.txnetworks.cn"
when loc == "Beta"
"https://openapi-beta.cdnetworks.com"
else
"https://openapi.us.cdnetworks.com"
end
end

def process_beta_response(path, options, response, session_retries)
if expired_session_response?(response)
if session_retries <= MAX_SESSION_RETRIES
new_session_token = get_session_token(true)
options[:sessionToken] = new_session_token
return call(path, options, session_retries + 1)
else
raise OpenApiError::CriticalApiError.new("Session expired and failed to be re-established after #{session_retries} tries")
end
end

begin
data = JSON.parse(response.body)

result_code = data.values.first['resultCode'] || data.values.first['returnCode']

if !%w{0 200 404}.include?(result_code.to_s)
OpenApiError::ErrorHandler.handle_error_response(result_code, response.body)
else
{code: result_code, body: data}
end
rescue JSON::ParserError
raise OpenApiError::CriticalApiError.new("Unparseable body: #{response.body}")
end
end

def http
uri = URI.parse(base_url)
uri = URI.parse(base_url(location))

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

http
end

def expired_session_response?(response)
if response.code.to_s == "200"
begin
parsed = JSON.parse(response.body.to_s)
if parsed.values.first
parsed.values.first['returnCode'] == 102 ||
parsed.values.first['resultCode'] == 102
else
false
end
rescue JSON::ParserError
false
end
else
false
end
end
end
56 changes: 56 additions & 0 deletions lib/cdnetworks-client/auth_open_api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
module AuthOpenApi
LOGIN_URL = "/api/rest/login"
LOGOUT_URL = "/api/rest/logout"

class AuthSession
def raise_handled_error(code, desc)
raise OpenApiError::ApiError.new("Auth error: #{code} - #{desc}")
end

def initialize(user, pass, client)
@user = user
@pass = pass
@client = client
end

def session
@session ||= login
end

def login
params = {
user: @user,
pass: @pass,
output: "json"
}
resp = @client.call(LOGIN_URL, params)

resp[:body]['loginResponse']['session']
end

def logout
params = {
user: @user,
pass: @pass,
output: "json"
}

resp = @client.call(LOGOUT_URL, params)
resp[:body]['logoutResponse']
end
end

def get_session_token(reset_session = false, keynum = 0)
if !@auth_session || reset_session
@auth_session = AuthSession.new(@user, @password, self)
end

session = Array.wrap(@auth_session.session)[keynum]

if session.is_a?(Hash)
session['sessionToken']
else
nil
end
end
end
27 changes: 22 additions & 5 deletions lib/cdnetworks-client/config_open_api.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
module ConfigOpenApi

def list(options={})
call(config_open_path("list"),add_config_credentials(options))
response = call(config_open_path("list"), add_config_credentials(options))
if location == "Beta"
response[:body]["PadConfigResponse"]["data"]["data"]
else
response
end
end

def view(options={})
Expand All @@ -17,12 +21,25 @@ def edit(options={})
end

def config_open_path(command)
"/config/rest/pan/site/#{command}"
if location == "Beta"
"/api/rest/pan/site/#{command}"
else
"/config/rest/pan/site/#{command}"
end
end

def add_config_credentials(options)
options[:user] = @user
options[:pass] = @password
if location == "Beta"
session_token = get_session_token
keys = get_api_key_list(session_token)
api_key = keys.find{|k| k["type"] == 0}["apiKey"]

options[:sessionToken] = session_token
options[:apiKey] = api_key
else
options[:user] = @user
options[:pass] = @password
end

options
end
Expand Down
31 changes: 31 additions & 0 deletions lib/cdnetworks-client/open_api_error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module OpenApiError
ERROR_CODES = {
"101" => "User login information error",
"102" => "Invalid session",
"103" => "Logout failure",
"104" => "Input parameter error (no entry is made or invalid value)",
"202" => "Inaccessible menu (Setting can be modified via Customer Portal)",
"203" => "Inaccessible service (Setting can be modified via Customer Portal)",
"204" => "Inaccessible Request (See Table-1. The Scope of CDNetworks Statistics Open API)",
"301" => "Unregistered API key (Registration information is provided in Customer Portal)",
"999" => "Temporary error"
}

class ApiError < StandardError

end

class CriticalApiError < StandardError

end

class ErrorHandler
def self.handle_error_response(code, body)
if desc = ERROR_CODES[code.to_s]
raise ApiError.new("Open API Error #{code}: #{desc}")
else
raise ApiError.new("Unknown Open API Response Code #{code} (body: #{body})")
end
end
end
end
29 changes: 29 additions & 0 deletions lib/cdnetworks-client/open_api_keys.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module OpenApiKeys
GET_KEY_PATH = "/api/rest/getApiKeyList"

def get_api_key_list(session_token)
params = {
output: "json",
sessionToken: session_token
}
uri = URI("#{base_url(@location)}/#{GET_KEY_PATH}")
uri.query = URI.encode_www_form(params)

response = call(GET_KEY_PATH, params)

response[:body]['apiKeyInfo']['apiKeyInfoItem']
end

def get_api_key(session_token, service_name)

key_for_service = (get_api_key_list(session_token) || []).find do |service|
service['serviceName'] == service_name
end

unless key_for_service
raise "No key found for #{service_name}"
end

return key_for_service['apiKey']
end
end
29 changes: 29 additions & 0 deletions lib/cdnetworks-client/statistics_open_api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module StatisticsOpenApi
include AuthOpenApi
include OpenApiKeys

BANDWIDTH_PATH = "/api/rest/traffic/edge"

def bandwidth_usage(service_name, from, to, time_interval = 2)
session_token = get_session_token

api_key = get_api_key(session_token, service_name)

opts = {
sessionToken: session_token,
apiKey: api_key,
fromDate: from.strftime("%Y%m%d"),
toDate: to.strftime("%Y%m%d"),
timeInterval: time_interval,
output: "json"
}

response = call(BANDWIDTH_PATH, opts)

if response[:code].to_s == "404"
nil
else
Array.wrap(response[:body]['trafficResponse']['trafficItem']).map{|i| i['dataTransferred']}.inject(&:+)
end
end
end
11 changes: 11 additions & 0 deletions lib/patches.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class Array
def self.wrap(object)
if object.nil?
[]
elsif object.respond_to?(:to_ary)
object.to_ary || [object]
else
[object]
end
end
end
37 changes: 37 additions & 0 deletions spec/integration_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
require_relative 'spec_helper'

describe CdnetworksClient do
before(:all) do
@user = ENV['CDN_USER']
@pass = ENV['CDN_PASS']

unless @user && @pass
skip "either CDN_USER or CDN_PASS env var not set. skipping"
end

WebMock.allow_net_connect!

@client = CdnetworksClient.new(user: @user, pass: @pass, location: "Beta")
end

after(:all) do
WebMock.disable_net_connect!
end

it 'gets a session' do
expect(@client.get_session_token).not_to be_nil
end

it 'gets bandwidth usage' do
pad = ENV['CDN_PAD']
skip "must set CDN_PAD env var" unless pad
usage = @client.bandwidth_usage(pad, Date.today - 2, Date.today - 1)
expect(usage).to be > 0
end

it 'lists pads for a session' do
pads = @client.list

expect(pads.length).to be > 0
end
end
Loading

0 comments on commit 35eaa7b

Please sign in to comment.