noko 0.0.0 → 1.4.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.
- checksums.yaml +4 -4
- data/LICENSE.txt +22 -0
- data/README.md +40 -0
- data/lib/noko.rb +11 -0
- data/lib/noko/client.rb +83 -0
- data/lib/noko/client/account.rb +7 -0
- data/lib/noko/client/current_user.rb +19 -0
- data/lib/noko/client/entries.rb +23 -0
- data/lib/noko/client/expenses.rb +23 -0
- data/lib/noko/client/invoices.rb +39 -0
- data/lib/noko/client/project_groups.rb +31 -0
- data/lib/noko/client/projects.rb +55 -0
- data/lib/noko/client/tags.rb +35 -0
- data/lib/noko/client/timers.rb +31 -0
- data/lib/noko/client/users.rb +39 -0
- data/lib/noko/errors.rb +5 -0
- data/lib/noko/link_header.rb +16 -0
- data/lib/noko/params.rb +27 -0
- data/lib/noko/record.rb +31 -0
- data/lib/noko/version.rb +3 -0
- data/noko.gemspec +17 -0
- data/test/noko/client/account_test.rb +9 -0
- data/test/noko/client/current_user_test.rb +39 -0
- data/test/noko/client/entries_test.rb +45 -0
- data/test/noko/client/errors_test.rb +27 -0
- data/test/noko/client/expenses_test.rb +45 -0
- data/test/noko/client/invoices_test.rb +75 -0
- data/test/noko/client/options_test.rb +27 -0
- data/test/noko/client/project_groups_test.rb +63 -0
- data/test/noko/client/projects_test.rb +99 -0
- data/test/noko/client/response_test.rb +39 -0
- data/test/noko/client/tags_test.rb +63 -0
- data/test/noko/client/timers_test.rb +57 -0
- data/test/noko/client/users_test.rb +75 -0
- data/test/noko/client_test.rb +59 -0
- data/test/noko/record_test.rb +28 -0
- metadata +85 -8
@@ -0,0 +1,39 @@
|
|
1
|
+
module Noko
|
2
|
+
class Client
|
3
|
+
def get_users(params = nil)
|
4
|
+
get('/v2/users', params)
|
5
|
+
end
|
6
|
+
|
7
|
+
def get_user(id)
|
8
|
+
get("/v2/users/#{id}")
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_user_entries(id, params = nil)
|
12
|
+
get("/v2/users/#{id}/entries", params)
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_user_expenses(id, params = nil)
|
16
|
+
get("/v2/users/#{id}/expenses", params)
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_user(attributes)
|
20
|
+
post('/v2/users', attributes)
|
21
|
+
end
|
22
|
+
|
23
|
+
def update_user(id, attributes)
|
24
|
+
put("/v2/users/#{id}", attributes)
|
25
|
+
end
|
26
|
+
|
27
|
+
def delete_user(id)
|
28
|
+
delete("/v2/users/#{id}")
|
29
|
+
end
|
30
|
+
|
31
|
+
def reactivate_user(id)
|
32
|
+
put("/v2/users/#{id}/activate")
|
33
|
+
end
|
34
|
+
|
35
|
+
def deactivate_user(id)
|
36
|
+
put("/v2/users/#{id}/deactivate")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/noko/errors.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'noko/record'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Noko
|
5
|
+
module LinkHeader # :nodoc:
|
6
|
+
extend self
|
7
|
+
|
8
|
+
REGEXP = /<([^>]+)>; rel="(\w+)"/
|
9
|
+
|
10
|
+
def parse(string)
|
11
|
+
string.scan(REGEXP).each_with_object(Record.new) do |(uri, rel), record|
|
12
|
+
record[rel.to_sym] = URI.parse(uri).request_uri
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/noko/params.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module Noko
|
4
|
+
module Params # :nodoc:
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def join(path, params = nil)
|
8
|
+
return path if params.nil? || params.empty?
|
9
|
+
|
10
|
+
path + '?' + encode(params)
|
11
|
+
end
|
12
|
+
|
13
|
+
def encode(params)
|
14
|
+
params.map { |k, v| "#{escape(k)}=#{array_escape(v)}" }.join('&')
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def array_escape(object)
|
20
|
+
Array(object).map { |value| escape(value) }.join(',')
|
21
|
+
end
|
22
|
+
|
23
|
+
def escape(component)
|
24
|
+
CGI.escape(component.to_s)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/noko/record.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Noko
|
2
|
+
class Record
|
3
|
+
def initialize(attributes = {})
|
4
|
+
@attributes = attributes
|
5
|
+
end
|
6
|
+
|
7
|
+
def [](name)
|
8
|
+
@attributes[name]
|
9
|
+
end
|
10
|
+
|
11
|
+
def []=(name, value)
|
12
|
+
@attributes[name] = value
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(name, *args, &block)
|
16
|
+
if @attributes.has_key?(name) && args.empty? && block.nil?
|
17
|
+
return @attributes[name]
|
18
|
+
else
|
19
|
+
super name, *args, &block
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def respond_to_missing?(name, include_private = false)
|
24
|
+
@attributes.has_key?(name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_h
|
28
|
+
@attributes
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/noko/version.rb
ADDED
data/noko.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'noko'
|
3
|
+
s.version = '1.4.0'
|
4
|
+
s.license = 'MIT'
|
5
|
+
s.platform = Gem::Platform::RUBY
|
6
|
+
s.authors = ['Tim Craft']
|
7
|
+
s.email = ['mail@timcraft.com']
|
8
|
+
s.homepage = 'https://github.com/timcraft/noko'
|
9
|
+
s.description = 'Ruby client for Version 2 of the Noko/Freckle API'
|
10
|
+
s.summary = 'Ruby client for Version 2 of the Noko/Freckle API'
|
11
|
+
s.files = Dir.glob('{lib,test}/**/*') + %w(LICENSE.txt README.md noko.gemspec)
|
12
|
+
s.required_ruby_version = '>= 1.9.3'
|
13
|
+
s.add_development_dependency('rake', '~> 12')
|
14
|
+
s.add_development_dependency('webmock', '~> 3.0')
|
15
|
+
s.add_development_dependency('minitest', '~> 5.0')
|
16
|
+
s.require_path = 'lib'
|
17
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative '../client_test'
|
2
|
+
|
3
|
+
class ClientCurrentUserTest < ClientTest
|
4
|
+
def test_get_current_user
|
5
|
+
expect_request(:get, "#{base_url}/current_user").with(auth_header).to_return(json_response)
|
6
|
+
|
7
|
+
assert_instance_of Noko::Record, client.get_current_user
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_get_current_user_entries
|
11
|
+
expect_request(:get, "#{base_url}/current_user/entries").with(auth_header).to_return(json_array_response)
|
12
|
+
|
13
|
+
assert_equal [], client.get_current_user_entries
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_get_current_user_entries_encodes_params
|
17
|
+
expect_request(:get, "#{base_url}/current_user/entries?billable=true")
|
18
|
+
|
19
|
+
client.get_current_user_entries(billable: true)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_get_current_user_expenses
|
23
|
+
expect_request(:get, "#{base_url}/current_user/expenses").with(auth_header).to_return(json_array_response)
|
24
|
+
|
25
|
+
assert_equal [], client.get_current_user_expenses
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_get_current_user_expenses_encodes_params
|
29
|
+
expect_request(:get, "#{base_url}/current_user/expenses?invoiced=true")
|
30
|
+
|
31
|
+
client.get_current_user_expenses(invoiced: true)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_update_current_user
|
35
|
+
expect_request(:put, "#{base_url}/current_user").with(json_request).to_return(json_response)
|
36
|
+
|
37
|
+
assert_instance_of Noko::Record, client.update_current_user(week_start: 'monday')
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative '../client_test'
|
2
|
+
|
3
|
+
class ClientEntriesTest < ClientTest
|
4
|
+
def test_get_entries
|
5
|
+
expect_request(:get, "#{base_url}/entries").with(auth_header).to_return(json_array_response)
|
6
|
+
|
7
|
+
assert_equal [], client.get_entries
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_get_entries_encodes_params
|
11
|
+
expect_request(:get, "#{base_url}/entries?billable=true")
|
12
|
+
|
13
|
+
client.get_entries(billable: true)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_get_entries_encodes_project_ids
|
17
|
+
expect_request(:get, "#{base_url}/entries?project_ids=123,456,789")
|
18
|
+
|
19
|
+
client.get_entries(project_ids: ids)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_get_entry
|
23
|
+
expect_request(:get, "#{base_url}/entries/#{id}").with(auth_header).to_return(json_response)
|
24
|
+
|
25
|
+
assert_instance_of Noko::Record, client.get_entry(id)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_create_entry
|
29
|
+
expect_request(:post, "#{base_url}/entries").with(json_request).to_return(json_response.merge(status: 201))
|
30
|
+
|
31
|
+
assert_instance_of Noko::Record, client.create_entry(date: Date.today, minutes: 60)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_update_entry
|
35
|
+
expect_request(:put, "#{base_url}/entries/#{id}").with(json_request).to_return(json_response)
|
36
|
+
|
37
|
+
assert_instance_of Noko::Record, client.update_entry(id, minutes: 120)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_delete_entry
|
41
|
+
expect_request(:delete, "#{base_url}/entries/#{id}").with(auth_header).to_return(status: 204)
|
42
|
+
|
43
|
+
assert_equal :no_content, client.delete_entry(id)
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative '../client_test'
|
2
|
+
|
3
|
+
class ClientErrorsTest < ClientTest
|
4
|
+
def test_bad_request_errors
|
5
|
+
message = 'The Project cannot be deleted because it has entries, expenses, or invoices.'
|
6
|
+
|
7
|
+
response = json_response.merge(status: 400, body: %({"message":"#{message}"}))
|
8
|
+
|
9
|
+
stub_request(:delete, "#{base_url}/projects/#{id}").to_return(response)
|
10
|
+
|
11
|
+
exception = assert_raises(Noko::Error) do
|
12
|
+
client.delete_project(id)
|
13
|
+
end
|
14
|
+
|
15
|
+
assert_includes exception.message, message
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_authentication_errors
|
19
|
+
response = json_response.merge(status: 401)
|
20
|
+
|
21
|
+
stub_request(:get, "#{base_url}/entries").to_return(response)
|
22
|
+
|
23
|
+
assert_raises(Noko::AuthenticationError) do
|
24
|
+
client.get_entries
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative '../client_test'
|
2
|
+
|
3
|
+
class ClientExpensesTest < ClientTest
|
4
|
+
def test_get_expenses
|
5
|
+
expect_request(:get, "#{base_url}/expenses").with(auth_header).to_return(json_array_response)
|
6
|
+
|
7
|
+
assert_equal [], client.get_expenses
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_get_expenses_encodes_params
|
11
|
+
expect_request(:get, "#{base_url}/expenses?invoiced=true")
|
12
|
+
|
13
|
+
client.get_expenses(invoiced: true)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_get_expenses_encodes_ids
|
17
|
+
expect_request(:get, "#{base_url}/expenses?project_ids=123,456,789")
|
18
|
+
|
19
|
+
client.get_expenses(project_ids: ids)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_get_expense
|
23
|
+
expect_request(:get, "#{base_url}/expenses/#{id}").with(auth_header).to_return(json_response)
|
24
|
+
|
25
|
+
assert_instance_of Noko::Record, client.get_expense(id)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_create_expense
|
29
|
+
expect_request(:post, "#{base_url}/expenses").with(json_request).to_return(json_response.merge(status: 201))
|
30
|
+
|
31
|
+
assert_instance_of Noko::Record, client.create_expense(date: Date.today, project_id: id, price: '14.55')
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_update_expense
|
35
|
+
expect_request(:put, "#{base_url}/expenses/#{id}").with(json_request).to_return(json_response)
|
36
|
+
|
37
|
+
assert_instance_of Noko::Record, client.update_expense(id, price: '19.99')
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_delete_expense
|
41
|
+
expect_request(:delete, "#{base_url}/expenses/#{id}").with(auth_header).to_return(status: 204)
|
42
|
+
|
43
|
+
assert_equal :no_content, client.delete_expense(id)
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require_relative '../client_test'
|
2
|
+
|
3
|
+
class ClientInvoicesTest < ClientTest
|
4
|
+
def test_get_invoices
|
5
|
+
expect_request(:get, "#{base_url}/invoices").with(auth_header).to_return(json_array_response)
|
6
|
+
|
7
|
+
assert_equal [], client.get_invoices
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_get_invoices_encodes_params
|
11
|
+
expect_request(:get, "#{base_url}/invoices?state=unpaid")
|
12
|
+
|
13
|
+
client.get_invoices(state: :unpaid)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_get_invoice
|
17
|
+
expect_request(:get, "#{base_url}/invoices/#{id}").with(auth_header).to_return(json_response)
|
18
|
+
|
19
|
+
assert_instance_of Noko::Record, client.get_invoice(id)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_create_invoice
|
23
|
+
expect_request(:post, "#{base_url}/invoices").with(json_request).to_return(json_response.merge(status: 201))
|
24
|
+
|
25
|
+
assert_instance_of Noko::Record, client.create_invoice(invoice_date: Date.today)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_update_invoice
|
29
|
+
expect_request(:put, "#{base_url}/invoices/#{id}").with(json_request).to_return(json_response)
|
30
|
+
|
31
|
+
assert_instance_of Noko::Record, client.update_invoice(id, reference: 'AB 0001')
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_mark_invoice_paid
|
35
|
+
expect_request(:put, "#{base_url}/invoices/#{id}/paid").with(auth_header).to_return(status: 204)
|
36
|
+
|
37
|
+
assert_equal :no_content, client.mark_invoice_paid(id)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_mark_invoice_unpaid
|
41
|
+
expect_request(:put, "#{base_url}/invoices/#{id}/unpaid").with(auth_header).to_return(status: 204)
|
42
|
+
|
43
|
+
assert_equal :no_content, client.mark_invoice_unpaid(id)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_get_invoice_entries
|
47
|
+
expect_request(:get, "#{base_url}/invoices/#{id}/entries").with(auth_header).to_return(json_array_response)
|
48
|
+
|
49
|
+
assert_equal [], client.get_invoice_entries(id)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_get_invoice_entries_encodes_params
|
53
|
+
expect_request(:get, "#{base_url}/invoices/#{id}/entries?billable=true")
|
54
|
+
|
55
|
+
client.get_invoice_entries(id, billable: true)
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_get_invoice_expenses
|
59
|
+
expect_request(:get, "#{base_url}/invoices/#{id}/expenses").with(auth_header).to_return(json_array_response)
|
60
|
+
|
61
|
+
assert_equal [], client.get_invoice_expenses(id)
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_get_invoice_expenses_encodes_params
|
65
|
+
expect_request(:get, "#{base_url}/invoices/#{id}/expenses?invoiced=false")
|
66
|
+
|
67
|
+
client.get_invoice_expenses(id, invoiced: false)
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_delete_invoice
|
71
|
+
expect_request(:delete, "#{base_url}/invoices/#{id}").with(auth_header).to_return(status: 204)
|
72
|
+
|
73
|
+
assert_equal :no_content, client.delete_invoice(id)
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative '../client_test'
|
2
|
+
|
3
|
+
class ClientOptionsTest < ClientTest
|
4
|
+
def user_agent
|
5
|
+
'account.nokotime.com'
|
6
|
+
end
|
7
|
+
|
8
|
+
def access_token
|
9
|
+
'oauth2-access-token'
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_user_agent_option
|
13
|
+
client = Noko::Client.new(token: token, user_agent: user_agent)
|
14
|
+
|
15
|
+
expect_request(:get, "#{base_url}/entries").with(headers: {'User-Agent' => user_agent})
|
16
|
+
|
17
|
+
client.get_entries
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_access_token_option
|
21
|
+
client = Noko::Client.new(access_token: access_token)
|
22
|
+
|
23
|
+
expect_request(:get, "#{base_url}/entries").with(headers: {'Authorization' => 'token oauth2-access-token'})
|
24
|
+
|
25
|
+
client.get_entries
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative '../client_test'
|
2
|
+
|
3
|
+
class ClientProjectGroupsTest < ClientTest
|
4
|
+
def test_get_project_groups
|
5
|
+
expect_request(:get, "#{base_url}/project_groups").with(auth_header).to_return(json_array_response)
|
6
|
+
|
7
|
+
assert_equal [], client.get_project_groups
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_get_project_groups_encodes_params
|
11
|
+
expect_request(:get, "#{base_url}/project_groups?name=Sprockets")
|
12
|
+
|
13
|
+
client.get_project_groups(name: 'Sprockets')
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_get_project_group
|
17
|
+
expect_request(:get, "#{base_url}/project_groups/#{id}").with(auth_header).to_return(json_response)
|
18
|
+
|
19
|
+
assert_instance_of Noko::Record, client.get_project_group(id)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_create_project_group
|
23
|
+
expect_request(:post, "#{base_url}/project_groups").with(json_request).to_return(json_response.merge(status: 201))
|
24
|
+
|
25
|
+
assert_instance_of Noko::Record, client.create_project_group(name: 'Sprockets, Inc.')
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_get_project_group_entries
|
29
|
+
expect_request(:get, "#{base_url}/project_groups/#{id}/entries").with(auth_header).to_return(json_array_response)
|
30
|
+
|
31
|
+
assert_equal [], client.get_project_group_entries(id)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_get_project_group_entries_encodes_params
|
35
|
+
expect_request(:get, "#{base_url}/project_groups/#{id}/entries?billable=true")
|
36
|
+
|
37
|
+
client.get_project_group_entries(id, billable: true)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_get_project_group_projects
|
41
|
+
expect_request(:get, "#{base_url}/project_groups/#{id}/projects").with(auth_header).to_return(json_array_response)
|
42
|
+
|
43
|
+
assert_equal [], client.get_project_group_projects(id)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_get_project_group_projects_encodes_params
|
47
|
+
expect_request(:get, "#{base_url}/project_groups/#{id}/projects?billable=true")
|
48
|
+
|
49
|
+
client.get_project_group_projects(id, billable: true)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_update_project_group
|
53
|
+
expect_request(:put, "#{base_url}/project_groups/#{id}").with(json_request).to_return(json_response)
|
54
|
+
|
55
|
+
assert_instance_of Noko::Record, client.update_project_group(id, name: 'Sprockets, Inc.')
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_delete_project_group
|
59
|
+
expect_request(:delete, "#{base_url}/project_groups/#{id}").with(auth_header).to_return(status: 204)
|
60
|
+
|
61
|
+
assert_equal :no_content, client.delete_project_group(id)
|
62
|
+
end
|
63
|
+
end
|