ruby_desk 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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