togglv8-ng 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.
data/.travis.yml ADDED
@@ -0,0 +1,16 @@
1
+ language: ruby
2
+ rvm:
3
+ - ruby-head
4
+ - 2.4.0
5
+ - 2.3.3
6
+ - 2.2.6
7
+ - 2.1.10
8
+ - 2.0.0-p648
9
+ script: bundle exec rspec --format documentation
10
+ cache: bundler
11
+ branches:
12
+ only:
13
+ - master
14
+ addons:
15
+ code_climate:
16
+ repo_token: eb44970f9efc497fb68c6ec1ce051dea530776b6a5bbe75e57734625f761ac86
data/CHANGELOG.md ADDED
@@ -0,0 +1,101 @@
1
+ # Change Log
2
+
3
+ Notable changes are documented here following conventions outlined at [Keep a CHANGELOG](http://keepachangelog.com/).
4
+
5
+ Changes that are not intended to affect usage (e.g. documentation, specs, removal of dead code, etc.) are generally not documented here.
6
+
7
+ Version numbers are meant to adhere to [Semantic Versioning](http://semver.org/).
8
+
9
+
10
+ ## [Unreleased]
11
+
12
+
13
+ ## [1.2.1] - 2016-07-25
14
+ ### Changed
15
+
16
+ * Fix ReportsV2#project by requiring project_id.
17
+ * Improve spec code coverage.
18
+ * Include version in `user_agent` and `created_with` (e.g. TogglV8 v1.2.1)
19
+
20
+
21
+ ## [1.2.0] - 2016-07-24
22
+ ### Added
23
+
24
+ * Add support for [Toggl Reports API v2](https://github.com/toggl/toggl_api_docs/blob/master/reports.md).
25
+
26
+
27
+ ## [1.1.0] - 2016-02-22
28
+ ### Added
29
+
30
+ * Add `tags(workspace_id)`.
31
+
32
+
33
+ ## [1.0.5] - 2016-02-22
34
+ ### Added
35
+
36
+ * Add specs for encoding of ISO8601 times with + UTC offset. (See [1.0.4](#104---2016-01-22))
37
+
38
+
39
+ ## [1.0.4] - 2016-01-22
40
+ ### Fixed
41
+
42
+ * Manually encode `+` to `%2B` before every API call. (Fixes #11)
43
+
44
+
45
+ ## [1.0.3] - 2016-01-22
46
+ ### Added
47
+
48
+ * Add `debug()` method to enable debugging output including full API response.
49
+
50
+ ## [1.0.2] - 2015-12-12
51
+ ### Changed
52
+
53
+ * Require params 'tags' and 'tag_action' in `update_time_entries_tags()`.
54
+
55
+ ## [1.0.1] - 2015-12-10
56
+ ### Fixed
57
+
58
+ * Fix Toggl API call in `get_project_tasks()`. (Fixes #5)
59
+
60
+ ### Added
61
+
62
+ * Add `my_tasks()`.
63
+ * Add null checks to various methods.
64
+
65
+ ### Changed
66
+
67
+ * Require params 'name' and 'pid' in `create_task()`.
68
+
69
+ ## [1.0.0] - 2015-12-06
70
+ ### Added
71
+
72
+ * Add `my_deleted_projects()`.
73
+
74
+ ### Changed
75
+
76
+ * Exclude deleted projects from `my_projects()` results.
77
+ * Change `get_time_entries()` parameters.
78
+ - old: `start_timestamp=nil, end_timestamp=nil`
79
+ - new: `dates = {}`
80
+ * Raise RuntimeError w/ HTTP Status code if request is not successful.
81
+ * Handle 429 (Too Many Requests) by pausing for 1 second and retrying up to 3 times.
82
+ - API calls are limited to 1/sec due to toggl.com limits
83
+ * Refactor duplication out of GET/POST/PUT/DELETE API calls.
84
+
85
+ ## [0.2.0] - 2015-08-21
86
+ ### Added
87
+
88
+ * Add Ruby interface to most functions of [Toggl V8 API](https://github.com/toggl/toggl_api_docs/blob/master/toggl_api.md) (as of 2015-08-21).
89
+
90
+
91
+ [Unreleased]: https://github.com/kanet77/togglv8/compare/v1.2.1...HEAD
92
+ [1.2.1]: https://github.com/kanet77/togglv8/compare/v1.2.0...v1.2.1
93
+ [1.2.0]: https://github.com/kanet77/togglv8/compare/v1.1.0...v1.2.0
94
+ [1.1.0]: https://github.com/kanet77/togglv8/compare/v1.0.5...v1.1.0
95
+ [1.0.5]: https://github.com/kanet77/togglv8/compare/v1.0.4...v1.0.5
96
+ [1.0.4]: https://github.com/kanet77/togglv8/compare/v1.0.3...v1.0.4
97
+ [1.0.3]: https://github.com/kanet77/togglv8/compare/v1.0.2...v1.0.3
98
+ [1.0.2]: https://github.com/kanet77/togglv8/compare/v1.0.1...v1.0.2
99
+ [1.0.1]: https://github.com/kanet77/togglv8/compare/v1.0.0...v1.0.1
100
+ [1.0.0]: https://github.com/kanet77/togglv8/compare/v0.2.0...v1.0.0
101
+ [0.2.0]: https://github.com/kanet77/togglv8/compare/a1d5cc5...v0.2.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in togglv8.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013-2015 Tom Kane
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,160 @@
1
+
2
+ # Toggl API v8
3
+
4
+ [![Gem Version](https://badge.fury.io/rb/togglv8.svg)](https://badge.fury.io/rb/togglv8) [![Build Status](https://api.travis-ci.org/kanet77/togglv8.svg "Build Status")](https://travis-ci.org/kanet77/togglv8) [![Coverage Status](https://coveralls.io/repos/kanet77/togglv8/badge.svg?branch=master&service=github)](https://coveralls.io/github/kanet77/togglv8?branch=master) [![Code Climate](https://codeclimate.com/github/kanet77/togglv8/badges/gpa.svg)](https://codeclimate.com/github/kanet77/togglv8)
5
+
6
+ [Toggl](http://www.toggl.com) is a time tracking tool.
7
+
8
+ [togglv8](/) is a Ruby Wrapper for [Toggl API v8](https://github.com/toggl/toggl_api_docs). It is designed to mirror the Toggl API as closely as possible.
9
+
10
+ togglv8 supports both [Toggl API](https://github.com/toggl/toggl_api_docs/blob/master/toggl_api.md) and [Reports API](https://github.com/toggl/toggl_api_docs/blob/master/reports.md)
11
+
12
+ ## Change Log
13
+
14
+ See [CHANGELOG](CHANGELOG.md) for a summary of notable changes in each version.
15
+
16
+ ## Installation
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ ```ruby
21
+ gem 'togglv8'
22
+ ```
23
+
24
+ And then execute:
25
+
26
+ $ bundle
27
+
28
+ Or install it yourself as:
29
+
30
+ $ gem install togglv8
31
+
32
+ ## Initialization
33
+
34
+ ### TogglV8::API
35
+
36
+ TogglV8::API communicates with [Toggl API v8](https://github.com/toggl/toggl_api_docs/blob/master/toggl_api.md) and can be initialized in one of three ways.
37
+
38
+ ```ruby
39
+ TogglV8::API.new # reads API token from file ~/.toggl
40
+ TogglV8::API.new(api_token) # explicit API token
41
+ TogglV8::API.new(email, password) # email & password
42
+ ```
43
+
44
+ ### TogglV8::ReportsV2
45
+
46
+ TogglV8::ReportsV2 communicates with [Toggl Reports API v2](https://github.com/toggl/toggl_api_docs/blob/master/reports.md) and can be initialized in one of three ways. Toggl.com requires authentication with an API token for Reports API v2.
47
+
48
+ ```ruby
49
+ TogglV8::ReportsV2.new # reads API token from file ~/.toggl
50
+ TogglV8::ReportsV2.new(toggl_api_file: toggl_file) # reads API token from toggl_file
51
+ TogglV8::ReportsV2.new(api_token: api_token) # explicit API token
52
+ ```
53
+
54
+ **Note:** `workspace_id` must be set in order to generate reports.
55
+
56
+ ```ruby
57
+ toggl = TogglV8::API.new
58
+ reports = TogglV8::ReportsV2.new
59
+ reports.workspace_id = toggl.workspaces.first['id']
60
+ ```
61
+
62
+ ## Usage
63
+
64
+ This short example shows one way to create a time entry for the first workspace of the user identified by `<API_TOKEN>`. It then generates various reports containing that time entry.
65
+
66
+ ```ruby
67
+ require 'togglv8'
68
+ require 'json'
69
+
70
+ toggl_api = TogglV8::API.new(<API_TOKEN>)
71
+ user = toggl_api.me(all=true)
72
+ workspaces = toggl_api.my_workspaces(user)
73
+ workspace_id = workspaces.first['id']
74
+ time_entry = toggl_api.create_time_entry({
75
+ 'description' => "My awesome workspace time entry",
76
+ 'wid' => workspace_id,
77
+ 'duration' => 1200,
78
+ 'start' => toggl_api.iso8601((Time.now - 3600).to_datetime),
79
+ 'created_with' => "My awesome Ruby application"
80
+ })
81
+
82
+ begin
83
+ reports = TogglV8::ReportsV2.new(api_token: <API_TOKEN>)
84
+ begin
85
+ reports.summary
86
+ rescue Exception => e
87
+ puts e.message # workspace_id is required
88
+ end
89
+ reports.workspace_id = workspace_id
90
+ summary = reports.summary
91
+ puts "Generating summary JSON..."
92
+ puts JSON.pretty_generate(summary)
93
+ puts "Generating summary PDF..."
94
+ reports.write_summary('toggl_summary.pdf')
95
+ puts "Generating weekly CSV..."
96
+ reports.write_weekly('toggl_weekly.csv')
97
+ puts "Generating details XLS..."
98
+ reports.write_details('toggl_details.xls')
99
+ # Note: toggl.com does not generate Weekly XLS report (as of 2016-07-24)
100
+ ensure
101
+ toggl_api.delete_time_entry(time_entry['id'])
102
+ end
103
+ ```
104
+
105
+ See specs for more examples.
106
+
107
+ **Note:** Requests are rate-limited. The togglv8 gem will handle a 429 response by pausing for 1 second and trying again, for up to 3 attempts. See [Toggl API docs](https://github.com/toggl/toggl_api_docs#the-api-format):
108
+
109
+ > For rate limiting we have implemented a Leaky bucket. When a limit has been hit the request will get a HTTP 429 response and it's the task of the client to sleep/wait until bucket is empty. Limits will and can change during time, but a safe window will be 1 request per second. Limiting is applied per api token per IP, meaning two users from the same IP will get their rate allocated separately.
110
+
111
+ ## Debugging
112
+
113
+ The `TogglV8::API#debug` method determines if debug output is printed to STDOUT. This code snippet demonstrates the debug output.
114
+
115
+ ```ruby
116
+ require 'togglv8'
117
+
118
+ toggl = TogglV8::API.new
119
+
120
+ toggl.debug(true) # or simply toggl.debug
121
+ user1 = toggl.me
122
+ puts "user: #{user1['fullname']}, debug: true"
123
+
124
+ puts '-'*80
125
+
126
+ toggl.debug(false)
127
+ user2 = toggl.me
128
+ puts "user: #{user2['fullname']}, debug: false"
129
+ ```
130
+
131
+ ## Documentation
132
+
133
+ Run `rdoc` to generate documentation. Open `doc/index.html` in your browser.
134
+
135
+ Also available on [DocumentUp](https://documentup.com/kanet77/togglv8)
136
+
137
+ ## Acknowledgements
138
+
139
+ - Thanks to the following contributors (in alphabetical order):
140
+ * [archonic](https://github.com/archonic) ([fork](https://github.com/archonic/togglv8))
141
+ * [ddiatmb](https://github.com/ddiatmb) ([fork](https://github.com/ddiatmb/togglv8))
142
+ * [itaymendel](https://github.com/itaymendel)
143
+ * [ppawlikmb](https://github.com/ppawlikmb) ([fork](https://github.com/ppawlikmb/togglv8))
144
+ * [worldsmithroy](https://github.com/worldsmithroy) ([fork](https://github.com/worldsmithroy/togglv8))
145
+ - Thanks to [Koen Van der Auwera](https://github.com/atog) for the [Ruby Wrapper for Toggl API v6](https://github.com/atog/toggl)
146
+ - Thanks to the Toggl team for exposing the API.
147
+
148
+ ## Contributing
149
+
150
+ 1. Fork it ( https://github.com/kanet77/togglv8/fork )
151
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
152
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
153
+ 4. Push to the branch (`git push origin my-new-feature`)
154
+ 5. Create a new Pull Request
155
+
156
+ Pull Requests that include tests are **much** more likely to be accepted and merged quickly.
157
+
158
+ ## License
159
+
160
+ Copyright (c) 2013-2016 Tom Kane. Released under the [MIT License](http://opensource.org/licenses/mit-license.php). See [LICENSE.txt](LICENSE.txt) for details.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/clean'
3
+
4
+ CLEAN.add 'coverage'
5
+ CLEAN.add 'doc'
6
+ CLEAN.include 'tmp-*'
data/lib/logging.rb ADDED
@@ -0,0 +1,38 @@
1
+ # :nocov:
2
+ require 'logger'
3
+ # require 'awesome_print' # for debug output
4
+
5
+ # From http://stackoverflow.com/questions/917566/ruby-share-logger-instance-among-module-classes
6
+ module Logging
7
+ class << self
8
+ def logger
9
+ @logger ||= Logger.new($stdout)
10
+ end
11
+
12
+ def logger=(logger)
13
+ @logger = logger
14
+ end
15
+ end
16
+
17
+ # Addition
18
+ def self.included(base)
19
+ class << base
20
+ def logger
21
+ Logging.logger
22
+ end
23
+ end
24
+ end
25
+
26
+ def logger
27
+ Logging.logger
28
+ end
29
+
30
+ def debug(debug=true)
31
+ if debug
32
+ logger.level = Logger::DEBUG
33
+ else
34
+ logger.level = Logger::WARN
35
+ end
36
+ end
37
+ end
38
+ # :nocov:
data/lib/reportsv2.rb ADDED
@@ -0,0 +1,177 @@
1
+ module TogglV8
2
+ TOGGL_REPORTS_URL = 'https://api.track.toggl.com/reports/api/v2'
3
+
4
+ class ReportsV2
5
+ include TogglV8::Connection
6
+
7
+ REPORTS_V2_URL = TOGGL_REPORTS_URL + 'v2/'
8
+
9
+ attr_reader :conn
10
+
11
+ attr_accessor :workspace_id
12
+
13
+ def initialize(opts={})
14
+ debug(false)
15
+
16
+ @user_agent = TogglV8::NAME
17
+
18
+ username = opts[:api_token]
19
+ if username.nil?
20
+ toggl_api_file = opts[:toggl_api_file] || File.join(Dir.home, TOGGL_FILE)
21
+ if File.exist?(toggl_api_file) then
22
+ username = IO.read(toggl_api_file)
23
+ else
24
+ raise "Expecting one of:\n" +
25
+ " 1) api_token in file #{toggl_api_file}, or\n" +
26
+ " 2) parameter: (toggl_api_file), or\n" +
27
+ " 3) parameter: (api_token), or\n" +
28
+ "\n\tSee https://github.com/kanet77/togglv8#togglv8reportsv2" +
29
+ "\n\tand https://github.com/toggl/toggl_api_docs/blob/master/reports.md#authentication"
30
+ end
31
+ end
32
+
33
+ @conn = TogglV8::Connection.open(username, API_TOKEN, REPORTS_V2_URL, opts)
34
+ end
35
+
36
+ ##
37
+ # ---------
38
+ # :section: Report
39
+ #
40
+ # The following parameters and filters can be used in all of the reports
41
+ #
42
+ # user_agent : the name of this application so Toggl can get in touch
43
+ # (string, *required*)
44
+ # workspace_id : The workspace whose data you want to access.
45
+ # (integer, *required*)
46
+ # since : ISO 8601 date (YYYY-MM-DD), by default until - 6 days.
47
+ # (string)
48
+ # until : ISO 8601 date (YYYY-MM-DD), by default today
49
+ # (string)
50
+ # billable : possible values: yes/no/both, default both
51
+ # client_ids : client ids separated by a comma, 0 if you want to filter out time entries without a client
52
+ # project_ids : project ids separated by a comma, 0 if you want to filter out time entries without a project
53
+ # user_ids : user ids separated by a comma
54
+ # members_of_group_ids : group ids separated by a comma. This limits provided user_ids to the provided group members
55
+ # or_members_of_group_ids : group ids separated by a comma. This extends provided user_ids with the provided group members
56
+ # tag_ids : tag ids separated by a comma, 0 if you want to filter out time entries without a tag
57
+ # task_ids : task ids separated by a comma, 0 if you want to filter out time entries without a task
58
+ # time_entry_ids : time entry ids separated by a comma
59
+ # description : time entry description
60
+ # (string)
61
+ # without_description : filters out the time entries which do not have a description ('(no description)')
62
+ # (true/false)
63
+ # order_field : date/description/duration/user in detailed reports
64
+ # title/duration/amount in summary reports
65
+ # title/day1/day2/day3/day4/day5/day6/day7/week_total in weekly report
66
+ # order_desc : on for descending and off for ascending order
67
+ # (on/off)
68
+ # distinct_rates : on/off, default off
69
+ # rounding : on/off, default off, rounds time according to workspace settings
70
+ # display_hours : decimal/minutes, display hours with minutes or as a decimal number, default minutes
71
+ #
72
+ # NB! Maximum date span (until - since) is one year.
73
+
74
+ # extension can be one of ['.pdf', '.csv', '.xls']. Possibly others?
75
+ def report(type, extension, params)
76
+ raise "workspace_id is required" if @workspace_id.nil?
77
+ get "#{type}#{extension}", {
78
+ :'user_agent' => @user_agent,
79
+ :'workspace_id' => @workspace_id,
80
+ }.merge(params)
81
+ end
82
+
83
+ def weekly(extension='', params={})
84
+ report('weekly', extension, params)
85
+ end
86
+
87
+ def details(extension='', params={})
88
+ report('details', extension, params)
89
+ end
90
+
91
+ def summary(extension='', params={})
92
+ report('summary', extension, params)
93
+ end
94
+
95
+ ##
96
+ # ---------
97
+ # :section: Write report to file
98
+ #
99
+ def write_report(filename)
100
+ extension = File.extname(filename)
101
+ report = yield(extension)
102
+ File.open(filename, "wb") do |file|
103
+ file.write(report)
104
+ end
105
+ end
106
+
107
+ def write_weekly(filename, params={})
108
+ write_report(filename) do |extension|
109
+ weekly(extension, params)
110
+ end
111
+ end
112
+
113
+ def write_details(filename, params={})
114
+ write_report(filename) do |extension|
115
+ details(extension, params)
116
+ end
117
+ end
118
+
119
+ def write_summary(filename, params={})
120
+ write_report(filename) do |extension|
121
+ summary(extension, params)
122
+ end
123
+ end
124
+
125
+
126
+ ##
127
+ # ---------
128
+ # :section: Miscellaneous information
129
+ #
130
+ def revision
131
+ get "revision"
132
+ end
133
+
134
+ def index
135
+ get "index"
136
+ end
137
+
138
+ def env
139
+ get "env"
140
+ end
141
+
142
+ ##
143
+ # ---------
144
+ # :section: Project Dashboard
145
+ #
146
+ # Project dashboard returns at-a-glance information for a single project.
147
+ # This feature is only available with Toggl pro.
148
+ #
149
+ # user_agent : email, or other way to contact client application developer
150
+ # (string, *required*)
151
+ # workspace_id : The workspace whose data you want to access
152
+ # (integer, *required*)
153
+ # project_id : The project whose data you want to access
154
+ # (integer, *required*)
155
+ # page : number of 'tasks_page' you want to fetch
156
+ # (integer, optional)
157
+ # order_field string : name/assignee/duration/billable_amount/estimated_seconds
158
+ # order_desc string : on/off, on for descending and off for ascending order
159
+ def project(project_id, params={})
160
+ raise "workspace_id is required" if @workspace_id.nil?
161
+ get "project", {
162
+ :'user_agent' => @user_agent,
163
+ :'workspace_id' => @workspace_id,
164
+ :'project_id' => project_id,
165
+ }.merge(params)
166
+ end
167
+
168
+ ##
169
+ # ---------
170
+ # :section: Error (for testing)
171
+ #
172
+ # excludes endpoints 'error500' and 'division_by_zero_error'
173
+ def error400
174
+ get "error400"
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,37 @@
1
+ module TogglV8
2
+ class API
3
+
4
+ ##
5
+ # ---------
6
+ # :section: Clients
7
+ #
8
+ # name : The name of the client (string, required, unique in workspace)
9
+ # wid : workspace ID, where the client will be used (integer, required)
10
+ # notes : Notes for the client (string, not required)
11
+ # hrate : The hourly rate for this client (float, not required, available only for pro workspaces)
12
+ # cur : The name of the client's currency (string, not required, available only for pro workspaces)
13
+ # at : timestamp that is sent in the response, indicates the time client was last updated
14
+
15
+ def create_client(params)
16
+ requireParams(params, ['name', 'wid'])
17
+ post "clients", { 'client' => params }
18
+ end
19
+
20
+ def get_client(client_id)
21
+ get "clients/#{client_id}"
22
+ end
23
+
24
+ def update_client(client_id, params)
25
+ put "clients/#{client_id}", { 'client' => params }
26
+ end
27
+
28
+ def delete_client(client_id)
29
+ delete "clients/#{client_id}"
30
+ end
31
+
32
+ def get_client_projects(client_id, params={})
33
+ active = params.has_key?('active') ? "?active=#{params['active']}" : ""
34
+ get "clients/#{client_id}/projects#{active}"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,99 @@
1
+ require 'faraday'
2
+ require 'oj'
3
+
4
+ require_relative '../logging'
5
+
6
+ module TogglV8
7
+ module Connection
8
+ include Logging
9
+
10
+ DELAY_SEC = 1
11
+ MAX_RETRIES = 3
12
+
13
+ API_TOKEN = 'api_token'
14
+ TOGGL_FILE = '.toggl'
15
+
16
+ def self.open(username=nil, password=API_TOKEN, url=nil, opts={})
17
+ raise 'Missing URL' if url.nil?
18
+
19
+ Faraday.new(:url => url, :ssl => {:verify => true}) do |faraday|
20
+ faraday.adapter Faraday.default_adapter
21
+ faraday.request :url_encoded
22
+ faraday.response :logger, Logger.new('faraday.log') if opts[:log]
23
+ faraday.headers = { "Content-Type" => "application/json" }
24
+ faraday.request :authorization, :basic, username, password
25
+ end
26
+ end
27
+
28
+ def requireParams(params, fields=[])
29
+ raise ArgumentError, 'params is not a Hash' unless params.is_a? Hash
30
+ return if fields.empty?
31
+ errors = []
32
+ for f in fields
33
+ errors.push("params[#{f}] is required") unless params.has_key?(f)
34
+ end
35
+ raise ArgumentError, errors.join(', ') if !errors.empty?
36
+ end
37
+
38
+ def _call_api(procs)
39
+ # logger.debug(procs[:debug_output].call)
40
+ full_resp = nil
41
+ i = 0
42
+ loop do
43
+ i += 1
44
+ full_resp = procs[:api_call].call
45
+ # logger.ap(full_resp.env, :debug)
46
+ break if full_resp.status != 429 || i >= MAX_RETRIES
47
+ sleep(DELAY_SEC)
48
+ end
49
+
50
+ raise full_resp.headers['warning'] if full_resp.headers['warning'] && !full_resp.headers['warning'].match?(/Deprecation Notice\. Toggl Track API wont be available in this domain after June 2021/)
51
+ raise "HTTP Status: #{full_resp.status}" unless full_resp.success?
52
+ return {} if full_resp.body.nil? || full_resp.body == 'null'
53
+
54
+ full_resp
55
+ end
56
+
57
+ def get(resource, params={})
58
+ query_params = params.map { |k,v| "#{k}=#{v}" }.join('&')
59
+ resource += "?#{query_params}" unless query_params.empty?
60
+ resource.gsub!('+', '%2B')
61
+ full_resp = _call_api(debug_output: lambda { "GET #{resource}" },
62
+ api_call: lambda { self.conn.get(resource) } )
63
+ return {} if full_resp == {}
64
+ begin
65
+ resp = Oj.load(full_resp.body)
66
+ return resp['data'] if resp.respond_to?(:has_key?) && resp.has_key?('data')
67
+ return resp
68
+ rescue Oj::ParseError
69
+ return full_resp.body
70
+ end
71
+ end
72
+
73
+ def post(resource, data='')
74
+ resource.gsub!('+', '%2B')
75
+ full_resp = _call_api(debug_output: lambda { "POST #{resource} / #{data}" },
76
+ api_call: lambda { self.conn.post(resource, Oj.dump(data)) } )
77
+ return {} if full_resp == {}
78
+ resp = Oj.load(full_resp.body)
79
+ resp['data']
80
+ end
81
+
82
+ def put(resource, data='')
83
+ resource.gsub!('+', '%2B')
84
+ full_resp = _call_api(debug_output: lambda { "PUT #{resource} / #{data}" },
85
+ api_call: lambda { self.conn.put(resource, Oj.dump(data)) } )
86
+ return {} if full_resp == {}
87
+ resp = Oj.load(full_resp.body)
88
+ resp['data']
89
+ end
90
+
91
+ def delete(resource)
92
+ resource.gsub!('+', '%2B')
93
+ full_resp = _call_api(debug_output: lambda { "DELETE #{resource}" },
94
+ api_call: lambda { self.conn.delete(resource) } )
95
+ return {} if full_resp == {}
96
+ full_resp.body
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,14 @@
1
+ module TogglV8
2
+ class API
3
+
4
+ ##
5
+ # ---------
6
+ # :section: Dashboard
7
+ #
8
+ # See https://github.com/toggl/toggl_api_docs/blob/master/chapters/dashboard.md
9
+
10
+ def dashboard(workspace_id)
11
+ get "dashboard/#{workspace_id}"
12
+ end
13
+ end
14
+ end