forked from nellshamrell/cdnetworks-client
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from wstrinz/statistics-bandwidth-usage
Statistics bandwidth usage
- Loading branch information
Showing
10 changed files
with
523 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.