ruby_desk 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.rdoc CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  A gem built by BadrIT (www.badrit.com) that works as an interface for oDesk APIs. It can be used for both desktop and web applications
4
4
 
5
+ == Using RubyDesk with Rails
6
+ If you want to use oDesk APIs with Rails applications you may better use ror_desk.
7
+ ror_desk is a plugin built on Ruby Desk and makes it much easier to use with Rails.
8
+
5
9
  == Example
6
10
  Initialize with your api key
7
11
  rd = RubyDesk::Connector.new(api_key, api_secret)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.1
1
+ 0.5.0
@@ -2,6 +2,8 @@ require 'digest/md5'
2
2
  require 'uri'
3
3
  require 'net/http'
4
4
  require 'net/https'
5
+ require 'rexml/document'
6
+ require 'json'
5
7
 
6
8
  module RubyDesk
7
9
  class Connector
@@ -22,6 +24,7 @@ module RubyDesk
22
24
 
23
25
  # Sign the given parameters and returns the signature
24
26
  def sign(params)
27
+ RubyDesk.logger.info "Params to sign: #{params.inspect}"
25
28
  # sort parameters by its names (keys)
26
29
  sorted_params = params.sort { |a, b| a.to_s <=> b.to_s}
27
30
 
@@ -65,6 +68,10 @@ module RubyDesk
65
68
  'Content-Type' => 'application/x-www-form-urlencoded'
66
69
  }
67
70
 
71
+ RubyDesk.logger.info "URL: #{api_call[:url]}"
72
+ RubyDesk.logger.info "method: #{api_call[:method]}"
73
+ RubyDesk.logger.info "Params: #{data}"
74
+
68
75
  case api_call[:method]
69
76
  when :get, 'get' then
70
77
  resp, data = http.request(Net::HTTP::Get.new(url.path+"?"+data, headers))
@@ -74,13 +81,29 @@ module RubyDesk
74
81
  resp, data = http.request(Net::HTTP::Delete.new(url.path, headers), data)
75
82
  end
76
83
 
77
- return data
84
+ RubyDesk.logger.info "Response code: #{resp.code}"
85
+ RubyDesk.logger.info "Returned data: #{data}"
86
+
87
+ case resp.code
88
+ when "200" then return data
89
+ when "401" then raise RubyDesk::UnauthorizedError, data
90
+ when "500" then raise RubyDesk::ServerError, data
91
+ end
92
+
78
93
  end
79
94
 
80
95
  # Prepares an API call with the given arguments then invokes it and returns its body
81
96
  def prepare_and_invoke_api_call(path, options = {})
82
97
  api_call = prepare_api_call(path, options)
83
- invoke_api_call(api_call)
98
+ data = invoke_api_call(api_call)
99
+
100
+ parsed_data = case options[:format]
101
+ when 'json' then JSON.parse(data)
102
+ when 'xml' then REXML::Document.new(data)
103
+ else JSON.parse(data) rescue REXML::Document.new(data) rescue data
104
+ end
105
+ RubyDesk.logger.info "Parsed data: #{parsed_data.inspect}"
106
+ return parsed_data
84
107
  end
85
108
 
86
109
  # Returns the URL that authenticates the application for the current user
@@ -107,10 +130,9 @@ module RubyDesk
107
130
  # Return Data
108
131
  # * token
109
132
  def get_token
110
- response = prepare_and_invoke_api_call 'auth/v1/keys/tokens',
133
+ json = prepare_and_invoke_api_call 'auth/v1/keys/tokens',
111
134
  :params=>{:frob=>@frob, :api_key=>@api_key}, :method=>:post,
112
135
  :auth=>false
113
- json = JSON.parse(response)
114
136
  @api_token = json['token']
115
137
  end
116
138
 
@@ -123,9 +145,8 @@ module RubyDesk
123
145
  # * frob
124
146
 
125
147
  def get_frob
126
- response = prepare_and_invoke_api_call 'auth/v1/keys/frobs',
148
+ json = prepare_and_invoke_api_call 'auth/v1/keys/frobs',
127
149
  :params=>{:api_key=>@api_key}, :method=>:post, :auth=>false
128
- json = JSON.parse(response)
129
150
  @frob = json['frob']
130
151
  end
131
152
 
@@ -140,9 +161,8 @@ module RubyDesk
140
161
  #
141
162
  # * token
142
163
  def check_token
143
- prepare_and_invoke_api_call 'auth/v1/keys/token', :method=>:get
144
- json = JSON.parse(response)
145
- # TODO what to make with results?
164
+ json = prepare_and_invoke_api_call 'auth/v1/keys/token', :method=>:get
165
+ # TODO what to do with results?
146
166
  return json
147
167
  end
148
168
 
@@ -116,11 +116,10 @@ class RubyDesk::Provider < RubyDesk::OdeskEntity
116
116
  if options.respond_to? :to_str
117
117
  return search(connector, :q=>options.to_str)
118
118
  end
119
- response = connector.prepare_and_invoke_api_call(
119
+ json = connector.prepare_and_invoke_api_call(
120
120
  'profiles/v1/search/providers', :method=>:get,
121
121
  :auth=>false, :sign=>false)
122
- # parses a JSON result returned from oDesk and extracts an array of Providers out of it
123
- json = JSON.parse(response)
122
+
124
123
  providers = []
125
124
  [json['providers']['provider']].flatten.each do |provider|
126
125
  providers << self.new(provider)
@@ -130,9 +129,8 @@ class RubyDesk::Provider < RubyDesk::OdeskEntity
130
129
 
131
130
  def get_profile(connector, id, options={})
132
131
  brief = options.delete :brief || false
133
- response = connector.prepare_and_invoke_api_call(
132
+ json = connector.prepare_and_invoke_api_call(
134
133
  "profiles/v1/providers/#{id}" + (brief ? "/brief" : ""), :method=>:get)
135
- json = JSON.parse(response)
136
134
  return self.new(json['profile'])
137
135
  end
138
136
  end
@@ -9,10 +9,9 @@ class RubyDesk::Snapshot < RubyDesk::OdeskEntity
9
9
  attribute :user, :class=>RubyDesk::User
10
10
 
11
11
  def self.work_diary(connector, company_id, user_id, date = nil, timezone = "mine")
12
- response = connector.prepare_and_invoke_api_call(
12
+ json = connector.prepare_and_invoke_api_call(
13
13
  "team/v1/workdiaries/#{company_id}/#{user_id}" + (date ? "/"+date : ""),
14
14
  :params=>{:timezone=>timezone}, :method=>:get)
15
- json = JSON.parse(response)
16
15
 
17
16
  return nil unless json['snapshots']['snapshot']
18
17
  [json['snapshots']['snapshot']].flatten.map do |snapshot|
@@ -28,13 +27,10 @@ class RubyDesk::Snapshot < RubyDesk::OdeskEntity
28
27
  when Array then timestamp.map{|t| t.strftime("%Y%m%d")}.join(";")
29
28
  end
30
29
  # Invoke API call
31
- response = connector.prepare_and_invoke_api_call(
30
+ json = connector.prepare_and_invoke_api_call(
32
31
  "team/v1/workdiaries/#{company_id}/#{user_id}" +
33
32
  (timestamp_param ? "/#{timestamp_param}" : ""), :method=>:get)
34
33
 
35
- # Parse result in json format
36
- json = JSON.parse(response)
37
-
38
34
  # Generate ruby objects for each snapshot
39
35
  [json['snapshot']].flatten.map do |snapshot|
40
36
  self.new(snapshot)
@@ -4,10 +4,9 @@ class RubyDesk::TeamRoom
4
4
 
5
5
  class << self
6
6
  def get_teamrooms(connector)
7
- response = connector.prepare_and_invoke_api_call 'team/v1/teamrooms',
7
+ json = connector.prepare_and_invoke_api_call 'team/v1/teamrooms',
8
8
  :method=>:get
9
- # parses a JSON result returned from oDesk and extracts an array of TeamRooms out of it
10
- json = JSON.parse(response)
9
+
11
10
  team_rooms = []
12
11
  [json['teamrooms']['teamroom']].flatten.each do |teamroom|
13
12
  # Append this TeamRoom to array
@@ -28,9 +27,9 @@ class RubyDesk::TeamRoom
28
27
  end
29
28
 
30
29
  def snapshot(connector, online='now')
31
- response = connector.prepare_and_invoke_api_call "team/v1/teamrooms/#{self.id}",
30
+ json = connector.prepare_and_invoke_api_call "team/v1/teamrooms/#{self.id}",
32
31
  :params=>{:online=>online}, :method=>:get
33
- json = JSON.parse(response)
32
+
34
33
  RubyDesk::Snapshot.new(json['teamroom']['snapshot'])
35
34
  end
36
35
 
@@ -54,11 +54,12 @@ class RubyDesk::TimeReport
54
54
  call_url << "/hours" if options[:hours]
55
55
 
56
56
  gds_query = build_query(options)
57
- response = connector.prepare_and_invoke_api_call(call_url, :method=>:get,
57
+ json = connector.prepare_and_invoke_api_call(call_url, :method=>:get,
58
58
  :base_url=>RubyDesk::Connector::ODESK_GDS_URL, :format=>nil,
59
59
  :params=>{:tq=>URI.escape(gds_query," ,%&=./"), :tqx=>"out:json"})
60
60
 
61
- json = JSON.parse(response)
61
+ raise RubyDesk::Error, json['errors'].inspect if json['status'] == "error"
62
+
62
63
  columns = [json['table']['cols']].flatten
63
64
  rows = [json['table']['rows']].flatten.map do |row_data|
64
65
  row = {}
@@ -73,7 +74,8 @@ class RubyDesk::TimeReport
73
74
 
74
75
  def self.build_query(options)
75
76
  gds_query = ""
76
- gds_query << "SELECT " << options[:select]
77
+ gds_query << "SELECT " << (options[:select].respond_to?(:join)?
78
+ options[:select].join(",") : options[:select])
77
79
  if options[:conditions]
78
80
  gds_query << " WHERE "
79
81
  combined = options[:conditions].map do |field, condition|
@@ -83,10 +85,11 @@ class RubyDesk::TimeReport
83
85
  "#{field}=#{value_to_str(condition)}"
84
86
  when Array then
85
87
  condition.map{|v| "#{field}=#{value_to_str(v)}"}.join(" OR ")
86
- when Range
88
+ when Range then
87
89
  "#{field}>=#{value_to_str(condition.first)} AND #{field}" +
88
90
  (condition.exclude_end? ? "<" : "<=") +
89
91
  value_to_str(condition.last)
92
+ else raise "Invalid condition for field: #{field}"
90
93
  end +
91
94
  ")"
92
95
  end
data/lib/ruby_desk.rb CHANGED
@@ -1,8 +1,18 @@
1
1
  require 'rubygems'
2
2
  require 'json'
3
+ require 'logger'
3
4
 
4
5
  module RubyDesk
5
6
  class Error < RuntimeError; end;
7
+ class UnauthorizedError < Error; end;
8
+ class ServerError < Error; end;
9
+
10
+ class << self
11
+ attr_accessor :logger
12
+ end
13
+
14
+ self.logger = Logger.new(STDERR)
15
+ self.logger.sev_threshold = Logger::FATAL
6
16
  end
7
17
 
8
18
  # This first file is required by other files
@@ -2,6 +2,17 @@ require 'helper'
2
2
  require 'date'
3
3
 
4
4
  class TestRubyDesk < Test::Unit::TestCase
5
+
6
+ def dummy_connector(result_filename)
7
+ connector = RubyDesk::Connector.new('824d225a889aca186c55ac49a6b23957',
8
+ '984aa36db13fff5c')
9
+ connector.instance_variable_set '@result_filename', result_filename
10
+ def connector.invoke_api_call(*args)
11
+ File.read(File.join(File.dirname(__FILE__), @result_filename))
12
+ end if result_filename
13
+ return connector
14
+ end
15
+
5
16
  def test_sign
6
17
  connector = RubyDesk::Connector.new('824d225a889aca186c55ac49a6b23957',
7
18
  '984aa36db13fff5c')
@@ -32,10 +43,7 @@ class TestRubyDesk < Test::Unit::TestCase
32
43
  end
33
44
 
34
45
  def test_provider_search
35
- connector = Object.new
36
- def connector.prepare_and_invoke_api_call(*args)
37
- File.read(File.join(File.dirname(__FILE__), 'providers.json'))
38
- end
46
+ connector = dummy_connector('providers.json')
39
47
 
40
48
  providers = RubyDesk::Provider.search(connector, 'rails')
41
49
 
@@ -46,10 +54,7 @@ class TestRubyDesk < Test::Unit::TestCase
46
54
  end
47
55
 
48
56
  def test_get_profile
49
- connector = Object.new
50
- def connector.prepare_and_invoke_api_call(*args)
51
- File.read(File.join(File.dirname(__FILE__), 'profile.json'))
52
- end
57
+ connector = dummy_connector('profile.json')
53
58
 
54
59
  profile = RubyDesk::Provider.get_profile(connector, 'aseldawy')
55
60
 
@@ -59,10 +64,7 @@ class TestRubyDesk < Test::Unit::TestCase
59
64
  end
60
65
 
61
66
  def test_team_rooms
62
- connector = Object.new
63
- def connector.prepare_and_invoke_api_call(*args)
64
- File.read(File.join(File.dirname(__FILE__), 'teamrooms.json'))
65
- end
67
+ connector = dummy_connector('teamrooms.json')
66
68
 
67
69
  teamrooms = RubyDesk::TeamRoom.get_teamrooms(connector)
68
70
 
@@ -71,10 +73,7 @@ class TestRubyDesk < Test::Unit::TestCase
71
73
  end
72
74
 
73
75
  def test_workdiary
74
- connector = Object.new
75
- def connector.prepare_and_invoke_api_call(*args)
76
- File.read(File.join(File.dirname(__FILE__), 'workdiary.json'))
77
- end
76
+ connector = dummy_connector('workdiary.json')
78
77
 
79
78
  snapshots = RubyDesk::Snapshot.work_diary(connector, 'techunlimited',
80
79
  'aseldawy')
@@ -83,10 +82,7 @@ class TestRubyDesk < Test::Unit::TestCase
83
82
  end
84
83
 
85
84
  def test_snapshot
86
- connector = Object.new
87
- def connector.prepare_and_invoke_api_call(*args)
88
- File.read(File.join(File.dirname(__FILE__), 'snapshot.json'))
89
- end
85
+ connector = dummy_connector('snapshot.json')
90
86
 
91
87
  snapshots = RubyDesk::Snapshot.snapshot_details(connector, 'techunlimited',
92
88
  'aseldawy', Time.now)
@@ -107,6 +103,8 @@ class TestRubyDesk < Test::Unit::TestCase
107
103
  "SELECT worked_on WHERE (provider_id>=1 AND provider_id<=3)"],
108
104
  [{:select=>"worked_on", :conditions=>{:provider_id=>1...3}},
109
105
  "SELECT worked_on WHERE (provider_id>=1 AND provider_id<3)"],
106
+ [{:select=>["worked_on", "hours"]},
107
+ "SELECT worked_on,hours"],
110
108
  ]
111
109
  test_data.each do |options, query|
112
110
  assert query.include?(RubyDesk::TimeReport.build_query(options))
@@ -114,10 +112,7 @@ class TestRubyDesk < Test::Unit::TestCase
114
112
  end
115
113
 
116
114
  def test_timreport
117
- connector = Object.new
118
- def connector.prepare_and_invoke_api_call(*args)
119
- File.read(File.join(File.dirname(__FILE__), 'timereport.json'))
120
- end
115
+ connector = dummy_connector('timereport.json')
121
116
 
122
117
  timereport = RubyDesk::TimeReport.find(connector,
123
118
  :select=>"worked_on, sum(hours), provider_id")
@@ -127,5 +122,13 @@ class TestRubyDesk < Test::Unit::TestCase
127
122
  assert timereport.first['hours'].is_a?(Numeric)
128
123
  end
129
124
 
125
+ def test_timreport_error
126
+ assert_raises RubyDesk::Error do
127
+ connector = dummy_connector('timereport_error.json')
128
+ timereport = RubyDesk::TimeReport.find(connector,
129
+ :select=>"worked_on, sum(hours), provider_id")
130
+ end
131
+ end
132
+
130
133
  end
131
134
 
@@ -0,0 +1,2 @@
1
+ {"version":"0.6","status":"error","errors":[{"reason":"access_denied","message":"You have no access to this timereport"}]}
2
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_desk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ahmed ElDawy
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-13 00:00:00 +02:00
12
+ date: 2010-01-21 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -51,6 +51,7 @@ files:
51
51
  - test/teamrooms.json
52
52
  - test/test_ruby_desk.rb
53
53
  - test/timereport.json
54
+ - test/timereport_error.json
54
55
  - test/workdiary.json
55
56
  has_rdoc: true
56
57
  homepage: http://github.com/aseldawy/ruby_desk