harvested 2.0.0 → 3.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d2bbcab0b8d5c473899f541789533afac807bc1d
4
- data.tar.gz: 2271ae74a87f8108722d0019afb636661728fbad
3
+ metadata.gz: d40d1bfd28ea226ff25aa4e6f6b78d6ce3df6d37
4
+ data.tar.gz: c3ebf97344f99de79afc0eae59dd6b0a6de43f7d
5
5
  SHA512:
6
- metadata.gz: edfa9ad33c17a0f759b725788a1c0fe4412609097e6c06a4b16cd553fd49c8429cdc495e23f0313197f555c298eb024fd0f157fbb395aa7c06bd839bf3d4cf61
7
- data.tar.gz: 7d37d6f6f261c930e1030ddee9c82557823fcb9795303081f48ecbcd743413709a40841edaf15736b39c0842ba2a1a48fcf3a4cc504b2ab726efff1604a52451
6
+ metadata.gz: 2bc9034c02ad6d9ce05a8a2c70b545db1c8b5924085c674ec07093092c2d95c18f3af451240278e858398b6c445805615fa263f2d6892de82842e7bbe35d6ee3
7
+ data.tar.gz: bcea2bb7c21a00dba42ba7f666d79fe343b618f75a855b60f0c9cada27b39a4eed577c96181b843ba3cb50fdaef0acab99b8c73763a5879ddd009e85d2587848
data/.gitignore CHANGED
@@ -31,3 +31,5 @@ spec/support/harvest_credentials.yml
31
31
  Gemfile.lock
32
32
  .bundle
33
33
  vendor/bundle
34
+
35
+ tmp
@@ -1,38 +1,58 @@
1
- 2.0.0 - April 23, 2014
1
+ ## 3.0.0.rc1 - May 30, 2014
2
+ * Require Ruby 2.0+
3
+ * Allow OAuth authentication (Thanks Brendan Loudermilk - @bloudermilk)
4
+ * Allow expenses_by_project to be retrieved (Thanks Jordan Yeo - @jordanyeo)
5
+ * Reports now pass through any remaining options (Thanks Philip Arndt - @parndt)
6
+
7
+ ## 2.0.0 - April 23, 2014
2
8
  * Every connection must be SSL
3
- 1.2.0 - February 21, 2014
9
+
10
+ ## 1.2.0 - February 21, 2014
4
11
  * Adds time.trackable_projects: projects that the current user can create entries for
5
12
  * Show hint on error for projects.all as unprivileged user
6
- 1.1.0 - December 13, 2013
13
+
14
+ ## 1.1.0 - December 13, 2013
7
15
  * Adds ability to toggle timers (thanks Eli Fatsi - @efatsi)
8
- 1.0.1 - June 21, 2013
16
+
17
+ ## 1.0.1 - June 21, 2013
9
18
  * Adds ability to pass updated_since paramter to report methods (thanks Pete McWilliams)
10
19
  * Adds ability to create time entries for a given user
11
- 1.0.0 - April 26, 2013
20
+
21
+ ## 1.0.0 - April 26, 2013
12
22
  * Same as 0.6.4 - This should have been v1.0 long ago.
13
- 0.6.4 - October 24, 2012
23
+
24
+ ## 0.6.4 - October 24, 2012
14
25
  * Removes yard and redcarpet dependencies (added on accident)
15
- 0.6.3 - October 24, 2012
26
+
27
+ ## 0.6.3 - October 24, 2012
16
28
  * Adds task activation (Thanks Mark Rickert - @markrickert)
17
29
  * Adds basic invoice support (Thanks Jeffrey Lee - @jlee42)
18
30
  * Adds basic invoice payment support (Thanks Adam Doeler - @releod)
19
- 0.6.2 - August 25, 2012
31
+
32
+ ## 0.6.2 - August 25, 2012
20
33
  * Fixes Mash constructor errors
21
- 0.6.1 - August 22, 2012
34
+
35
+ ## 0.6.1 - August 22, 2012
22
36
  * Adds options to "all" finder (thanks Mikel Lindsaar)
23
37
  * Adds Unauthorized error type (thanks @bcobb)
24
- 0.6.0 - August 22, 2012
38
+
39
+ ## 0.6.0 - August 22, 2012
25
40
  * Replaces Dash with Mash
26
- 0.5.3 - August 21, 2012
41
+
42
+ ## 0.5.3 - August 21, 2012
27
43
  * Adds new fields has_timesheet_2012_beta and timesheet_2012_beta_control_group to Users (thanks Aldric Giacomoni)
28
- 0.5.2 - August 17, 2012
44
+
45
+ ## 0.5.2 - August 17, 2012
29
46
  * Adds new field password_change_required to Users
30
- 0.5.1 - June 27, 2012
47
+
48
+ ## 0.5.1 - June 27, 2012
31
49
  * Updates README and harvested_credentials.example.yml with warnings about normal accounts
32
- 0.5.0 - June 18, 2012
50
+
51
+ ## 0.5.0 - June 18, 2012
33
52
  * Bugfixes on User: https://github.com/zmoazeni/harvested/pull/26
34
53
  * Bugfixes on TimeEntry: https://github.com/zmoazeni/harvested/pull/25
35
- 0.4.0 - August 4, 2011
54
+
55
+ ## 0.4.0 - August 4, 2011
36
56
  * Large rewrite of codebase
37
57
  * Rewrite of library to use JSON instead of XML
38
58
  * Rewrite of tests to use rspec instead of cucumber
@@ -41,14 +61,18 @@
41
61
  * Implements most of the invoice functionality. Creating/Editing/Updating is disabled due to Harvest API issue
42
62
  * Consolidates various forks (thanks Chris Ritterdorf, bricooke, Michelle Moon Lee, tkwong, Steve McKinney, and others that helped but aren't in the git log!)
43
63
  * Tests passing against MRI 1.8, 1.9, JRuby, and Rubinius
44
- 0.3.3 - January 27, 2010
64
+
65
+ ## 0.3.3 - January 27, 2010
45
66
  * Adds fields to TaskAssignment (Quilted)
46
- 0.3.2 - January 27, 2010
67
+
68
+ ## 0.3.2 - January 27, 2010
47
69
  * Adds of_user support to Time Entry (Benjamin Wong - tkwong)
48
- 0.3.1 - October 14, 2010
70
+
71
+ ## 0.3.1 - October 14, 2010
49
72
  * Updates docs removing :ssl => false since Harvest released SSL to everyone
50
73
  * Adds department to the User model (Steve McKinney - )
51
- 0.3.0 - April 11, 2010
74
+
75
+ ## 0.3.0 - April 11, 2010
52
76
  * Adds users
53
77
  * Adds clients
54
78
  * Adds contacts
@@ -18,6 +18,8 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.required_ruby_version = '>= 2.0'
22
+
21
23
  spec.add_runtime_dependency('httparty')
22
24
  spec.add_runtime_dependency('hashie', '~> 1')
23
25
  spec.add_runtime_dependency('json')
@@ -19,23 +19,25 @@ module Harvest
19
19
 
20
20
  protected
21
21
  def request(method, credentials, path, options = {})
22
- params = {}
23
- params[:path] = path
24
- params[:options] = options
25
- params[:method] = method
22
+ params = {
23
+ path: path,
24
+ options: options,
25
+ method: method
26
+ }
26
27
 
27
- response = HTTParty.send(method, "#{credentials.host}#{path}",
28
- :query => options[:query],
29
- :body => options[:body],
30
- :format => :plain,
31
- :headers => {
32
- "Accept" => "application/json",
28
+ httparty_options = {
29
+ query: options[:query],
30
+ body: options[:body],
31
+ format: :plain,
32
+ headers: {
33
+ "Accept" => "application/json",
33
34
  "Content-Type" => "application/json; charset=utf-8",
34
- "Authorization" => "Basic #{credentials.basic_auth}",
35
- "User-Agent" => "Harvestable/#{Harvest::VERSION}",
35
+ "User-Agent" => "Harvested/#{Harvest::VERSION}"
36
36
  }.update(options[:headers] || {})
37
- )
37
+ }
38
38
 
39
+ credentials.set_authentication(httparty_options)
40
+ response = HTTParty.send(method, "#{credentials.host}#{path}", httparty_options)
39
41
  params[:response] = response.inspect.to_s
40
42
 
41
43
  case response.code
@@ -2,31 +2,45 @@ module Harvest
2
2
  module API
3
3
  class Reports < Base
4
4
 
5
+ TIME_FORMAT = '%Y%m%d'
6
+
5
7
  def time_by_project(project, start_date, end_date, options = {})
6
- query = {:from => start_date.strftime("%Y%m%d"), :to => end_date.strftime("%Y%m%d")}
7
- query[:user_id] = options[:user].to_i if options[:user]
8
- query[:billable] = (options[:billable] ? "yes" : "no") unless options[:billable].nil?
9
- query[:updated_since] = options[:updated_since].to_s if options[:updated_since]
8
+ query = { from: start_date.strftime(TIME_FORMAT), to: end_date.strftime(TIME_FORMAT) }
9
+ query[:user_id] = options.delete(:user).to_i if options[:user]
10
+ query[:billable] = (options.delete(:billable) ? "yes" : "no") unless options[:billable].nil?
11
+ query[:updated_since] = options.delete(:updated_since).to_s if options[:updated_since]
12
+ query.update(options)
10
13
 
11
- response = request(:get, credentials, "/projects/#{project.to_i}/entries", :query => query)
14
+ response = request(:get, credentials, "/projects/#{project.to_i}/entries", query: query)
12
15
  Harvest::TimeEntry.parse(JSON.parse(response.body).map {|h| h["day_entry"]})
13
16
  end
14
17
 
15
18
  def time_by_user(user, start_date, end_date, options = {})
16
- query = {:from => start_date.strftime("%Y%m%d"), :to => end_date.strftime("%Y%m%d")}
17
- query[:project_id] = options[:project].to_i if options[:project]
18
- query[:billable] = (options[:billable] ? "yes" : "no") unless options[:billable].nil?
19
- query[:updated_since] = options[:updated_since].to_s if options[:updated_since]
19
+ query = { from: start_date.strftime(TIME_FORMAT), to: end_date.strftime(TIME_FORMAT) }
20
+ query[:project_id] = options.delete(:project).to_i if options[:project]
21
+ query[:billable] = (options.delete(:billable) ? "yes" : "no") unless options[:billable].nil?
22
+ query[:updated_since] = options.delete(:updated_since).to_s if options[:updated_since]
23
+ query.update(options)
20
24
 
21
- response = request(:get, credentials, "/people/#{user.to_i}/entries", :query => query)
25
+ response = request(:get, credentials, "/people/#{user.to_i}/entries", query: query)
22
26
  Harvest::TimeEntry.parse(JSON.parse(response.body).map {|h| h["day_entry"]})
23
27
  end
24
28
 
25
29
  def expenses_by_user(user, start_date, end_date, options = {})
26
- query = {:from => start_date.strftime("%Y%m%d"), :to => end_date.strftime("%Y%m%d")}
27
- query[:updated_since] = options[:updated_since].to_s if options[:updated_since]
30
+ query = { from: start_date.strftime(TIME_FORMAT), to: end_date.strftime(TIME_FORMAT) }
31
+ query[:updated_since] = options.delete(:updated_since).to_s if options[:updated_since]
32
+ query.update(options)
33
+
34
+ response = request(:get, credentials, "/people/#{user.to_i}/expenses", query: query)
35
+ Harvest::Expense.parse(response.parsed_response)
36
+ end
37
+
38
+ def expenses_by_project(project, start_date, end_date, options = {})
39
+ query = { from: start_date.strftime(TIME_FORMAT), to: end_date.strftime(TIME_FORMAT) }
40
+ query[:updated_since] = options.delete(:updated_since).to_s if options[:updated_since]
41
+ query.update(options)
28
42
 
29
- response = request(:get, credentials, "/people/#{user.to_i}/expenses", :query => query)
43
+ response = request(:get, credentials, "/projects/#{project.to_i}/expenses", query: query)
30
44
  Harvest::Expense.parse(response.parsed_response)
31
45
  end
32
46
 
@@ -4,9 +4,14 @@ module Harvest
4
4
 
5
5
  # @see Harvest.client
6
6
  # @see Harvest.hardy_client
7
- def initialize(subdomain, username, password)
8
- @credentials = Credentials.new(subdomain, username, password)
9
- raise InvalidCredentials unless credentials.valid?
7
+ def initialize(subdomain: nil, username: nil, password: nil, access_token: nil)
8
+ @credentials = if subdomain && username && password
9
+ BasicAuthCredentials.new(subdomain: subdomain, username: username, password: password)
10
+ elsif access_token
11
+ OAuthCredentials.new(access_token)
12
+ else
13
+ fail 'You must provide either :subdomain, :username, and :password or an oauth :access_token'
14
+ end
10
15
  end
11
16
 
12
17
  # All API actions surrounding accounts
@@ -1,21 +1,37 @@
1
1
  module Harvest
2
- class Credentials
3
- attr_accessor :subdomain, :username, :password
4
-
5
- def initialize(subdomain, username, password)
2
+ class BasicAuthCredentials
3
+ def initialize(subdomain: nil, username: nil, password: nil)
6
4
  @subdomain, @username, @password = subdomain, username, password
7
5
  end
8
6
 
9
- def valid?
10
- !subdomain.nil? && !username.nil? && !password.nil?
7
+ def set_authentication(request_options)
8
+ request_options[:headers] ||= {}
9
+ request_options[:headers]["Authorization"] = "Basic #{basic_auth}"
11
10
  end
12
11
 
12
+ def host
13
+ "https://#{@subdomain}.harvestapp.com"
14
+ end
15
+
16
+ private
17
+
13
18
  def basic_auth
14
- Base64.encode64("#{username}:#{password}").delete("\r\n")
19
+ Base64.encode64("#{@username}:#{@password}").delete("\r\n")
20
+ end
21
+ end
22
+
23
+ class OAuthCredentials
24
+ def initialize(access_token)
25
+ @access_token = access_token
26
+ end
27
+
28
+ def set_authentication(request_options)
29
+ request_options[:query] ||= {}
30
+ request_options[:query]["access_token"] = @access_token
15
31
  end
16
32
 
17
33
  def host
18
- "https://#{subdomain}.harvestapp.com"
34
+ "https://api.harvestapp.com"
19
35
  end
20
36
  end
21
37
  end
@@ -1,6 +1,4 @@
1
1
  module Harvest
2
- class InvalidCredentials < StandardError; end
3
-
4
2
  class HTTPError < StandardError
5
3
  attr_reader :response
6
4
  attr_reader :params
@@ -12,12 +10,12 @@ module Harvest
12
10
  @hint = hint
13
11
  super(response)
14
12
  end
15
-
13
+
16
14
  def to_s
17
15
  "#{self.class.to_s} : #{response.code} #{response.body}" + (hint ? "\n#{hint}" : "")
18
16
  end
19
17
  end
20
-
18
+
21
19
  class RateLimited < HTTPError; end
22
20
  class NotFound < HTTPError; end
23
21
  class Unavailable < HTTPError; end
@@ -1,3 +1,3 @@
1
1
  module Harvest
2
- VERSION = "2.0.0"
2
+ VERSION = "3.0.0.rc1"
3
3
  end
@@ -29,23 +29,38 @@ module Harvest
29
29
  # Creates a standard client that will raise all errors it encounters
30
30
  #
31
31
  # == Options
32
- # * +:ssl+ - Whether or not to use SSL when connecting to Harvest. This is dependent on whether your account supports it. Set to +true+ by default
32
+ # * Basic Authentication
33
+ # * +:subdomain+ - Your Harvest subdomain
34
+ # * +:username+ - Your Harvest username
35
+ # * +:password+ - Your Harvest password
36
+ # * OAuth
37
+ # * +:access_token+ - An OAuth 2.0 access token
38
+ #
33
39
  # == Examples
34
- # Harvest.client('mysubdomain', 'myusername', 'mypassword', :ssl => false)
40
+ # Harvest.client(subdomain: 'mysubdomain', username: 'myusername', password: 'mypassword')
41
+ # Harvest.client(access_token: 'myaccesstoken')
35
42
  #
36
43
  # @return [Harvest::Base]
37
- def client(subdomain, username, password, options = {})
38
- Harvest::Base.new(subdomain, username, password)
44
+ def client(subdomain: nil, username: nil, password: nil, access_token: nil)
45
+ Harvest::Base.new(subdomain: subdomain, username: username, password: password, access_token: access_token)
39
46
  end
40
47
 
41
48
  # Creates a hardy client that will retry common HTTP errors it encounters and sleep() if it determines it is over your rate limit
42
49
  #
43
50
  # == Options
44
- # * +:ssl+ - Whether or not to use SSL when connecting to Harvest. This is dependent on whether your account supports it. Set to +true+ by default
51
+ # * Basic Authentication
52
+ # * +:subdomain+ - Your Harvest subdomain
53
+ # * +:username+ - Your Harvest username
54
+ # * +:password+ - Your Harvest password
55
+ # * OAuth
56
+ # * +:access_token+ - An OAuth 2.0 access token
57
+ #
45
58
  # * +:retry+ - How many times the hardy client should retry errors. Set to +5+ by default.
46
59
  #
47
60
  # == Examples
48
- # Harvest.hardy_client('mysubdomain', 'myusername', 'mypassword', :ssl => true, :retry => 3)
61
+ # Harvest.hardy_client(subdomain: 'mysubdomain', username: 'myusername', password: 'mypassword', retry: 3)
62
+ #
63
+ # Harvest.hardy_client(access_token: 'myaccesstoken', retries: 3)
49
64
  #
50
65
  # == Errors
51
66
  # The hardy client will retry the following errors
@@ -60,9 +75,8 @@ module Harvest
60
75
  #
61
76
  # @return [Harvest::HardyClient] a Harvest::Base wrapped in a Harvest::HardyClient
62
77
  # @see Harvest::Base
63
- def hardy_client(subdomain, username, password, options = {})
64
- retries = options.delete(:retry)
65
- Harvest::HardyClient.new(client(subdomain, username, password), (retries || 5))
78
+ def hardy_client(subdomain: nil, username: nil, password: nil, access_token: nil, retries: 5)
79
+ Harvest::HardyClient.new(client(subdomain: subdomain, username: username, password: password, access_token: access_token), retries)
66
80
  end
67
81
  end
68
82
  end
@@ -4,52 +4,52 @@ describe 'harvest reporting' do
4
4
  it 'allows project and people entry reporting' do
5
5
  cassette("reports1") do
6
6
  user = harvest.users.create(
7
- "email" => "jane@example.com",
8
- "first_name" => "Jane",
9
- "last_name" => "Doe",
10
- "password" => "secure"
7
+ email: "jane@example.com",
8
+ first_name: "Jane",
9
+ last_name: "Doe",
10
+ password: "secure"
11
11
  )
12
- client = harvest.clients.create("name" => "Tim's Dry Cleaning")
13
- project1 = harvest.projects.create("name" => "Reporting Project1", "client_id" => client.id)
14
- project2 = harvest.projects.create("name" => "Reporting Project2", "client_id" => client.id)
15
- task1 = harvest.tasks.create("name" => "A billable task", "default_hourly_rate" => 120, "billable_by_default" => true)
16
- task2 = harvest.tasks.create("name" => "A non billable task", "billable_by_default" => false)
12
+ client = harvest.clients.create(name: "Tim's Dry Cleaning")
13
+ project1 = harvest.projects.create(name: "Reporting Project1", client_id: client.id)
14
+ project2 = harvest.projects.create(name: "Reporting Project2", client_id: client.id)
15
+ task1 = harvest.tasks.create(name: "A billable task", "default_hourly_rate" => 120, "billable_by_default" => true)
16
+ task2 = harvest.tasks.create(name: "A non billable task", "billable_by_default" => false)
17
17
 
18
- harvest.task_assignments.create("project" => project1, "task" => task1)
19
- harvest.task_assignments.create("project" => project2, "task" => task2)
20
- harvest.user_assignments.create("project" => project1, "user" => user)
21
- harvest.user_assignments.create("project" => project2, "user" => user)
18
+ harvest.task_assignments.create(project: project1, task: task1)
19
+ harvest.task_assignments.create(project: project2, task: task2)
20
+ harvest.user_assignments.create(project: project1, user: user)
21
+ harvest.user_assignments.create(project: project2, user: user)
22
22
 
23
- entry1 = harvest.time.create("notes" => "billable entry1", "hours" => 3, "spent_at" => "2009/12/28", "task_id" => task1.id, "project_id" => project1.id, "of_user" => user.id)
24
- entry2 = harvest.time.create("notes" => "non billable entry2", "hours" => 6, "spent_at" => "2009/12/28", "task_id" => task2.id, "project_id" => project2.id, "of_user" => user.id)
23
+ entry1 = harvest.time.create(notes: "billable entry1", hours: 3, spent_at: "2009/12/28", task_id: task1.id, project_id: project1.id, of_user: user.id)
24
+ entry2 = harvest.time.create(notes: "non billable entry2", hours: 6, spent_at: "2009/12/28", task_id: task2.id, project_id: project2.id, of_user: user.id)
25
25
 
26
26
  harvest.reports.time_by_project(project1, Time.utc(2009, 12, 20), Time.utc(2009,12,30)).first.should == entry1
27
27
 
28
- harvest.reports.time_by_project(project1, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :user => user).first.should == entry1
28
+ harvest.reports.time_by_project(project1, Time.utc(2009, 12, 20), Time.utc(2009,12,30), user: user).first.should == entry1
29
29
 
30
- harvest.reports.time_by_project(project2, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :user => user).first.should == entry2
30
+ harvest.reports.time_by_project(project2, Time.utc(2009, 12, 20), Time.utc(2009,12,30), user: user).first.should == entry2
31
31
 
32
- harvest.reports.time_by_project(project2, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :billable => true).should == []
33
- harvest.reports.time_by_project(project2, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :billable => false).first.should == entry2
32
+ harvest.reports.time_by_project(project2, Time.utc(2009, 12, 20), Time.utc(2009,12,30), billable: true).should == []
33
+ harvest.reports.time_by_project(project2, Time.utc(2009, 12, 20), Time.utc(2009,12,30), billable: false).first.should == entry2
34
34
 
35
- harvest.reports.time_by_project(project1, Time.utc(2009, 12, 10), Time.utc(2009,12,30), {:updated_since => Time.now.utc}).should == []
35
+ harvest.reports.time_by_project(project1, Time.utc(2009, 12, 10), Time.utc(2009,12,30), {updated_since: Time.now.utc}).should == []
36
36
 
37
37
  harvest.reports.time_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30)).map(&:id).should == [entry1, entry2].map(&:id)
38
38
 
39
- harvest.reports.time_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :project => project1).first.should == entry1
39
+ harvest.reports.time_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30), project: project1).first.should == entry1
40
40
 
41
- harvest.reports.time_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :project => project2).first.should == entry2
41
+ harvest.reports.time_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30), project: project2).first.should == entry2
42
42
 
43
- harvest.reports.time_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :billable => true).first.should == entry1
44
- harvest.reports.time_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :billable => false).first.should == entry2
43
+ harvest.reports.time_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30), billable: true).first.should == entry1
44
+ harvest.reports.time_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30), billable: false).first.should == entry2
45
45
 
46
46
  entry3_time = Time.now.utc
47
- entry3 = harvest.time.create("notes" => "test entry for checking updated_since", "hours" => 5, "spent_at" => "2009/12/15", "task_id" => task1.id, "project_id" => project1.id, "of_user" => user.id)
48
- entries = harvest.reports.time_by_user(user, Time.utc(2009, 12, 10), Time.utc(2009,12,30), {:project => project1, :updated_since => entry3_time})
47
+ entry3 = harvest.time.create(notes: "test entry for checking updated_since", hours: 5, spent_at: "2009/12/15", task_id: task1.id, project_id: project1.id, of_user: user.id)
48
+ entries = harvest.reports.time_by_user(user, Time.utc(2009, 12, 10), Time.utc(2009,12,30), {project: project1, updated_since: entry3_time})
49
49
  entries.first.should == entry3
50
50
  entries.size.should == 1
51
51
 
52
- client2 = harvest.clients.create("name" => "Phil's Sandwich Shop")
52
+ client2 = harvest.clients.create(name: "Phil's Sandwich Shop")
53
53
  harvest.reports.projects_by_client(client).map(&:id).to_set.should == [project1, project2].map(&:id).to_set
54
54
  harvest.reports.projects_by_client(client2).should == []
55
55
  end
@@ -58,32 +58,36 @@ describe 'harvest reporting' do
58
58
  it 'allows expense reporting' do
59
59
  cassette("reports2") do
60
60
  user = harvest.users.create(
61
- "email" => "simon@example.com",
62
- "first_name" => "Simon",
63
- "last_name" => "Stir",
64
- "password" => "secure"
61
+ email: "simon@example.com",
62
+ first_name: "Simon",
63
+ last_name: "Stir",
64
+ password: "secure"
65
65
  )
66
66
 
67
- category = harvest.expense_categories.create("name" => "Reporting category", "unit_price" => 100, "unit_name" => "deduction")
68
- client = harvest.clients.create("name" => "Philip's Butcher")
69
- project = harvest.projects.create("name" => "Expense Reporting Project", "client_id" => client.id)
70
- harvest.user_assignments.create("project" => project, "user" => user)
67
+ category = harvest.expense_categories.create(name: "Reporting category", "unit_price" => 100, "unit_name" => "deduction")
68
+ client = harvest.clients.create(name: "Philip's Butcher")
69
+ project = harvest.projects.create(name: "Expense Reporting Project", client_id: client.id)
70
+ harvest.user_assignments.create(project: project, user: user)
71
71
 
72
72
  expense = harvest.expenses.create({
73
- "notes" => "Drive to Chicago",
74
- "total_cost" => 75.0,
75
- "spent_at" => Time.utc(2009, 12, 28),
76
- "expense_category_id" => category.id,
77
- "project_id" => project.id
73
+ notes: "Drive to Chicago",
74
+ total_cost: 75.0,
75
+ spent_at: Time.utc(2009, 12, 28),
76
+ expense_category_id: category.id,
77
+ project_id: project.id
78
78
  }, user.id)
79
79
 
80
+ harvest.reports.expenses_by_project(project, Time.utc(2009, 12, 20), Time.utc(2009,12,30)).first.should == expense
80
81
  harvest.reports.expenses_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30)).first.should == expense
81
82
 
82
- harvest.reports.expenses_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30), {:updated_since => Time.now.utc}).should == []
83
+ harvest.reports.expenses_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30), {updated_since: Time.now.utc}).should == []
83
84
 
84
85
  my_user = harvest.users.all.detect {|u| u.email == credentials["username"]}
85
86
  my_user.should_not be_nil
86
87
  harvest.reports.expenses_by_user(my_user, Time.utc(2009, 12, 20), Time.utc(2009,12,30)).should == []
88
+
89
+ my_project = harvest.projects.create(name: "Travel Expense Project", client_id: client.id)
90
+ harvest.reports.expenses_by_project(my_project, Time.utc(2009, 12, 20), Time.utc(2009,12,30)).should == []
87
91
  end
88
92
  end
89
93
  end
@@ -44,24 +44,6 @@ describe 'harvest users' do
44
44
  end
45
45
  end
46
46
 
47
- it "allows password resets" do
48
- cassette("users3") do
49
- pending "Something is wrong with resetting passwords"
50
- user = harvest.users.create(
51
- "first_name" => "Timmy",
52
- "last_name" => "Ruth",
53
- "email" => "timmy@ruth.com",
54
- "timezone" => "cst",
55
- "is_admin" => "false",
56
- "telephone" => "444-4444"
57
- )
58
- user.should be_active
59
-
60
- harvest.users.reset_password(user) # nothing else to assert
61
- harvest.users.delete(user)
62
- end
63
- end
64
-
65
47
  context "assignments" do
66
48
  it "allows adding, updating, and removing users from projects" do
67
49
  cassette('users4') do
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Harvest::Base do
4
4
  describe "username/password errors" do
5
5
  it "should raise error if missing a credential" do
6
- lambda { Harvest::Base.new("subdomain", nil, "secure") }.should raise_error(Harvest::InvalidCredentials)
6
+ lambda { Harvest::Base.new(subdomain: "subdomain", password: "secure") }.should raise_error(/must provide either/i)
7
7
  end
8
8
  end
9
9
  end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Harvest::BasicAuthCredentials do
4
+ context '#set_authentication' do
5
+ it "should set the headers Authorization" do
6
+ credentials = Harvest::BasicAuthCredentials.new(subdomain: 'some-domain', username: 'username', password: 'password')
7
+ credentials.set_authentication(options = {})
8
+ options[:headers]['Authorization'].should == "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Harvest::OAuthCredentials do
4
+ context '#set_authentication' do
5
+ it "should set the access token in the query" do
6
+ credentials = Harvest::OAuthCredentials.new('theaccesstoken')
7
+ credentials.set_authentication(options = {})
8
+ options[:query]['access_token'].should == 'theaccesstoken'
9
+ end
10
+ end
11
+ end
@@ -2,6 +2,4 @@ require 'spec_helper'
2
2
 
3
3
  describe Harvest::Task do
4
4
  it_behaves_like 'a json sanitizer', %w(cache_version)
5
-
6
- it 'casts default_hourly_rate to float'
7
- end
5
+ end
@@ -6,23 +6,23 @@ module HarvestedHelpers
6
6
  def credentials; HarvestedHelpers.credentials; end
7
7
 
8
8
  def self.simple_harvest
9
- Harvest.client(credentials["subdomain"], credentials["username"], credentials["password"])
9
+ Harvest.client(subdomain: credentials["subdomain"], username: credentials["username"], password: credentials["password"])
10
10
  end
11
11
 
12
12
  def harvest; @harvest ||= HarvestedHelpers.simple_harvest; end
13
13
 
14
14
  def hardy_harvest
15
- Harvest.hardy_client(credentials["subdomain"], credentials["username"], credentials["password"])
15
+ Harvest.hardy_client(subdomain: credentials["subdomain"], username: credentials["username"], password: credentials["password"])
16
16
  end
17
17
 
18
18
  def self.clean_remote
19
19
  harvest = simple_harvest
20
20
  harvest.users.all.each do |u|
21
- harvest.reports.expenses_by_user(u, Time.utc(2000, 1, 1), Time.utc(2013, 6,21)).each do |expense|
21
+ harvest.reports.expenses_by_user(u, Time.utc(2000, 1, 1), Time.utc(2014, 6, 21)).each do |expense|
22
22
  harvest.expenses.delete(expense, u)
23
23
  end
24
24
 
25
- harvest.reports.time_by_user(u, Time.utc(2000, 1, 1), Time.utc(2013, 6,21)).each do |time|
25
+ harvest.reports.time_by_user(u, Time.utc(2000, 1, 1), Time.utc(2014, 6, 21)).each do |time|
26
26
  harvest.time.delete(time, u)
27
27
  end
28
28
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: harvested
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zach Moazeni
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-23 00:00:00.000000000 Z
11
+ date: 2014-05-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -65,7 +65,7 @@ files:
65
65
  - ".gitignore"
66
66
  - ".ruby-version"
67
67
  - Gemfile
68
- - HISTORY
68
+ - HISTORY.md
69
69
  - MIT-LICENSE
70
70
  - README.md
71
71
  - Rakefile
@@ -136,11 +136,12 @@ files:
136
136
  - spec/functional/time_tracking_spec.rb
137
137
  - spec/functional/users_spec.rb
138
138
  - spec/harvest/base_spec.rb
139
- - spec/harvest/credentials_spec.rb
139
+ - spec/harvest/basic_auth_credentials_spec.rb
140
140
  - spec/harvest/expense_category_spec.rb
141
141
  - spec/harvest/expense_spec.rb
142
142
  - spec/harvest/invoice_payment_spec.rb
143
143
  - spec/harvest/invoice_spec.rb
144
+ - spec/harvest/oauth_credentials_spec.rb
144
145
  - spec/harvest/project_spec.rb
145
146
  - spec/harvest/task_assignment_spec.rb
146
147
  - spec/harvest/task_spec.rb
@@ -165,12 +166,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
165
166
  requirements:
166
167
  - - ">="
167
168
  - !ruby/object:Gem::Version
168
- version: '0'
169
+ version: '2.0'
169
170
  required_rubygems_version: !ruby/object:Gem::Requirement
170
171
  requirements:
171
- - - ">="
172
+ - - ">"
172
173
  - !ruby/object:Gem::Version
173
- version: '0'
174
+ version: 1.3.1
174
175
  requirements: []
175
176
  rubyforge_project:
176
177
  rubygems_version: 2.2.2
@@ -192,11 +193,12 @@ test_files:
192
193
  - spec/functional/time_tracking_spec.rb
193
194
  - spec/functional/users_spec.rb
194
195
  - spec/harvest/base_spec.rb
195
- - spec/harvest/credentials_spec.rb
196
+ - spec/harvest/basic_auth_credentials_spec.rb
196
197
  - spec/harvest/expense_category_spec.rb
197
198
  - spec/harvest/expense_spec.rb
198
199
  - spec/harvest/invoice_payment_spec.rb
199
200
  - spec/harvest/invoice_spec.rb
201
+ - spec/harvest/oauth_credentials_spec.rb
200
202
  - spec/harvest/project_spec.rb
201
203
  - spec/harvest/task_assignment_spec.rb
202
204
  - spec/harvest/task_spec.rb
@@ -1,22 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Harvest::Credentials do
4
- describe "#valid?" do
5
- it "should return true if domain, username, and password is filled out" do
6
- Harvest::Credentials.new("some-domain", "username", "password").should be_valid
7
- end
8
-
9
- it "should return false if either domain, username, or password is nil" do
10
- Harvest::Credentials.new("some-domain", "username", nil).should_not be_valid
11
- Harvest::Credentials.new("some-domain", nil, "password").should_not be_valid
12
- Harvest::Credentials.new(nil, "username", "password").should_not be_valid
13
- Harvest::Credentials.new(nil, nil, nil).should_not be_valid
14
- end
15
- end
16
-
17
- describe "#basic_auth" do
18
- it "should base64 encode the credentials" do
19
- Harvest::Credentials.new("some-domain", "username", "password").basic_auth.should == "dXNlcm5hbWU6cGFzc3dvcmQ="
20
- end
21
- end
22
- end