toggl_api 0.0.2
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 +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/LICENSE.txt +22 -0
- data/README.md +48 -0
- data/Rakefile +1 -0
- data/lib/toggl_api.rb +7 -0
- data/lib/toggl_api/api/client.rb +42 -0
- data/lib/toggl_api/api/project.rb +46 -0
- data/lib/toggl_api/api/project_user.rb +38 -0
- data/lib/toggl_api/api/tag.rb +24 -0
- data/lib/toggl_api/api/task.rb +38 -0
- data/lib/toggl_api/api/time_entry.rb +73 -0
- data/lib/toggl_api/api/user.rb +46 -0
- data/lib/toggl_api/api/workspace.rb +36 -0
- data/lib/toggl_api/api/workspace_user.rb +32 -0
- data/lib/toggl_api/base.rb +39 -0
- data/lib/toggl_api/error.rb +8 -0
- data/lib/toggl_api/report.rb +30 -0
- data/lib/toggl_api/request.rb +100 -0
- data/lib/toggl_api/version.rb +3 -0
- data/test/base_test.rb +38 -0
- data/test/fixtures/authentication.json +44 -0
- data/test/fixtures/bulk_update_tasks.json +24 -0
- data/test/fixtures/bulk_update_time_entries.json +29 -0
- data/test/fixtures/client.json +11 -0
- data/test/fixtures/client_projects.json +21 -0
- data/test/fixtures/clients.json +15 -0
- data/test/fixtures/create_client.json +8 -0
- data/test/fixtures/create_project.json +13 -0
- data/test/fixtures/create_project_user.json +0 -0
- data/test/fixtures/create_tag.json +7 -0
- data/test/fixtures/create_task.json +10 -0
- data/test/fixtures/create_time_entry.json +13 -0
- data/test/fixtures/invite_users_to_workspace.json +12 -0
- data/test/fixtures/project.json +13 -0
- data/test/fixtures/project_users.json +18 -0
- data/test/fixtures/relations_of_workspace_and_user.json +22 -0
- data/test/fixtures/report_failure.json +8 -0
- data/test/fixtures/report_success.json +6 -0
- data/test/fixtures/reset_api_token.json +2 -0
- data/test/fixtures/signup.json +19 -0
- data/test/fixtures/start_time_entry.json +13 -0
- data/test/fixtures/stop_time_entry.json +13 -0
- data/test/fixtures/task.json +10 -0
- data/test/fixtures/time_entries.json +24 -0
- data/test/fixtures/time_entry.json +15 -0
- data/test/fixtures/update_client.json +9 -0
- data/test/fixtures/update_project.json +12 -0
- data/test/fixtures/update_project_user.json +0 -0
- data/test/fixtures/update_tag.json +7 -0
- data/test/fixtures/update_task.json +13 -0
- data/test/fixtures/update_time_entry.json +15 -0
- data/test/fixtures/update_user.json +31 -0
- data/test/fixtures/update_workspace_user.json +9 -0
- data/test/fixtures/workspace_clients.json +19 -0
- data/test/fixtures/workspace_projects.json +21 -0
- data/test/fixtures/workspace_tasks.json +29 -0
- data/test/fixtures/workspace_users.json +47 -0
- data/test/fixtures/workspaces.json +12 -0
- data/test/helper.rb +10 -0
- data/test/test_client.rb +51 -0
- data/test/test_project.rb +94 -0
- data/test/test_report.rb +53 -0
- data/test/test_tag.rb +30 -0
- data/test/test_task.rb +51 -0
- data/test/test_time_entry.rb +69 -0
- data/test/test_user.rb +53 -0
- data/test/test_workspace.rb +72 -0
- data/toggl_api.gemspec +30 -0
- metadata +260 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Toggl
|
|
2
|
+
module Api
|
|
3
|
+
module Workspace
|
|
4
|
+
|
|
5
|
+
# *name*: (string, required)
|
|
6
|
+
# premium: If it's a pro workspace or not. Shows if someone is paying for the workspace or not (boolean, not required)
|
|
7
|
+
# at: timestamp that is sent in the response, indicates the time item was last updated
|
|
8
|
+
|
|
9
|
+
def workspaces
|
|
10
|
+
get "/workspaces"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get_workspace_users(wid)
|
|
14
|
+
get "/workspaces/#{wid}/users"
|
|
15
|
+
end
|
|
16
|
+
alias :workspace_users :get_workspace_users
|
|
17
|
+
|
|
18
|
+
def get_workspace_clients(wid)
|
|
19
|
+
get "/workspaces/#{wid}/clients"
|
|
20
|
+
end
|
|
21
|
+
alias :workspace_clients :get_workspace_clients
|
|
22
|
+
|
|
23
|
+
# the token owner must be workspace admin
|
|
24
|
+
def get_workspace_projects(wid, active=true)
|
|
25
|
+
get "/workspaces/#{wid}/projects", {:active => active}
|
|
26
|
+
end
|
|
27
|
+
alias :workspace_projects :get_workspace_projects
|
|
28
|
+
|
|
29
|
+
def get_workspace_tasks(wid, active=true)
|
|
30
|
+
get "/workspaces/#{wid}/tasks", {:active => active}
|
|
31
|
+
end
|
|
32
|
+
alias :workspace_tasks :get_workspace_tasks
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Toggl
|
|
2
|
+
module Api
|
|
3
|
+
module WorkspaceUser
|
|
4
|
+
|
|
5
|
+
# id: workspace user id (integer)
|
|
6
|
+
# uid: user id of the workspace user (integer)
|
|
7
|
+
# admin: if user is workspace admin (boolean)
|
|
8
|
+
# active: if the workspace user has accepted the invitation to this workspace (boolean)
|
|
9
|
+
# invite_url: if user has not accepted the invitation the url for accepting his/her invitation is sent when the request is made by workspace_admin
|
|
10
|
+
|
|
11
|
+
def invite_users_to_workspace(wid, emails)
|
|
12
|
+
post "/workspaces/#{wid}/invite",{:emails => emails}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def update_workspace_user(wuid, options)
|
|
16
|
+
options = Hashie::Mash.new options
|
|
17
|
+
post "/workspace_users/#{wuid}",(options.key?(:workspace_user) ? options : {:workspace_user => options})
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def delete_workspace_user(uid)
|
|
21
|
+
delete "/workspace_users/#{uid}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
#orkspace_user objects (the connection between user and workspace)
|
|
25
|
+
def get_relations_of_workspace_and_user(wid)
|
|
26
|
+
get "/workspaces/#{wid}/workspace_users"
|
|
27
|
+
end
|
|
28
|
+
alias :relations_of_workspace_and_user :get_relations_of_workspace_and_user
|
|
29
|
+
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'toggl_api/request'
|
|
2
|
+
require 'toggl_api/api/client'
|
|
3
|
+
require 'toggl_api/api/project'
|
|
4
|
+
require 'toggl_api/api/project_user'
|
|
5
|
+
require 'toggl_api/api/tag'
|
|
6
|
+
require 'toggl_api/api/task'
|
|
7
|
+
require 'toggl_api/api/time_entry'
|
|
8
|
+
require 'toggl_api/api/user'
|
|
9
|
+
require 'toggl_api/api/workspace'
|
|
10
|
+
require 'toggl_api/api/workspace_user'
|
|
11
|
+
|
|
12
|
+
module Toggl
|
|
13
|
+
class Base
|
|
14
|
+
|
|
15
|
+
include Toggl::Request
|
|
16
|
+
include Toggl::Api::Client
|
|
17
|
+
include Toggl::Api::Project
|
|
18
|
+
include Toggl::Api::ProjectUser
|
|
19
|
+
include Toggl::Api::Tag
|
|
20
|
+
include Toggl::Api::Task
|
|
21
|
+
include Toggl::Api::TimeEntry
|
|
22
|
+
include Toggl::Api::User
|
|
23
|
+
include Toggl::Api::Workspace
|
|
24
|
+
include Toggl::Api::WorkspaceUser
|
|
25
|
+
|
|
26
|
+
APIVERSION = "v8"
|
|
27
|
+
|
|
28
|
+
def initialize(username, pass='api_token')
|
|
29
|
+
@username,@pass = username,pass
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def basic_path(path)
|
|
35
|
+
path = "/api/#{APIVERSION}#{path}" unless path =~ /api/
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
module Toggl
|
|
2
|
+
class Unauthorized < StandardError; end
|
|
3
|
+
class Forbidden < StandardError; end
|
|
4
|
+
class BadRequest < StandardError; end
|
|
5
|
+
class Unavailable < StandardError; end
|
|
6
|
+
class NotFound < StandardError; end
|
|
7
|
+
class InternalServerError < StandardError; end
|
|
8
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'toggl_api/request'
|
|
2
|
+
|
|
3
|
+
module Toggl
|
|
4
|
+
class Report
|
|
5
|
+
|
|
6
|
+
include Toggl::Request
|
|
7
|
+
|
|
8
|
+
APIVERSION = "v2"
|
|
9
|
+
|
|
10
|
+
def initialize(token)
|
|
11
|
+
@username,@pass = token,"api_token"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
#reports
|
|
15
|
+
%w[weekly details summary].each do |method|
|
|
16
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
17
|
+
def #{method}(workspace_id, params={})
|
|
18
|
+
request(:get, '/#{method}', params.merge({:workspace_id => workspace_id,:user_agent => user_agent}))
|
|
19
|
+
end
|
|
20
|
+
RUBY
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def basic_path(path)
|
|
26
|
+
path = "/reports/api/#{APIVERSION}#{path}" unless path =~ /reports/
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
require 'toggl_api/error'
|
|
2
|
+
|
|
3
|
+
module Toggl
|
|
4
|
+
module Request
|
|
5
|
+
|
|
6
|
+
ENDPOINT = "https://www.toggl.com"
|
|
7
|
+
|
|
8
|
+
attr_writer :user_agent, :connection_options
|
|
9
|
+
|
|
10
|
+
def user_agent
|
|
11
|
+
@user_agent ||= "Toggl Api Ruby Gem #{Toggl::VERSION}"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def connection_options
|
|
15
|
+
@connection_options ||= {
|
|
16
|
+
:headers => {
|
|
17
|
+
:accept => 'application/json',
|
|
18
|
+
:user_agent => user_agent,
|
|
19
|
+
'Content-Type' => 'application/json'
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
#perform http request
|
|
25
|
+
%w[get delete].each do |method|
|
|
26
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
27
|
+
def #{method}(path, params={})
|
|
28
|
+
request(:#{method}, path, params)
|
|
29
|
+
end
|
|
30
|
+
RUBY
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
%w[post put].each do |method|
|
|
34
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
35
|
+
def #{method}(path, params={})
|
|
36
|
+
request(:#{method}, path, MultiJson.dump(params))
|
|
37
|
+
end
|
|
38
|
+
RUBY
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def request(method, path, params={})
|
|
44
|
+
handle_response connection.send(method.to_sym, basic_path(path), params)
|
|
45
|
+
rescue Faraday::Error::ClientError
|
|
46
|
+
raise TogglApi::Error
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def connection
|
|
50
|
+
@connection ||= Faraday.new(ENDPOINT,connection_options) do |faraday|
|
|
51
|
+
faraday.request :url_encoded
|
|
52
|
+
faraday.adapter Faraday.default_adapter
|
|
53
|
+
faraday.basic_auth @username, @pass
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def handle_response(response)
|
|
58
|
+
raise_errors(response)
|
|
59
|
+
data = mash(MultiJson.load(response.body))
|
|
60
|
+
(data.is_a?(Hash) && data.key?('data')) ? data['data'] : data
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def raise_errors(response)
|
|
64
|
+
message = "(#{response.status}): #{response.body}"
|
|
65
|
+
case response.status.to_i
|
|
66
|
+
when 400
|
|
67
|
+
raise BadRequest, message
|
|
68
|
+
when 401
|
|
69
|
+
raise Unauthorized, message
|
|
70
|
+
when 403
|
|
71
|
+
raise Forbidden, message
|
|
72
|
+
when 404
|
|
73
|
+
raise NotFound, message
|
|
74
|
+
when 500
|
|
75
|
+
raise InternalServerError, message
|
|
76
|
+
when 502..503
|
|
77
|
+
raise Unavailable, message
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def mash(obj)
|
|
82
|
+
if obj.is_a?(Array)
|
|
83
|
+
obj.map { |item| make_mash_with_consistent_hash(item) }
|
|
84
|
+
elsif obj.is_a?(Hash)
|
|
85
|
+
make_mash_with_consistent_hash(obj)
|
|
86
|
+
else
|
|
87
|
+
obj
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def make_mash_with_consistent_hash(obj)
|
|
92
|
+
m = Hashie::Mash.new(obj)
|
|
93
|
+
def m.hash
|
|
94
|
+
inspect.hash
|
|
95
|
+
end
|
|
96
|
+
return m
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
end
|
|
100
|
+
end
|
data/test/base_test.rb
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require File.expand_path '../helper.rb', __FILE__
|
|
2
|
+
|
|
3
|
+
class baseTest < MiniTest::Unit::TestCase
|
|
4
|
+
|
|
5
|
+
def setup
|
|
6
|
+
@base = Toggl::Base.new('abc','123')
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def test_authorization_header
|
|
10
|
+
assert_match /Basic/,@base.get("/api/v8/me")[:request_headers]["Authorization"]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_get_method
|
|
14
|
+
@base.stub :request, Faraday::Response.new do
|
|
15
|
+
assert_kind_of Faraday::Response, @base.get("/api/v8/me")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def test_post_method
|
|
20
|
+
@base.stub :request, Faraday::Response.new do
|
|
21
|
+
assert_kind_of Faraday::Response, @base.post("/api/v8/me")
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_put_method
|
|
26
|
+
@base.stub :request, Faraday::Response.new do
|
|
27
|
+
assert_kind_of Faraday::Response, @base.put("/api/v8/me")
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_delete_method
|
|
32
|
+
@base.stub :request, Faraday::Response.new do
|
|
33
|
+
assert_kind_of Faraday::Response, @base.delete("/api/v8/me")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"since":1361780172,
|
|
3
|
+
"data": {
|
|
4
|
+
"id":123,
|
|
5
|
+
"api_token":"1971800d4d82861d8f2c1651fea4d212",
|
|
6
|
+
"default_wid":777,
|
|
7
|
+
"email":"john.doe@gmail.com",
|
|
8
|
+
"fullname":"John Doe",
|
|
9
|
+
"jquery_timeofday_format":"h:i A",
|
|
10
|
+
"jquery_date_format":"m/d/Y",
|
|
11
|
+
"timeofday_format":"h:mm A",
|
|
12
|
+
"date_format":"MM/DD/YYYY",
|
|
13
|
+
"store_start_and_stop_time":true,
|
|
14
|
+
"beginning_of_week":1,
|
|
15
|
+
"language":"en_US",
|
|
16
|
+
"image_url":"https://www.toggl.com/images/profile.png",
|
|
17
|
+
"new_blog_post":{},
|
|
18
|
+
"projects": [
|
|
19
|
+
{
|
|
20
|
+
"id":90123,
|
|
21
|
+
"wid":777,
|
|
22
|
+
"name":"Our best project",
|
|
23
|
+
"billable":true,
|
|
24
|
+
"active":true,
|
|
25
|
+
"at":"2013-02-12T09:47:57+00:00"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"tags": [
|
|
29
|
+
{
|
|
30
|
+
"id":238526,
|
|
31
|
+
"wid":777,
|
|
32
|
+
"name":"billed"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"tasks": [],
|
|
36
|
+
"workspaces": [
|
|
37
|
+
{
|
|
38
|
+
"id":777,"name":"John's WS","at":"2012-11-28T11:56:49+00:00"
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"clients": []
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"data": [
|
|
3
|
+
{
|
|
4
|
+
"name":"A new task",
|
|
5
|
+
"id":1335076912,
|
|
6
|
+
"wid":888,
|
|
7
|
+
"pid":777,
|
|
8
|
+
"active":false,
|
|
9
|
+
"at":"2013-02-26T15:09:52+00:00",
|
|
10
|
+
"estimated_seconds":3600,
|
|
11
|
+
"uname": "John Swift",
|
|
12
|
+
"done_seconds": 1200
|
|
13
|
+
}, {
|
|
14
|
+
"name":"Another task",
|
|
15
|
+
"id":1335076911,
|
|
16
|
+
"wid":888,
|
|
17
|
+
"pid":777,
|
|
18
|
+
"active":false,
|
|
19
|
+
"at":"2013-02-26T15:09:52+00:00",
|
|
20
|
+
"estimated_seconds":3600,
|
|
21
|
+
"done_seconds": 10
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{ "data":[
|
|
2
|
+
{
|
|
3
|
+
"id":436694100,
|
|
4
|
+
"guid":"dfc88541-f3b0-bffe-fa2e-46a09fa97664",
|
|
5
|
+
"wid":777,
|
|
6
|
+
"pid":20123718,
|
|
7
|
+
"billable":true,
|
|
8
|
+
"start":"2013-08-01T10:46:00",
|
|
9
|
+
"stop":"2013-08-01T11:46:02",
|
|
10
|
+
"duration":3602,
|
|
11
|
+
"description":"Development",
|
|
12
|
+
"tags":["billed","poductive","overhours"],
|
|
13
|
+
"duronly":false,
|
|
14
|
+
"at":"2013-08-01T12:04:57"
|
|
15
|
+
},{
|
|
16
|
+
"id":436694101,
|
|
17
|
+
"guid":"ce3c2409-e624-64e2-6742-c623ff284091",
|
|
18
|
+
"wid":777,
|
|
19
|
+
"billable":false,
|
|
20
|
+
"start":"2013-08-01T11:11:00",
|
|
21
|
+
"stop":"2013-08-01T11:46:04",
|
|
22
|
+
"duration":2104,
|
|
23
|
+
"description":"Meeting with the client",
|
|
24
|
+
"tags":["billed","poductive","holiday"],
|
|
25
|
+
"duronly":false,
|
|
26
|
+
"at":"2013-08-01T11:46:08"
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id":909,
|
|
4
|
+
"wid":777,
|
|
5
|
+
"cid":987,
|
|
6
|
+
"name":"Very lucrative project",
|
|
7
|
+
"billable":false,
|
|
8
|
+
"is_private":true,
|
|
9
|
+
"active":true,
|
|
10
|
+
"at":"2013-03-06T09:15:18+00:00"
|
|
11
|
+
},{
|
|
12
|
+
"id":32143,
|
|
13
|
+
"wid":777,
|
|
14
|
+
"cid":987,
|
|
15
|
+
"name":"Factory server infrastructure",
|
|
16
|
+
"billable":true,
|
|
17
|
+
"is_private":true,
|
|
18
|
+
"active":true,
|
|
19
|
+
"at":"2013-03-06T09:16:06+00:00"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id":1239455,
|
|
4
|
+
"wid":777,
|
|
5
|
+
"name":"Very Big Company",
|
|
6
|
+
"notes":"something about the client",
|
|
7
|
+
"at":"2013-02-26T08:55:28+00:00"
|
|
8
|
+
}, {
|
|
9
|
+
"id":1239456,
|
|
10
|
+
"wid":777,
|
|
11
|
+
"name":"Small Startup",
|
|
12
|
+
"notes":"Really cool people",
|
|
13
|
+
"at":"2013-03-26T08:55:28+00:00"
|
|
14
|
+
}
|
|
15
|
+
]
|