ApiWrapperFor8x8 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.md +30 -1
- data/lib/ApiWrapperFor8x8.rb +1 -0
- data/lib/ApiWrapperFor8x8/agent.rb +2 -2
- data/lib/ApiWrapperFor8x8/agents.rb +13 -19
- data/lib/ApiWrapperFor8x8/channel.rb +11 -2
- data/lib/ApiWrapperFor8x8/connection.rb +20 -3
- data/lib/ApiWrapperFor8x8/error.rb +2 -0
- data/lib/ApiWrapperFor8x8/stats.rb +50 -0
- data/lib/ApiWrapperFor8x8/version.rb +1 -1
- data/spec/agent_spec.rb +11 -0
- data/spec/agents_spec.rb +3 -16
- data/spec/channel_spec.rb +1 -1
- data/spec/connection_spec.rb +5 -6
- data/spec/error_spec.rb +40 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/stats_spec.rb +39 -0
- metadata +7 -2
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -19,7 +19,36 @@ Or install it yourself as:
|
|
19
19
|
|
20
20
|
## Usage
|
21
21
|
|
22
|
-
|
22
|
+
###Setup
|
23
|
+
```ruby
|
24
|
+
@api_connection = ApiWrapperFor8x8::Connection.new({
|
25
|
+
:username => 'foo', # your username for 8x8
|
26
|
+
:password => 'bar' # your password for 8x8
|
27
|
+
})
|
28
|
+
```
|
29
|
+
|
30
|
+
###Params for each call
|
31
|
+
Date range: it has to be a iso8601 format and a string with comma separated, Ex "#{(Time.now-3600*24).iso8601,Time.now.iso8601}"
|
32
|
+
Timezone: it need to be following: list_of_timezone[http://en.wikipedia.org/wiki/List_of_zoneinfo_time_zones], Ex. America/Los_Angeles
|
33
|
+
It has more params, which can be seen on 8x8 site[http://www.8x8.com/Support/BusinessSupport/Documentation/VirtualContactCenterDocumentation/VirtualContactCenterStats.aspx]
|
34
|
+
|
35
|
+
###Channel
|
36
|
+
|
37
|
+
Get a list of channels
|
38
|
+
```ruby
|
39
|
+
@api_coonection.channel_list
|
40
|
+
```
|
41
|
+
|
42
|
+
Get a list of agnets
|
43
|
+
```ruby
|
44
|
+
@api_coonection.agent_list
|
45
|
+
```
|
46
|
+
|
47
|
+
Get a list of agnet details
|
48
|
+
```ruby
|
49
|
+
@api_coonection.agents_details({:d => 'YOUR DATE RANGE', :tz => 'YOUR TIMEZONE'}, {FILTER OPTIONS})
|
50
|
+
```
|
51
|
+
|
23
52
|
|
24
53
|
## Contributing
|
25
54
|
|
data/lib/ApiWrapperFor8x8.rb
CHANGED
@@ -2,30 +2,24 @@ module ApiWrapperFor8x8
|
|
2
2
|
module Agents
|
3
3
|
include ApiWrapperFor8x8::Agent
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
# Get a list of all the agents
|
6
|
+
#
|
7
|
+
def agent_list(filtered_options={})
|
8
|
+
get('/stats/agents.json', {}, filtered_options)
|
7
9
|
end
|
8
10
|
|
11
|
+
# Get all the details of agents
|
12
|
+
#
|
13
|
+
# Ex. Get details for date range of '2013-09-04T00:00:00-07:00,2013-09-04T23:59:59-07:00'
|
14
|
+
# and filtered with queue-name and agent-id
|
15
|
+
# @api_connection.agents_detail({:d => '2013-09-04T00:00:00-07:00,2013-09-04T23:59:59-07:00'},
|
16
|
+
# {"agent-id"=>"foo", "queue-name"=>"bar"})
|
9
17
|
def agents_detail(params={}, filtered_options={})
|
10
18
|
details = []
|
11
|
-
|
12
|
-
details
|
19
|
+
agent_list.each do |agent|
|
20
|
+
details.concat(agent_detail(agent['agent-id'], params, filtered_options))
|
13
21
|
end
|
14
|
-
details
|
22
|
+
details
|
15
23
|
end
|
16
|
-
|
17
|
-
def filtered_agents(agent_list, filtered_options)
|
18
|
-
if filtered_options.size == 0
|
19
|
-
return list
|
20
|
-
end
|
21
|
-
agent_list.select do |agent|
|
22
|
-
flag = true
|
23
|
-
filtered_options.each do |key, value|
|
24
|
-
flag = false unless (agent[key] && agent[key] == value)
|
25
|
-
end
|
26
|
-
flag
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
24
|
end
|
31
25
|
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
module ApiWrapperFor8x8
|
2
2
|
module Channel
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
# Get a list of channels
|
5
|
+
#
|
6
|
+
def channel_list(filtered_options={})
|
7
|
+
get "/stats/channels.json", {}, filtered_options
|
6
8
|
end
|
7
9
|
|
8
10
|
# Get one details on the channel
|
@@ -21,6 +23,13 @@ module ApiWrapperFor8x8
|
|
21
23
|
|
22
24
|
# It is easier for geting the sum of a value from the
|
23
25
|
# an array of records with the restriction you can set
|
26
|
+
#
|
27
|
+
# Ex. Get a sum of 'accepted-count' from channel_id: 1, date range
|
28
|
+
# , filtered_options, and queue-name
|
29
|
+
# @api_connection.channel_sum_x('accepted-count',
|
30
|
+
# 1,
|
31
|
+
# {:d => '2013-09-04T00:00:00-07:00,2013-09-04T23:59:59-07:00'},
|
32
|
+
# {"agent-id"=>"foo", "queue-name"=>"bar"}}
|
24
33
|
def channel_sum_x(x, guid=0, params_options={}, filtered_options={})
|
25
34
|
details = channel_details(guid, params_options, filtered_options)
|
26
35
|
sum = details.map {|detail| detail[x]}.inject(:+) if details
|
@@ -4,12 +4,13 @@ module ApiWrapperFor8x8
|
|
4
4
|
|
5
5
|
include ApiWrapperFor8x8::Channel
|
6
6
|
include ApiWrapperFor8x8::Agents
|
7
|
+
include ApiWrapperFor8x8::Stats
|
7
8
|
|
8
9
|
RECORDS_LIMIT = 50
|
9
10
|
MAX_TRY = 3
|
10
11
|
VALID_SEGMENT = ['channels', 'agents', 'statistics']
|
11
12
|
|
12
|
-
base_uri "
|
13
|
+
base_uri "https://na3.mycontactual.com/api"
|
13
14
|
format :json
|
14
15
|
|
15
16
|
def initialize(creds={})
|
@@ -20,9 +21,11 @@ module ApiWrapperFor8x8
|
|
20
21
|
end
|
21
22
|
|
22
23
|
def request(method, url, options={})
|
23
|
-
|
24
|
+
unless api_token_keys_valid?
|
25
|
+
raise ApiWrapperFor8x8::ResponseError.new(nil, "Please set username and password correctly")
|
26
|
+
end
|
24
27
|
options[:basic_auth] = @configuration
|
25
|
-
self.class.__send__(method, url, options)
|
28
|
+
parsed_response(self.class.__send__(method, url, options))
|
26
29
|
end
|
27
30
|
|
28
31
|
def api_token_keys_valid?
|
@@ -93,5 +96,19 @@ module ApiWrapperFor8x8
|
|
93
96
|
flag
|
94
97
|
end
|
95
98
|
end
|
99
|
+
|
100
|
+
def parsed_response(response)
|
101
|
+
if response.is_a? Net::HTTPResponse
|
102
|
+
unless response.is_a? Net::HTTPSuccess
|
103
|
+
raise ApiWrapperFor8x8::ResponseError.new(response)
|
104
|
+
end
|
105
|
+
JSON.parse(response.body)
|
106
|
+
else
|
107
|
+
unless response.success?
|
108
|
+
raise ApiWrapperFor8x8::ResponseError.new(response)
|
109
|
+
end
|
110
|
+
response.parsed_response
|
111
|
+
end
|
112
|
+
end
|
96
113
|
end
|
97
114
|
end
|
@@ -15,6 +15,8 @@ module ApiWrapperFor8x8
|
|
15
15
|
def common_solutions
|
16
16
|
if @response.code.to_i == 401
|
17
17
|
"Check your credentials and make sure they are correct and not expired"
|
18
|
+
elsif @response.code.to_i >= 500 && @response.code.to_i < 600
|
19
|
+
"Check the format of your json"
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ApiWrapperFor8x8
|
2
|
+
module Stats
|
3
|
+
|
4
|
+
DEFAULT_STAT_ATTR = ["entered-count", "accepted-count", "abandoned-count", "time-waiting"]
|
5
|
+
# Since the api only aggregate the records by half an
|
6
|
+
# hour, so why not they have aggregate by hour
|
7
|
+
def stats_per_hour(url, params={}, filtered_opts={}, stats_attr=[])
|
8
|
+
raise ApiWrapperFor8x8::ResponseError.new({}, "Required date range!") unless params[:d]
|
9
|
+
if stats_attr.include?('queue-name') || stats_attr.include?('time-stamp')
|
10
|
+
raise ApiWrapperFor8x8::ResponseError.new({}, "Does not support queue-name or time-stamp as stat attribute")
|
11
|
+
end
|
12
|
+
|
13
|
+
stats = get(url, params, filtered_opts)
|
14
|
+
stats_attr = DEFAULT_STAT_ATTR if stats_attr.size == 0
|
15
|
+
times = params[:d].split(',')
|
16
|
+
start = Time.parse(times.first)
|
17
|
+
stop = Time.parse(times.last)
|
18
|
+
hash = {}
|
19
|
+
|
20
|
+
# init the hash object
|
21
|
+
(beginning_of_hour(start).to_i..end_of_hour(stop).to_i).step(3600) do |h|
|
22
|
+
hour = Time.at(h)
|
23
|
+
hour_key = hour.iso8601
|
24
|
+
hash[hour_key] = {}
|
25
|
+
hash[hour_key]['timestamp'] = hour
|
26
|
+
stats_attr.each { |key| hash[hour_key][key] = 0 }
|
27
|
+
end
|
28
|
+
|
29
|
+
# insert stats data to hash with timestamp as key
|
30
|
+
stats.each do |stat|
|
31
|
+
timestamp = stat["time-stamp"]
|
32
|
+
timekey = hash[timestamp] ? timestamp : (Time.parse(timestamp) - 1800).iso8601
|
33
|
+
stats_attr.each do |stat_attr|
|
34
|
+
stats_by_time = hash[timekey] || {}
|
35
|
+
stats_by_time[stat_attr] += stat[stat_attr] if stats_by_time[stat_attr] && stat[stat_attr]
|
36
|
+
end
|
37
|
+
end if stats
|
38
|
+
return hash.collect{ |key, value| value }
|
39
|
+
end
|
40
|
+
|
41
|
+
def beginning_of_hour(time)
|
42
|
+
Time.mktime(time.year, time.month, time.day, time.hour).send(Time.now.gmt? ? :gmt : :localtime)
|
43
|
+
end
|
44
|
+
|
45
|
+
def end_of_hour(time)
|
46
|
+
beginning_of_hour(time)+3600
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
data/spec/agent_spec.rb
CHANGED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Agent' do
|
4
|
+
describe "get_detail" do
|
5
|
+
it "should have call get with correct params" do
|
6
|
+
id = 0
|
7
|
+
expect(@api).to receive(:get).with("/stats/agents/#{id}/statistics.json", {}, {})
|
8
|
+
@api.agent_detail(id, {}, {})
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/spec/agents_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe ApiWrapperFor8x8::Agents do
|
4
4
|
describe "agent_list" do
|
5
5
|
it "should GET /stats/agents.json" do
|
6
|
-
@api.should_receive(:get).with('/stats/agents.json', {})
|
6
|
+
@api.should_receive(:get).with('/stats/agents.json', {}, {})
|
7
7
|
@api.agent_list
|
8
8
|
end
|
9
9
|
end
|
@@ -15,22 +15,9 @@ describe ApiWrapperFor8x8::Agents do
|
|
15
15
|
resp = {'statistics' => {'statistic' => agents}}
|
16
16
|
agent_details = [{'agent-id' => agent_id, 'accepted-count' => 23}]
|
17
17
|
@api.stub(:agent_list)
|
18
|
-
expect(@api).to receive(:
|
19
|
-
expect(@api).to receive(:
|
20
|
-
expect(@api).to receive(:agent_detail).with(agent_id, {}).and_return(agent_details)
|
18
|
+
expect(@api).to receive(:agent_list).and_return(agents)
|
19
|
+
expect(@api).to receive(:agent_detail).with(agent_id, {}, {}).and_return(agent_details)
|
21
20
|
@api.agents_detail.should == agent_details
|
22
21
|
end
|
23
22
|
end
|
24
|
-
|
25
|
-
describe "filtered_agents" do
|
26
|
-
before :each do
|
27
|
-
@agent1 = {"agent-id" => "a", "enabled" => 'Y'}
|
28
|
-
@agent2 = {"agent-id" => 'b', "enabled" => 'D'}
|
29
|
-
@agent_lists = [@agent1, @agent2]
|
30
|
-
end
|
31
|
-
it "should filter agents with enabled" do
|
32
|
-
@api.filtered_agents(@agent_lists, {"enabled" => "Y"}).should == [@agent1]
|
33
|
-
@api.filtered_agents(@agent_lists, {"enabled" => "D"}).should == [@agent2]
|
34
|
-
end
|
35
|
-
end
|
36
23
|
end
|
data/spec/channel_spec.rb
CHANGED
@@ -8,7 +8,7 @@ describe ApiWrapperFor8x8::Channel do
|
|
8
8
|
|
9
9
|
describe "channel_list" do
|
10
10
|
it "should GET /stats/channels.json" do
|
11
|
-
@api.should_receive(:get).with('/stats/channels.json', {})
|
11
|
+
@api.should_receive(:get).with('/stats/channels.json', {}, {})
|
12
12
|
@api.channel_list
|
13
13
|
end
|
14
14
|
|
data/spec/connection_spec.rb
CHANGED
@@ -81,12 +81,11 @@ describe ApiWrapperFor8x8::Connection do
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
@api.get(url)
|
84
|
+
describe "base_uri" do
|
85
|
+
it "should not be empty or nil" do
|
86
|
+
ApiWrapperFor8x8::Connection::base_uri.should_not be_nil
|
87
|
+
ApiWrapperFor8x8::Connection::base_uri.size.should_not be_zero
|
88
|
+
end
|
90
89
|
end
|
91
90
|
|
92
91
|
end
|
data/spec/error_spec.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ApiWrapperFor8x8::ResponseError do
|
4
|
+
describe "For OAth response" do
|
5
|
+
before do
|
6
|
+
@net_http_response = Net::HTTPSuccess.new('1.1', '200', 'OK')
|
7
|
+
@error = ApiWrapperFor8x8::ResponseError.new(@net_http_response)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "response should return Net::HTTP response" do
|
11
|
+
@error.response.should == @net_http_response
|
12
|
+
end
|
13
|
+
|
14
|
+
it "code should return response code" do
|
15
|
+
@error.code.should == '200'
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "message" do
|
19
|
+
it "should return response message" do
|
20
|
+
@error.message.should == 'OK'
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should return specified message" do
|
24
|
+
error = ApiWrapperFor8x8::ResponseError.new(@net_http_response, "No way")
|
25
|
+
error.message.should == 'No way'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
describe "common_solutions" do
|
31
|
+
describe "on 401" do
|
32
|
+
it "should suggest to check credentials" do
|
33
|
+
net_http_response = Net::HTTPUnauthorized.new('1.1', '401', 'Unauthorized Access')
|
34
|
+
error = ApiWrapperFor8x8::ResponseError.new(net_http_response)
|
35
|
+
error.common_solutions.should match /Check your credentials/i
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/stats_spec.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "ApiWrapperFor8x8::Stats" do
|
4
|
+
describe "stats_per_hour" do
|
5
|
+
|
6
|
+
context "should return correct data with valid params" do
|
7
|
+
before :each do
|
8
|
+
@url = "/stats/channels/1/statistics.json"
|
9
|
+
@params = {:d => '2013-05-28T12:54:55-07:00,2013-05-28T19:54:55-07:00', :tz => 'America/Los_Angeles'}
|
10
|
+
@resp = [{"time-stamp" => "2013-05-28T13:30:00-07:00",
|
11
|
+
"entered-count" => 3,
|
12
|
+
"accepted-count" => 2,
|
13
|
+
"abandoned-count" => 1,
|
14
|
+
"time-waiting" => 24
|
15
|
+
},{"time-stamp" => "2013-05-28T14:00:00-07:00",
|
16
|
+
"entered-count" => 10,
|
17
|
+
"accepted-count" => 8,
|
18
|
+
"abandoned-count" => 2,
|
19
|
+
"time-waiting" => 124},
|
20
|
+
{"time-stamp" => "2013-05-28T19:00:00-07:00",
|
21
|
+
"entered-count" => 20,
|
22
|
+
"accepted-count" => 19,
|
23
|
+
"abandoned-count" => 1,
|
24
|
+
"time-waiting" => 144}]
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should return an array of hash that includes all the stat attr and the timestamp" do
|
28
|
+
expect(@api).to receive(:get).with(@url, @params, {}).and_return(@resp)
|
29
|
+
ret = @api.stats_per_hour(@url, @params, {})
|
30
|
+
|
31
|
+
ret.size.should == 9
|
32
|
+
ret.map {|r| r['entered-count']}.inject {|sum,num| sum+num}.should == 33
|
33
|
+
ret.map {|r| r['accepted-count']}.inject {|sum,num| sum+num}.should == 29
|
34
|
+
ret.map {|r| r['abandoned-count']}.inject {|sum,num| sum+num}.should == 4
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ApiWrapperFor8x8
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-09-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -143,13 +143,16 @@ files:
|
|
143
143
|
- lib/ApiWrapperFor8x8/channel.rb
|
144
144
|
- lib/ApiWrapperFor8x8/connection.rb
|
145
145
|
- lib/ApiWrapperFor8x8/error.rb
|
146
|
+
- lib/ApiWrapperFor8x8/stats.rb
|
146
147
|
- lib/ApiWrapperFor8x8/version.rb
|
147
148
|
- spec/.rspec
|
148
149
|
- spec/agent_spec.rb
|
149
150
|
- spec/agents_spec.rb
|
150
151
|
- spec/channel_spec.rb
|
151
152
|
- spec/connection_spec.rb
|
153
|
+
- spec/error_spec.rb
|
152
154
|
- spec/spec_helper.rb
|
155
|
+
- spec/stats_spec.rb
|
153
156
|
- spec/version_spec.rb
|
154
157
|
homepage: https://github.com/daifu/api-wrapper-for-8x8
|
155
158
|
licenses:
|
@@ -182,5 +185,7 @@ test_files:
|
|
182
185
|
- spec/agents_spec.rb
|
183
186
|
- spec/channel_spec.rb
|
184
187
|
- spec/connection_spec.rb
|
188
|
+
- spec/error_spec.rb
|
185
189
|
- spec/spec_helper.rb
|
190
|
+
- spec/stats_spec.rb
|
186
191
|
- spec/version_spec.rb
|