togglv8 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +4 -0
- data/.rdoc_options +23 -0
- data/.rspec +1 -0
- data/.travis.yml +11 -0
- data/API calls.md +330 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +75 -0
- data/Rakefile +8 -0
- data/lib/togglv8/clients.rb +37 -0
- data/lib/togglv8/dashboard.rb +14 -0
- data/lib/togglv8/project_users.rb +32 -0
- data/lib/togglv8/projects.rb +111 -0
- data/lib/togglv8/tags.rb +25 -0
- data/lib/togglv8/tasks.rb +53 -0
- data/lib/togglv8/time_entries.rb +89 -0
- data/lib/togglv8/users.rb +60 -0
- data/lib/togglv8/version.rb +4 -0
- data/lib/togglv8/workspaces.rb +43 -0
- data/lib/togglv8.rb +137 -0
- data/spec/lib/togglv8/clients_spec.rb +139 -0
- data/spec/lib/togglv8/dashboard_spec.rb +31 -0
- data/spec/lib/togglv8/projects_spec.rb +105 -0
- data/spec/lib/togglv8/tags_spec.rb +47 -0
- data/spec/lib/togglv8/tasks_spec.rb +97 -0
- data/spec/lib/togglv8/time_entries_spec.rb +247 -0
- data/spec/lib/togglv8/users_spec.rb +55 -0
- data/spec/lib/togglv8_spec.rb +59 -0
- data/spec/spec_helper.rb +140 -0
- data/spec/togglv8_spec_helper.rb +71 -0
- data/togglv8.gemspec +35 -0
- metadata +227 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
module TogglV8
|
2
|
+
class API
|
3
|
+
|
4
|
+
##
|
5
|
+
# ---------
|
6
|
+
# :section: Projects
|
7
|
+
#
|
8
|
+
# See Toggl {Projects}[https://github.com/toggl/toggl_api_docs/blob/master/chapters/projects.md]
|
9
|
+
#
|
10
|
+
# name : The name of the project
|
11
|
+
# (string, *required*, unique for client and workspace)
|
12
|
+
# wid : workspace ID, where the project will be saved
|
13
|
+
# (integer, *required*)
|
14
|
+
# cid : client ID
|
15
|
+
# (integer, not required)
|
16
|
+
# active : whether the project is archived or not
|
17
|
+
# (boolean, by default true)
|
18
|
+
# is_private : whether project is accessible for only project users or for all workspace users
|
19
|
+
# (boolean, default true)
|
20
|
+
# template : whether the project can be used as a template
|
21
|
+
# (boolean, not required)
|
22
|
+
# template_id : id of the template project used on current project's creation
|
23
|
+
# billable : whether the project is billable or not
|
24
|
+
# (boolean, default true, available only for pro workspaces)
|
25
|
+
# auto_estimates : whether the estimated hours is calculated based on task estimations or is fixed manually
|
26
|
+
# (boolean, default false, not required, premium functionality)
|
27
|
+
# estimated_hours : if auto_estimates is true then the sum of task estimations is returned, otherwise user inserted hours
|
28
|
+
# (integer, not required, premium functionality)
|
29
|
+
# at : timestamp that is sent in the response for PUT, indicates the time task was last updated
|
30
|
+
# color : id of the color selected for the project
|
31
|
+
# rate : hourly rate of the project
|
32
|
+
# (float, not required, premium functionality)
|
33
|
+
# created_at : timestamp indicating when the project was created (UTC time), read-only
|
34
|
+
# ---------
|
35
|
+
|
36
|
+
##
|
37
|
+
# :category: Projects
|
38
|
+
#
|
39
|
+
# Public: Create a new project
|
40
|
+
#
|
41
|
+
# params - The Hash used to create the project (default: {})
|
42
|
+
# :name - The name of the project (string, required, unique for client and workspace)
|
43
|
+
# :wid - workspace ID, where the project will be saved (integer, required)
|
44
|
+
# :cid - client ID (integer, not required)
|
45
|
+
# :active - whether the project is archived or not (boolean, by default true)
|
46
|
+
# :is_private - whether project is accessible for only project users or for all workspace users (boolean, default true)
|
47
|
+
# :template - whether the project can be used as a template (boolean, not required)
|
48
|
+
# :template_id - id of the template project used on current project's creation
|
49
|
+
# :billable - whether the project is billable or not (boolean, default true, available only for pro workspaces)
|
50
|
+
# :auto_estimates - whether the estimated hours is calculated based on task estimations or is fixed manually (boolean, default false, not required, premium functionality)
|
51
|
+
# :estimated_hours - if auto_estimates is true then the sum of task estimations is returned, otherwise user inserted hours (integer, not required, premium functionality)
|
52
|
+
# :at - timestamp that is sent in the response for PUT, indicates the time task was last updated
|
53
|
+
# :color - id of the color selected for the project
|
54
|
+
# :rate - hourly rate of the project (float, not required, premium functionality)
|
55
|
+
# :created_at - timestamp indicating when the project was created (UTC time), read-only
|
56
|
+
#
|
57
|
+
# Examples
|
58
|
+
#
|
59
|
+
# toggl.create_project({ name: 'My project', wid: 1060392 })
|
60
|
+
# => {"id"=>10918774,
|
61
|
+
# "wid"=>1060392,
|
62
|
+
# "name"=>"project5",
|
63
|
+
# "billable"=>false,
|
64
|
+
# "is_private"=>true,
|
65
|
+
# "active"=>true,
|
66
|
+
# "template"=>false,
|
67
|
+
# "at"=>"2015-08-18T10:03:51+00:00",
|
68
|
+
# "color"=>"5",
|
69
|
+
# "auto_estimates"=>false}
|
70
|
+
#
|
71
|
+
# Returns a +Hash+ representing the newly created Project.
|
72
|
+
#
|
73
|
+
# See Toggl {Create Project}[https://github.com/toggl/toggl_api_docs/blob/master/chapters/projects.md#create-project]
|
74
|
+
def create_project(params)
|
75
|
+
requireParams(params, ['name', 'wid'])
|
76
|
+
post "projects", {project: params}
|
77
|
+
end
|
78
|
+
|
79
|
+
# [Get project data](https://github.com/toggl/toggl_api_docs/blob/master/chapters/projects.md#get-project-data)
|
80
|
+
def get_project(project_id)
|
81
|
+
get "projects/#{project_id}"
|
82
|
+
end
|
83
|
+
|
84
|
+
# [Update project data](https://github.com/toggl/toggl_api_docs/blob/master/chapters/projects.md#update-project-data)
|
85
|
+
def update_project(project_id, params)
|
86
|
+
put "projects/#{project_id}", { 'project' => params }
|
87
|
+
end
|
88
|
+
|
89
|
+
# [Delete a project](https://github.com/toggl/toggl_api_docs/blob/master/chapters/projects.md#delete-a-project)
|
90
|
+
def delete_project(project_id)
|
91
|
+
delete "projects/#{project_id}"
|
92
|
+
end
|
93
|
+
|
94
|
+
# [Get project users](https://github.com/toggl/toggl_api_docs/blob/master/chapters/projects.md#get-project-users)
|
95
|
+
def get_project_users(project_id)
|
96
|
+
get "projects/#{project_id}/project_users"
|
97
|
+
end
|
98
|
+
|
99
|
+
# [Get project tasks](https://github.com/toggl/toggl_api_docs/blob/master/chapters/projects.md#get-project-tasks)
|
100
|
+
def get_project_tasks(project_id)
|
101
|
+
get "projects/#{project_id}/project_tasks"
|
102
|
+
end
|
103
|
+
|
104
|
+
# [Get workspace projects](https://github.com/toggl/toggl_api_docs/blob/master/chapters/projects.md#get-workspace-projects)
|
105
|
+
|
106
|
+
# [Delete multiple projects](https://github.com/toggl/toggl_api_docs/blob/master/chapters/projects.md#delete-multiple-projects)
|
107
|
+
def delete_projects(project_ids)
|
108
|
+
delete "projects/#{project_ids.join(',')}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/togglv8/tags.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module TogglV8
|
2
|
+
class API
|
3
|
+
|
4
|
+
##
|
5
|
+
# ---------
|
6
|
+
# :section: Tags
|
7
|
+
#
|
8
|
+
# name : The name of the tag (string, required, unique in workspace)
|
9
|
+
# wid : workspace ID, where the tag will be used (integer, required)
|
10
|
+
|
11
|
+
def create_tag(params)
|
12
|
+
requireParams(params, ['name', 'wid'])
|
13
|
+
post "tags", {tag: params}
|
14
|
+
end
|
15
|
+
|
16
|
+
# ex: update_tag(12345, {name: "same tame game"})
|
17
|
+
def update_tag(tag_id, params)
|
18
|
+
put "tags/#{tag_id}", { 'tag' => params }
|
19
|
+
end
|
20
|
+
|
21
|
+
def delete_tag(tag_id)
|
22
|
+
delete "tags/#{tag_id}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module TogglV8
|
2
|
+
class API
|
3
|
+
|
4
|
+
##
|
5
|
+
# ---------
|
6
|
+
# :section: Tasks
|
7
|
+
#
|
8
|
+
# NOTE: Tasks are available only for pro workspaces.
|
9
|
+
#
|
10
|
+
# name : The name of the task (string, required, unique in project)
|
11
|
+
# pid : project ID for the task (integer, required)
|
12
|
+
# wid : workspace ID, where the task will be saved
|
13
|
+
# (integer, project's workspace id is used when not supplied)
|
14
|
+
# uid : user ID, to whom the task is assigned to (integer, not required)
|
15
|
+
# estimated_seconds : estimated duration of task in seconds (integer, not required)
|
16
|
+
# active : whether the task is done or not (boolean, by default true)
|
17
|
+
# at : timestamp that is sent in the response for PUT, indicates the time task was last updated
|
18
|
+
# -- Additional fields --
|
19
|
+
# done_seconds : duration (in seconds) of all the time entries registered for this task
|
20
|
+
# uname : full name of the person to whom the task is assigned to
|
21
|
+
|
22
|
+
def create_task(params)
|
23
|
+
requireParams(params, [:name, :pid])
|
24
|
+
post "tasks", { 'task' => params }
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_task(task_id)
|
28
|
+
get "tasks/#{task_id}"
|
29
|
+
end
|
30
|
+
|
31
|
+
# ex: update_task(1894675, {active: true, estimated_seconds: 4500, fields: "done_seconds,uname"})
|
32
|
+
def update_task(task_id, params)
|
33
|
+
put "tasks/#{task_id.join(',')}", { 'task' => params }
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete_task(task_id)
|
37
|
+
delete "tasks/#{task_id.join(',')}"
|
38
|
+
end
|
39
|
+
|
40
|
+
# ------------ #
|
41
|
+
# Mass Actions #
|
42
|
+
# ------------ #
|
43
|
+
|
44
|
+
def update_tasks(task_ids, params)
|
45
|
+
put "tasks/#{task_ids.join(',')}", { 'task' => params }
|
46
|
+
end
|
47
|
+
|
48
|
+
def delete_tasks(task_ids)
|
49
|
+
delete "tasks/#{task_ids.join(',')}"
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module TogglV8
|
2
|
+
class API
|
3
|
+
|
4
|
+
##
|
5
|
+
# ---------
|
6
|
+
# :section: Time Entries
|
7
|
+
#
|
8
|
+
# https://github.com/toggl/toggl_api_docs/blob/master/chapters/time_entries.md
|
9
|
+
#
|
10
|
+
# description : (string, strongly suggested to be used)
|
11
|
+
# wid : workspace ID (integer, required if pid or tid not supplied)
|
12
|
+
# pid : project ID (integer, not required)
|
13
|
+
# tid : task ID (integer, not required)
|
14
|
+
# billable : (boolean, not required, default false, available for pro workspaces)
|
15
|
+
# start : time entry start time (string, required, ISO 8601 date and time)
|
16
|
+
# stop : time entry stop time (string, not required, ISO 8601 date and time)
|
17
|
+
# duration : time entry duration in seconds. If the time entry is currently running,
|
18
|
+
# the duration attribute contains a negative value,
|
19
|
+
# denoting the start of the time entry in seconds since epoch (Jan 1 1970).
|
20
|
+
# The correct duration can be calculated as current_time + duration,
|
21
|
+
# where current_time is the current time in seconds since epoch. (integer, required)
|
22
|
+
# created_with : the name of your client app (string, required)
|
23
|
+
# tags : a list of tag names (array of strings, not required)
|
24
|
+
# duronly : should Toggl show the start and stop time of this time entry? (boolean, not required)
|
25
|
+
# at : timestamp that is sent in the response, indicates the time item was last updated
|
26
|
+
|
27
|
+
def create_time_entry(params)
|
28
|
+
params['created_with'] = 'TogglV8' unless params.has_key?('created_with')
|
29
|
+
requireParams(params, ['start', 'duration', 'created_with'])
|
30
|
+
if !params.has_key?('wid') and !params.has_key?('pid') and !params.has_key?('tid') then
|
31
|
+
raise ArgumentError, "one of params['wid'], params['pid'], params['tid'] is required"
|
32
|
+
end
|
33
|
+
post "time_entries", { 'time_entry' => params }
|
34
|
+
end
|
35
|
+
|
36
|
+
def start_time_entry(params)
|
37
|
+
params['created_with'] = 'TogglV8' unless params.has_key?('created_with')
|
38
|
+
if !params.has_key?('wid') and !params.has_key?('pid') and !params.has_key?('tid') then
|
39
|
+
raise ArgumentError, "one of params['wid'], params['pid'], params['tid'] is required"
|
40
|
+
end
|
41
|
+
post "time_entries/start", {time_entry: params}
|
42
|
+
end
|
43
|
+
|
44
|
+
def stop_time_entry(time_entry_id)
|
45
|
+
put "time_entries/#{time_entry_id}/stop", {}
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_time_entry(time_entry_id)
|
49
|
+
get "time_entries/#{time_entry_id}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_current_time_entry
|
53
|
+
get "time_entries/current"
|
54
|
+
end
|
55
|
+
|
56
|
+
def update_time_entry(time_entry_id, params)
|
57
|
+
put "time_entries/#{time_entry_id}", { 'time_entry' => params }
|
58
|
+
end
|
59
|
+
|
60
|
+
def delete_time_entry(time_entry_id)
|
61
|
+
delete "time_entries/#{time_entry_id}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def iso8601(timestamp)
|
65
|
+
return nil if timestamp.nil?
|
66
|
+
if timestamp.is_a?(DateTime) or timestamp.is_a?(Date)
|
67
|
+
formatted_ts = timestamp.iso8601
|
68
|
+
elsif timestamp.is_a?(String)
|
69
|
+
formatted_ts = DateTime.parse(timestamp).iso8601
|
70
|
+
else
|
71
|
+
raise ArgumentError, "Can't convert #{timestamp.class} to ISO-8601 Date/Time"
|
72
|
+
end
|
73
|
+
return formatted_ts.sub('+00:00', 'Z')
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_time_entries(start_timestamp=nil, end_timestamp=nil)
|
77
|
+
params = []
|
78
|
+
params.push("start_date=#{iso8601(start_timestamp)}") if !start_timestamp.nil?
|
79
|
+
params.push("end_date=#{iso8601(end_timestamp)}") if !end_timestamp.nil?
|
80
|
+
get "time_entries%s" % [params.empty? ? "" : "?#{params.join('&')}"]
|
81
|
+
end
|
82
|
+
|
83
|
+
# Example params: {"tags":["billed","productive"], "tag_action": "add"}
|
84
|
+
# tag_action can be 'add' or 'remove'
|
85
|
+
def update_time_entries_tags(time_entry_ids, params)
|
86
|
+
put "time_entries/#{time_entry_ids.join(',')}", { 'time_entry' => params }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module TogglV8
|
2
|
+
class API
|
3
|
+
|
4
|
+
##
|
5
|
+
# ---------
|
6
|
+
# :section: Users
|
7
|
+
#
|
8
|
+
# api_token : (string)
|
9
|
+
# default_wid : default workspace id (integer)
|
10
|
+
# email : (string)
|
11
|
+
# jquery_timeofday_format : (string)
|
12
|
+
# jquery_date_format : (string)
|
13
|
+
# timeofday_format : (string)
|
14
|
+
# date_format : (string)
|
15
|
+
# store_start_and_stop_time : whether start and stop time are saved on time entry (boolean)
|
16
|
+
# beginning_of_week : (integer, Sunday=0)
|
17
|
+
# language : user's language (string)
|
18
|
+
# image_url : url with the user's profile picture(string)
|
19
|
+
# sidebar_piechart : should a piechart be shown on the sidebar (boolean)
|
20
|
+
# at : timestamp of last changes
|
21
|
+
# new_blog_post : an object with toggl blog post title and link
|
22
|
+
|
23
|
+
def me(all=nil)
|
24
|
+
# TODO: Reconcile this with get_client_projects
|
25
|
+
# NOTE: response['since'] is discarded
|
26
|
+
get "me%s" % [all.nil? ? "" : "?with_related_data=#{all}"]
|
27
|
+
end
|
28
|
+
|
29
|
+
def my_clients(user=nil)
|
30
|
+
user = me(all=true) if user.nil?
|
31
|
+
user['clients'] || {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def my_projects(user=nil)
|
35
|
+
user = me(all=true) if user.nil?
|
36
|
+
user['projects'] || {}
|
37
|
+
end
|
38
|
+
|
39
|
+
def my_tags(user=nil)
|
40
|
+
user = me(all=true) if user.nil?
|
41
|
+
user['tags'] || {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def my_time_entries(user=nil)
|
45
|
+
user = me(all=true) if user.nil?
|
46
|
+
user['time_entries'] || {}
|
47
|
+
end
|
48
|
+
|
49
|
+
def my_workspaces(user=nil)
|
50
|
+
user = me(all=true) if user.nil?
|
51
|
+
user['workspaces'] || {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def create_user(params)
|
55
|
+
params['created_with'] = 'TogglV8' unless params.has_key?('created_with')
|
56
|
+
requireParams(params, ['email', 'password', 'timezone', 'created_with'])
|
57
|
+
post "signups", { 'user' => params }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module TogglV8
|
2
|
+
class API
|
3
|
+
|
4
|
+
##
|
5
|
+
# ---------
|
6
|
+
# :section: Workspaces
|
7
|
+
#
|
8
|
+
# name : (string, required)
|
9
|
+
# premium : If it's a pro workspace or not.
|
10
|
+
# Shows if someone is paying for the workspace or not (boolean, not required)
|
11
|
+
# at : timestamp that is sent in the response, indicates the time item was last updated
|
12
|
+
|
13
|
+
def workspaces
|
14
|
+
get "workspaces"
|
15
|
+
end
|
16
|
+
|
17
|
+
def clients(workspace_id=nil)
|
18
|
+
if workspace_id.nil?
|
19
|
+
get "clients"
|
20
|
+
else
|
21
|
+
get "workspaces/#{workspace_id}/clients"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def projects(workspace_id, params={})
|
26
|
+
active = params.has_key?(:active) ? "?active=#{params[:active]}" : ""
|
27
|
+
get "workspaces/#{workspace_id}/projects#{active}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def users(workspace_id)
|
31
|
+
get "workspaces/#{workspace_id}/users"
|
32
|
+
end
|
33
|
+
|
34
|
+
def tasks(workspace_id, params={})
|
35
|
+
active = params.has_key?(:active) ? "?active=#{params[:active]}" : ""
|
36
|
+
get "workspaces/#{workspace_id}/tasks#{active}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def leave_workspace(workspace_id)
|
40
|
+
delete "workspaces/#{workspace_id}/leave"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/togglv8.rb
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'oj'
|
3
|
+
|
4
|
+
require 'logger'
|
5
|
+
require 'awesome_print' # for debug output
|
6
|
+
|
7
|
+
require_relative 'togglv8/clients'
|
8
|
+
require_relative 'togglv8/dashboard'
|
9
|
+
require_relative 'togglv8/project_users'
|
10
|
+
require_relative 'togglv8/projects'
|
11
|
+
require_relative 'togglv8/tags'
|
12
|
+
require_relative 'togglv8/tasks'
|
13
|
+
require_relative 'togglv8/time_entries'
|
14
|
+
require_relative 'togglv8/users'
|
15
|
+
require_relative 'togglv8/version'
|
16
|
+
require_relative 'togglv8/workspaces'
|
17
|
+
|
18
|
+
# mode: :compat will convert symbols to strings
|
19
|
+
Oj.default_options = { mode: :compat }
|
20
|
+
|
21
|
+
module TogglV8
|
22
|
+
TOGGL_API_URL = 'https://www.toggl.com/api/'
|
23
|
+
|
24
|
+
class API
|
25
|
+
TOGGL_API_V8_URL = TOGGL_API_URL + 'v8/'
|
26
|
+
API_TOKEN = 'api_token'
|
27
|
+
TOGGL_FILE = '.toggl'
|
28
|
+
|
29
|
+
attr_reader :conn
|
30
|
+
|
31
|
+
def initialize(username=nil, password=API_TOKEN, opts={})
|
32
|
+
@logger = Logger.new(STDOUT)
|
33
|
+
@logger.level = Logger::WARN
|
34
|
+
|
35
|
+
if username.nil? && password == API_TOKEN
|
36
|
+
toggl_api_file = File.join(Dir.home, TOGGL_FILE)
|
37
|
+
if FileTest.exist?(toggl_api_file) then
|
38
|
+
username = IO.read(toggl_api_file)
|
39
|
+
else
|
40
|
+
raise "Expecting\n" +
|
41
|
+
" 1) api_token in file #{toggl_api_file}, or\n" +
|
42
|
+
" 2) parameter: (api_token), or\n" +
|
43
|
+
" 3) parameters: (username, password).\n" +
|
44
|
+
"\n\tSee https://github.com/toggl/toggl_api_docs/blob/master/chapters/authentication.md"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
@conn = TogglV8::API.connection(username, password, opts)
|
49
|
+
end
|
50
|
+
|
51
|
+
#---------#
|
52
|
+
# Private #
|
53
|
+
#---------#
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
attr_writer :conn
|
58
|
+
|
59
|
+
def self.connection(username, password, opts={})
|
60
|
+
Faraday.new(url: TOGGL_API_V8_URL, ssl: {verify: true}) do |faraday|
|
61
|
+
faraday.request :url_encoded
|
62
|
+
faraday.response :logger, Logger.new('faraday.log') if opts[:log]
|
63
|
+
faraday.adapter Faraday.default_adapter
|
64
|
+
faraday.headers = { "Content-Type" => "application/json" }
|
65
|
+
faraday.basic_auth username, password
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
def requireParams(params, fields=[])
|
71
|
+
raise ArgumentError, 'params is not a Hash' unless params.is_a? Hash
|
72
|
+
return if fields.empty?
|
73
|
+
errors = []
|
74
|
+
for f in fields
|
75
|
+
errors.push("params[#{f}] is required") unless params.has_key?(f)
|
76
|
+
end
|
77
|
+
raise ArgumentError, errors.join(', ') if !errors.empty?
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
def get(resource)
|
82
|
+
@logger.debug("GET #{resource}")
|
83
|
+
full_resp = self.conn.get(resource)
|
84
|
+
# @logger.ap(full_resp.env, :debug)
|
85
|
+
|
86
|
+
raise 'Too many requests in a given amount of time.' if full_resp.status == 429
|
87
|
+
raise Oj.dump(full_resp.env) unless full_resp.success?
|
88
|
+
return {} if full_resp.body.nil? || full_resp.body == 'null'
|
89
|
+
|
90
|
+
resp = Oj.load(full_resp.body)
|
91
|
+
|
92
|
+
return resp['data'] if resp.respond_to?(:has_key?) && resp.has_key?('data')
|
93
|
+
resp
|
94
|
+
end
|
95
|
+
|
96
|
+
def post(resource, data='')
|
97
|
+
@logger.debug("POST #{resource} / #{data}")
|
98
|
+
full_resp = self.conn.post(resource, Oj.dump(data))
|
99
|
+
# @logger.ap(full_resp.env, :debug)
|
100
|
+
|
101
|
+
raise 'Too many requests in a given amount of time.' if full_resp.status == 429
|
102
|
+
raise Oj.dump(full_resp.env) unless full_resp.success?
|
103
|
+
return {} if full_resp.body.nil? || full_resp.body == 'null'
|
104
|
+
|
105
|
+
resp = Oj.load(full_resp.body)
|
106
|
+
resp['data']
|
107
|
+
end
|
108
|
+
|
109
|
+
def put(resource, data='')
|
110
|
+
@logger.debug("PUT #{resource} / #{data}")
|
111
|
+
full_resp = self.conn.put(resource, Oj.dump(data))
|
112
|
+
# @logger.ap(full_resp.env, :debug)
|
113
|
+
|
114
|
+
raise 'Too many requests in a given amount of time.' if full_resp.status == 429
|
115
|
+
raise Oj.dump(full_resp.env) unless full_resp.success?
|
116
|
+
return {} if full_resp.body.nil? || full_resp.body == 'null'
|
117
|
+
|
118
|
+
resp = Oj.load(full_resp.body)
|
119
|
+
resp['data']
|
120
|
+
end
|
121
|
+
|
122
|
+
def delete(resource)
|
123
|
+
@logger.debug("DELETE #{resource}")
|
124
|
+
full_resp = self.conn.delete(resource)
|
125
|
+
# @logger.ap(full_resp.env, :debug)
|
126
|
+
|
127
|
+
raise 'Too many requests in a given amount of time.' if full_resp.status == 429
|
128
|
+
|
129
|
+
raise Oj.dump(full_resp.env) unless full_resp.success?
|
130
|
+
return {} if full_resp.body.nil? || full_resp.body == 'null'
|
131
|
+
|
132
|
+
full_resp.body
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
describe 'Clients' do
|
2
|
+
before :all do
|
3
|
+
@toggl = TogglV8::API.new(Testing::API_TOKEN)
|
4
|
+
@workspaces = @toggl.workspaces
|
5
|
+
@workspace_id = @workspaces.first['id']
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'gets {} if there are no clients' do
|
9
|
+
client = @toggl.clients
|
10
|
+
expect(client).to be_empty
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'gets {} if there are no workspace clients' do
|
14
|
+
client = @toggl.clients(@workspace_id)
|
15
|
+
expect(client).to be_empty
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'new client' do
|
19
|
+
before :all do
|
20
|
+
@client = @toggl.create_client({ 'name' => 'new client', 'wid' => @workspace_id })
|
21
|
+
client_ids = @toggl.my_clients.map { |c| c['id'] }
|
22
|
+
expect(client_ids).to eq [ @client['id'] ]
|
23
|
+
end
|
24
|
+
|
25
|
+
after :all do
|
26
|
+
TogglV8SpecHelper.delete_all_clients(@toggl)
|
27
|
+
clients = @toggl.my_clients
|
28
|
+
expect(clients).to be_empty
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'gets a client' do
|
32
|
+
client_ids = @toggl.clients.map { |c| c['id'] }
|
33
|
+
expect(client_ids).to eq [ @client['id'] ]
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'gets a workspace client' do
|
37
|
+
client_ids = @toggl.clients(@workspace_id).map { |c| c['id'] }
|
38
|
+
expect(client_ids).to eq [ @client['id'] ]
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'multiple clients' do
|
42
|
+
before :all do
|
43
|
+
@client2 = @toggl.create_client({ 'name' => 'new client 2', 'wid' => @workspace_id })
|
44
|
+
end
|
45
|
+
|
46
|
+
after :all do
|
47
|
+
@toggl.delete_client(@client2['id'])
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'gets clients' do
|
51
|
+
client_ids = @toggl.clients.map { |c| c['id'] }
|
52
|
+
expect(client_ids).to match_array [ @client['id'], @client2['id'] ]
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'gets workspace clients' do
|
56
|
+
client_ids = @toggl.clients(@workspace_id).map { |c| c['id'] }
|
57
|
+
expect(client_ids).to match_array [ @client['id'], @client2['id'] ]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'creates a client' do
|
62
|
+
expect(@client).to_not be nil
|
63
|
+
expect(@client['name']).to eq 'new client'
|
64
|
+
expect(@client['notes']).to eq nil
|
65
|
+
expect(@client['wid']).to eq @workspace_id
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'gets client data' do
|
69
|
+
client = @toggl.get_client(@client['id'])
|
70
|
+
expect(client).to_not be nil
|
71
|
+
expect(client['name']).to eq @client['name']
|
72
|
+
expect(client['wid']).to eq @client['wid']
|
73
|
+
expect(client['notes']).to eq @client['notes']
|
74
|
+
expect(client['at']).to_not be nil
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'client projects' do
|
78
|
+
it 'gets {} if there are no client projects' do
|
79
|
+
projects = @toggl.get_client_projects(@client['id'])
|
80
|
+
expect(projects).to be_empty
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'new client projects' do
|
84
|
+
before :all do
|
85
|
+
@project = @toggl.create_project({ 'name' => 'project', 'wid' => @workspace_id, 'cid' => @client['id'] })
|
86
|
+
end
|
87
|
+
|
88
|
+
after :all do
|
89
|
+
TogglV8SpecHelper.delete_all_projects(@toggl)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'gets a client project' do
|
93
|
+
projects = @toggl.get_client_projects(@client['id'])
|
94
|
+
project_ids = projects.map { |p| p['id'] }
|
95
|
+
expect(project_ids).to eq [ @project['id'] ]
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'gets multiple client projects' do
|
99
|
+
project2 = @toggl.create_project({ 'name' => 'project2', 'wid' => @workspace_id, 'cid' => @client['id'] })
|
100
|
+
|
101
|
+
projects = @toggl.get_client_projects(@client['id'])
|
102
|
+
project_ids = projects.map { |p| p['id'] }
|
103
|
+
expect(project_ids).to match_array [ @project['id'], project2['id'] ]
|
104
|
+
|
105
|
+
@toggl.delete_project(project2['id'])
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'updated client' do
|
112
|
+
before :each do
|
113
|
+
@client = @toggl.create_client({ 'name' => 'client to update', 'wid' => @workspace_id })
|
114
|
+
end
|
115
|
+
|
116
|
+
after :each do
|
117
|
+
@toggl.delete_client(@client['id'])
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'updates client data' do
|
121
|
+
new_values = {
|
122
|
+
'name' => 'CLIENT-NEW',
|
123
|
+
'notes' => 'NOTES-NEW',
|
124
|
+
}
|
125
|
+
|
126
|
+
client = @toggl.update_client(@client['id'], new_values)
|
127
|
+
expect(client).to include(new_values)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'updates Pro project data', :pro_account do
|
131
|
+
new_values = {
|
132
|
+
'hrate' => '7.77',
|
133
|
+
'cur' => 'USD',
|
134
|
+
}
|
135
|
+
client = @toggl.update_client(@client['id'], new_values)
|
136
|
+
expect(client).to include(new_values)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|