harvested 2.0.0 → 3.0.0.rc1

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.
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