fitbit_api 0.8.3 → 0.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c18d864ec416bee1e27c1b468d524bb8404e63076ba7652917270271d1eea58
4
- data.tar.gz: 6487033355317ece6cbac6eb43a2b744e975d48b524d88dd4c4378221b96e7e8
3
+ metadata.gz: 303b1eda2135541723313d505d4c34292e94b473c2a87cff32119bd8de97b0ea
4
+ data.tar.gz: c526445b8815cbe19ce92f6b762036aae9bbf57cfccea2cba9131ed63788307e
5
5
  SHA512:
6
- metadata.gz: 3146e0990d97885808beb6db94a877003af2ed3847ee135ceea082199d8623fdb54301fd66218e5e7b52629d513d70e53231a0a3deb23e229e98af0ce762b1fb
7
- data.tar.gz: 0b94b009192e261dde7ba9e98119dbc942290b12792113d858af621b34158a96f7f206d9cb35b0ba2334dc7f4cfd0b836e30c59102ce4e12d38e14c71bfc18a0
6
+ metadata.gz: d7cc3ae62f509e40a0c58bc31f5ffc75865909d482e862401afa0737fc6138ea226c3a9909721903b86f00febd1a8eea46ac01b1d93d2c9dbd0d706b6c201938
7
+ data.tar.gz: d0d6328a931c8d3fe931f6e31b283681092b31d4b50d5186aeb1a7b8efe885dbf828d0c058a5296c4a4abe9b7e8b13b183482d1f6dc6102aac38a98284d9f3ec
data/.travis.yml CHANGED
@@ -1,9 +1,8 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 2.7.2
4
+ - 2.6.6
3
5
  - 2.5.1
4
6
  - 2.4.2
5
7
  - 2.3.5
6
- - 2.2.2
7
- - 2.1.6
8
- - 2.0.0
9
8
  before_install: gem install bundler -v 1.10.3
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ 0.10.0
2
+ ------
3
+ - Add `auto_refresh_token` config to make token auto-refreshing configurable (defaults to true).
4
+ - Add `on_token_refresh` config to specify callback to execute on token refresh (optional).
5
+ - Bump `bundler` development dependency
6
+ - Clean up documentation formatting
7
+
8
+ 0.9.1
9
+ -----
10
+ - Fix `sleep_time_series` endpoint
11
+ - Tweak user-agent string in request headers
12
+
13
+ 0.9.0
14
+ -----
15
+ - Rework client to accept existing access tokens.
16
+ - Fix argument parsing in `FitbitAPI::Client` to respect fallback values passed to `#configure`.
17
+ - Add `byebug` as development dependency.
18
+
1
19
  0.8.3
2
20
  -----
3
21
  - Fix bug regarding optional `params` parsing for GET requests.
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/fitbit_api.svg)](https://badge.fury.io/rb/fitbit_api)
4
4
  [![Build Status](https://travis-ci.org/zokioki/fitbit_api.svg?branch=master)](https://travis-ci.org/zokioki/fitbit_api)
5
5
 
6
- FitbitAPI provides a Ruby interface to the [Fitbit Web API](https://dev.fitbit.com/reference/web-api/quickstart).
6
+ FitbitAPI provides a Ruby interface to the [Fitbit Web API](https://dev.fitbit.com/reference/web-api).
7
7
 
8
8
  ## Installation
9
9
 
@@ -27,12 +27,15 @@ You can reference the [fitbit_api_rails](https://github.com/zokioki/fitbit_api_r
27
27
 
28
28
  ### Quickstart
29
29
 
30
- If you already have access to a user's stored refresh token, you can instantiate a client instance like so:
30
+ If you already have a user's token data and Fitbit user_id:
31
31
 
32
32
  ```ruby
33
33
  client = FitbitAPI::Client.new(client_id: 'XXXXXX',
34
34
  client_secret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
35
- refresh_token: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
35
+ access_token: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
36
+ refresh_token: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
37
+ expires_at: 1234567890,
38
+ user_id: 'XXXXXX')
36
39
  ```
37
40
 
38
41
  ### OAuth 2.0 Authorization Flow
@@ -62,14 +65,14 @@ You're now authorized and can make calls to Fitbit's API.
62
65
 
63
66
  ### Interacting with the API
64
67
 
65
- Once a valid token has been generated, you're able to make API calls from the client object, like so:
68
+ Once a valid token has been generated, you're able to make API calls via the client object:
66
69
 
67
70
  ```ruby
68
71
  client.food_logs Date.today
69
72
  # => { "foods" => [{ "isFavorite" => true, "logDate" => "2015-06-26", "logId" => 1820, "loggedFood" => { "accessLevel" => "PUBLIC", "amount" => 132.57, "brand" => "", "calories" => 752, ...}] }
70
73
  ```
71
74
 
72
- To make responses more easily suited for attribute-assignment, they can be parsed to return a hash whose keys are in snake_case format. This can be done by setting the client's `snake_case_keys` option to `true`, like so:
75
+ To make responses more easily suited for attribute-assignment, they can be parsed to return a hash whose keys are in snake_case format. This can be done by setting the client's `snake_case_keys` option to `true`:
73
76
 
74
77
  ```ruby
75
78
  client.snake_case_keys = true
@@ -92,17 +95,29 @@ client.log_activity activity_id: 12345, durationMillis: '683300'
92
95
 
93
96
  When initializing a `FitbitAPI::Client` instance, you're given access to a handful of options:
94
97
 
95
- - `:api_version` - API version to be used when making requests (default: "1")
98
+ | option | description |
99
+ | ------------------ | -----------------------------|
100
+ | api_version | API version to be used when making requests (default: "1") |
101
+ | unit_system | The measurement unit system to use for response values (default: "en_US") |
102
+ | locale | The locale to use for response values (default: "en_US") |
103
+ | scope | A space-delimited list of permissions being requested (default: "activity nutrition profile settings sleep social weight heartrate") |
104
+ | snake_case_keys | Transform response payload's keys to snake case format (default: false) |
105
+ | symbolize_keys | Transform response payload's keys to symbols (default: false) |
106
+ | auto_refresh_token | Automatically refreshes the access token once expired (default: true) |
107
+ | on_token_refresh | A callback to be invoked whenever the access token is refreshed (default: nil) |
96
108
 
97
- - `:unit_system` - The measurement unit system to use for response values (default: "en_US" | available: "en_US", "en_GB", and "any" for metric)
109
+ If using this library in Rails, you can configure these options globally in an initializer:
98
110
 
99
- - `:locale` - The locale to use for response values (default: "en_US" | available: "en_US", "fr_FR", "de_DE", "es_ES", "en_GB", "en_AU", "en_NZ" and "ja_JP")
100
-
101
- - `:scope` - A space-delimited list of the permissions you are requesting (default: "activity nutrition profile settings sleep social weight heartrate" | available: "activity", "heartrate", "location", "nutrition", "profile", "settings" "sleep", "social" and "weight")
102
-
103
- - `:snake_case_keys` - Transform returned object's keys to snake case format (default: false)
104
-
105
- - `:symbolize_keys` - Transform returned object's keys to symbols (default: false)
111
+ ```ruby
112
+ # config/initializers/fitbit_api.rb
113
+
114
+ FitbitAPI.configure do |config|
115
+ config.client_id = 'XXXX'
116
+ config.client_secret = 'xxxx'
117
+ config.snake_case_keys = true
118
+ config.symbolize_keys = true
119
+ end
120
+ ```
106
121
 
107
122
  ## License
108
123
 
data/fitbit_api.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ['Zoran']
10
10
 
11
11
  spec.summary = %q{A Ruby interface to the Fitbit Web API.}
12
- spec.homepage = FitbitAPI::REPO_URL
12
+ spec.homepage = 'https://github.com/zokioki/fitbit_api'
13
13
  spec.license = 'MIT'
14
14
 
15
15
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
@@ -21,7 +21,8 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  spec.add_runtime_dependency 'oauth2', '~> 1.0'
23
23
 
24
- spec.add_development_dependency 'bundler', '~> 1.10'
25
- spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'byebug'
25
+ spec.add_development_dependency 'bundler', '~> 2.3'
26
+ spec.add_development_dependency 'rake', '>= 12.3.3'
26
27
  spec.add_development_dependency 'rspec'
27
28
  end
@@ -14,15 +14,18 @@ module FitbitAPI
14
14
  # GET Activities
15
15
  # ==============
16
16
 
17
- # Retrieves a summary and list of a user's activities and activity log entries for a given day
18
- # in the format requested using units in the unit system which corresponds to the Accept-Language header provided.
17
+ # Retrieves a summary and list of a user's activities and activity log entries
18
+ # for a given day in the format requested using units in the unit system which
19
+ # corresponds to the Accept-Language header provided.
20
+ #
21
+ # @param date [Date] The date for which to retrieve the activity data.
19
22
 
20
23
  def daily_activity_summary(date=Date.today, opts={})
21
24
  get("user/#{user_id}/activities/date/#{format_date(date)}.json", opts)
22
25
  end
23
26
 
24
- # Retrieves a list of a user's frequent activities in the format requested using units
25
- # in the unit system which corresponds to the Accept-Language header provided.
27
+ # Retrieves a list of a user's frequent activities in the format requested using
28
+ # units in the unit system which corresponds to the Accept-Language header provided.
26
29
 
27
30
  def frequent_activities(opts={})
28
31
  get("user/#{user_id}/activities/frequent.json", opts)
@@ -38,22 +41,24 @@ module FitbitAPI
38
41
  get("user/#{user_id}/activities/favorite.json", opts)
39
42
  end
40
43
 
41
- # Gets a tree of all valid Fitbit public activities from
42
- # the activities catalog as well as private custom activities the user created.
44
+ # Gets a tree of all valid Fitbit public activities from the activities catalog
45
+ # as well as private custom activities the user created.
43
46
 
44
47
  def all_activities(opts={})
45
48
  get('activities.json', opts)
46
49
  end
47
50
 
48
- # Retrieves a list of a user's activity log entries before or after a given day with
49
- # offset and limit using units in the unit system which corresponds to the Accept-Language header provided.
50
-
51
- # ==== URL Parameters
52
- # * +:beforeDate+ - the date; formatted in yyyy-MM-ddTHH:mm:ss
53
- # * +:afterDate+ - the date; formatted in yyyy-MM-ddTHH:mm:ss
54
- # * +:sort+ - the sort order of entries by date (asc or desc)
55
- # * +:offset+ - the offset number of entries. Must always be 0
56
- # * +:limit+ - the max of the number of entries returned (max: 20)
51
+ # Retrieves a list of a user's activity log entries before or after a given day
52
+ # with offset and limit using units in the unit system which corresponds to the
53
+ # Accept-Language header provided.
54
+ #
55
+ # activity_logs_list(before_date: Date.parse('2021-05-24'), limit: 5)
56
+ #
57
+ # @option before_date [Date] Specify when filtering entries that occured before the given date
58
+ # @option after_date [Date] Specify when filtering entries that occured after the given date
59
+ # @option sort [String] the Sort order of entries by date (asc or desc)
60
+ # @option offset [Integer] The offset number of entries. Must always be 0
61
+ # @option limit [Integer] The max of the number of entries returned (max: 20)
57
62
 
58
63
  def activity_logs_list(opts={})
59
64
  opts[:params] = {}
@@ -68,16 +73,18 @@ module FitbitAPI
68
73
  end
69
74
 
70
75
  # Returns the details of a specific activity in the Fitbit activities database in the format requested.
71
- # If activity has levels, also returns a list of activity level details.
76
+ # If activity has levels, also returns a list of activity level details.
77
+ #
78
+ # @param activity_id [Integer, String] The ID of the desired activity to retrieve
72
79
 
73
80
  def activity(activity_id)
74
81
  get("activities/#{activity_id}.json")
75
82
  end
76
83
 
77
84
  # Retrieves the user's activity statistics in the format requested using units
78
- # in the unit system which corresponds to the Accept-Language header provided.
79
- # Activity statistics includes Lifetime and Best achievement values from the
80
- # My Achievements tile on the website dashboard.
85
+ # in the unit system which corresponds to the Accept-Language header provided.
86
+ # Activity statistics includes Lifetime and Best achievement values from the
87
+ # My Achievements tile on the website dashboard.
81
88
 
82
89
  def lifetime_stats(opts={})
83
90
  get("user/#{user_id}/activities.json", opts)
@@ -142,23 +149,26 @@ module FitbitAPI
142
149
  # ===============
143
150
 
144
151
  # Creates log entry for an activity or user's private custom activity using units
145
- # in the unit system which corresponds to the Accept-Language header provided.
146
-
147
- # ==== POST Parameters
148
- # * +:activityId+ - activity id
149
- # * +:activityName+ - custom activity name. Either activity ID or activityName must be provided
150
- # * +:manualCalories+ - calories burned, specified manually. Required with activityName, otherwise optional
151
- # * +:startTime+ - activity start time; formatted in HH:mm:ss
152
- # * +:durationMillis+ - duration in milliseconds
153
- # * +:date+ - log entry date; formatted in yyyy-MM-dd
154
- # * +:distance+ - distance; required for logging directory activity; formatted in X.XX
155
- # * +:distanceUnit+ - distance measurement unit
152
+ # in the unit system which corresponds to the Accept-Language header provided.
153
+ #
154
+ # log_activity(body: { activity_id: 90013, manual_calories: 300, duration_millis: 6000000 })
155
+ #
156
+ # @option activity_id [Integer, String] The activity ID
157
+ # @option activity_name [String] Custom activity name. Either activity ID or activity_name must be provided
158
+ # @option manual_calories [Integer] Calories burned, specified manually. Required with activity_name, otherwise optional
159
+ # @option start_time [String] Activity start time; formatted in HH:mm:ss
160
+ # @option duration_millis [Integer] Duration in milliseconds
161
+ # @option date [String] Log entry date; formatted in yyyy-MM-dd
162
+ # @option distance [Integer] Distance; required for logging directory activity
163
+ # @option distance_unit [String] Distance measurement unit
156
164
 
157
165
  def log_activity(opts)
158
166
  post("user/#{user_id}/activities.json", opts)
159
167
  end
160
168
 
161
169
  # Adds the activity with the given ID to user's list of favorite activities.
170
+ #
171
+ # @param activity_id [Integer] The activity ID
162
172
 
163
173
  def add_favorite_activity(activity_id)
164
174
  post("user/#{user_id}/activities/favorite/#{activity_id}.json")
@@ -168,12 +178,16 @@ module FitbitAPI
168
178
  # =================
169
179
 
170
180
  # Deletes a user's activity log entry with the given ID.
181
+ #
182
+ # @param activity_log_id [Integer] The ID of the activity log entry
171
183
 
172
184
  def delete_activity(activity_log_id)
173
185
  delete("user/#{user_id}/activities/#{activity_log_id}.json")
174
186
  end
175
187
 
176
188
  # Removes the activity with the given ID from a user's list of favorite activities.
189
+ #
190
+ # @param activity_id [Integer] The ID of the activity to be removed
177
191
 
178
192
  def delete_favorite_activity(activity_id)
179
193
  delete("user/#{user_id}/activities/favorite/#{activity_id}.json")
@@ -4,6 +4,8 @@ module FitbitAPI
4
4
  # ==========
5
5
 
6
6
  # Returns a list of the set alarms connected to a user's account.
7
+ #
8
+ # @params tracker_id [Integer] The ID of the tracker for which the data is returned
7
9
 
8
10
  def alarms(tracker_id, opts={})
9
11
  get("user/#{user_id}/devices/tracker/#{tracker_id}/alarms.json", opts)
@@ -13,28 +15,33 @@ module FitbitAPI
13
15
  # ===========
14
16
 
15
17
  # Adds the alarm settings to a given ID for a given device.
16
-
17
- # ==== POST Parameters
18
- # * +:time+ - time of day that the alarm vibrates with a UTC timezone offset, e.g. 07:15-08:00
19
- # * +:enabled+ - boolean; if false, alarm does not vibrate until enabled is set to true
20
- # * +:recurring+ - boolean; if false, the alarm is a single event
21
- # * +:weekDays+ - comma separated list of days of the week on which the alarm vibrates (MONDAY,TUESDAY)
18
+ #
19
+ # add_alarm(body: { time: "07:15-08:00", enabled: true, recurring: true, week_days: "MONDAY,FRIDAY,SATURDAY" })
20
+ #
21
+ # @param tracker_id [Integer] The ID of the tracker for which the alarm is created
22
+ #
23
+ # @option time [String] Time of day that the alarm vibrates with a UTC timezone offset, e.g. 07:15-08:00
24
+ # @option enabled [Boolean] If false, alarm does not vibrate until enabled is set to true
25
+ # @option recurring [Boolean] If false, the alarm is a single event
26
+ # @option week_days [String] Comma separated list of days of the week on which the alarm vibrates (MONDAY,TUESDAY)
22
27
 
23
28
  def add_alarm(tracker_id, opts={})
24
29
  post("user/#{user_id}/devices/tracker/#{tracker_id}/alarms.json", opts)
25
30
  end
26
31
 
27
32
  # Updates the alarm entry with a given ID for a given device.
28
-
29
- # ==== POST Parameters
30
- # * +:time+ - time of day that the alarm vibrates with a UTC timezone offset, e.g. 07:15-08:00
31
- # * +:enabled+ - boolean; if false, alarm does not vibrate until enabled is set to true
32
- # * +:recurring+ - boolean; if false, the alarm is a single event
33
- # * +:weekDays+ - comma separated list of days of the week on which the alarm vibrates (MONDAY,TUESDAY)
34
- # * +:snoozeLength+ - integer; minutes between alarms
35
- # * +:snoozeCount+ - integer; maximum snooze count
36
- # * +:label+ - string; label for alarm
37
- # * +:vibe+ - vibe pattern; only one value for now (DEFAULT)
33
+ #
34
+ # @param tracker_id [Integer] The ID of the tracker for which the alarm is created
35
+ # @param alarm_id [Integer] The ID of the alarm to be updated
36
+ #
37
+ # @option time [String] Time of day that the alarm vibrates with a UTC timezone offset, e.g. 07:15-08:00
38
+ # @option enabled [Boolean] If false, alarm does not vibrate until enabled is set to true
39
+ # @option recurring [Boolean] If false, the alarm is a single event
40
+ # @option week_days [String] Comma separated list of days of the week on which the alarm vibrates (MONDAY,TUESDAY)
41
+ # @option snooze_length [Integer] Minutes between alarms
42
+ # @option snooze_count [Integer] Maximum snooze count
43
+ # @option label [String] Label for alarm
44
+ # @option vibe [String] Vibe pattern; only one value for now (DEFAULT)
38
45
 
39
46
  def update_alarm(tracker_id, alarm_id, opts={})
40
47
  post("user/#{user_id}/devices/tracker/#{tracker_id}/alarms/#{alarm_id}.json", opts)
@@ -44,6 +51,9 @@ module FitbitAPI
44
51
  # =============
45
52
 
46
53
  # Deletes the user's device alarm entry with the given ID for a given device.
54
+ #
55
+ # @param tracker_id [Integer] The ID of the tracker for which the alarm is to be deleted
56
+ # @param alarm_id [Integer] The ID of the alarm to be deleted
47
57
 
48
58
  def delete_alarm(tracker_id, alarm_id, opts={})
49
59
  delete("user/#{user_id}/devices/tracker/#{tracker_id}/alarms/#{alarm_id}.json", opts)
@@ -21,4 +21,7 @@ module FitbitAPI
21
21
 
22
22
  define_setting :snake_case_keys, false
23
23
  define_setting :symbolize_keys, false
24
+
25
+ define_setting :auto_refresh_token, true
26
+ define_setting :on_token_refresh
24
27
  end
@@ -13,22 +13,15 @@ require 'fitbit_api/water'
13
13
 
14
14
  module FitbitAPI
15
15
  class Client
16
- attr_accessor :api_version, :unit_system, :locale, :scope, :snake_case_keys, :symbolize_keys
17
- attr_reader :user_id
18
-
19
- def initialize(opts)
20
- missing_args = [:client_id, :client_secret] - opts.keys
21
- raise FitbitAPI::InvalidArgumentError, "Required arguments: #{missing_args.join(', ')}" if missing_args.size > 0
22
-
23
- %w(client_id client_secret redirect_uri site_url authorize_url token_url
24
- unit_system locale scope api_version snake_case_keys symbolize_keys).each do |attr|
25
- instance_variable_set("@#{attr}", (opts[attr.to_sym] || FitbitAPI.send(attr)))
26
- end
27
-
28
- @client = OAuth2::Client.new(@client_id, @client_secret, site: @site_url,
29
- authorize_url: @authorize_url, token_url: @token_url)
30
-
31
- restore_token(opts[:refresh_token]) if opts[:refresh_token]
16
+ attr_accessor :api_version, :unit_system, :locale, :scope,
17
+ :snake_case_keys, :symbolize_keys, :auto_refresh_token, :on_token_refresh
18
+ attr_reader :token, :user_id
19
+
20
+ def initialize(opts={})
21
+ validate_args(opts)
22
+ assign_attrs(opts)
23
+ set_client
24
+ establish_token(opts)
32
25
  end
33
26
 
34
27
  def auth_url
@@ -36,56 +29,118 @@ module FitbitAPI
36
29
  end
37
30
 
38
31
  def get_token(auth_code)
39
- @token = @client.auth_code.get_token(auth_code, redirect_uri: @redirect_uri, headers: auth_header)
32
+ @token = @client.auth_code.get_token(
33
+ auth_code,
34
+ redirect_uri: @redirect_uri,
35
+ headers: auth_headers
36
+ )
40
37
  @user_id = @token.params['user_id']
41
- return @token
38
+ @token
42
39
  end
43
40
 
44
- def restore_token(refresh_token)
45
- @token = OAuth2::AccessToken.from_hash(@client, refresh_token: refresh_token).refresh!(headers: auth_header)
46
- @user_id = @token.params['user_id']
47
- return @token
41
+ def refresh_token!
42
+ @token = @token.refresh!(headers: auth_headers)
43
+ @user_id ||= @token.params['user_id']
44
+ on_token_refresh.call(@token) if on_token_refresh.respond_to?(:call)
45
+
46
+ @token
48
47
  end
49
48
 
50
- def token
51
- @token.expired? ? refresh_token : @token
49
+ def get(path, opts={})
50
+ request(:get, path, opts)
52
51
  end
53
52
 
54
- def refresh_token
55
- @token = @token.refresh!(headers: auth_header)
53
+ def post(path, opts={})
54
+ request(:post, path, opts)
56
55
  end
57
56
 
58
- def auth_header
59
- { 'Authorization' => ('Basic ' + Base64.encode64(@client_id + ':' + @client_secret)) }
57
+ def delete(path, opts={})
58
+ request(:delete, path, opts)
60
59
  end
61
60
 
62
- def request_headers
63
- {
64
- 'User-Agent' => "fitbit_api-#{FitbitAPI::VERSION} gem (#{FitbitAPI::REPO_URL})",
65
- 'Accept-Language' => @unit_system,
66
- 'Accept-Locale' => @locale
67
- }
61
+ private
62
+
63
+ def validate_args(opts)
64
+ required_args = %i[client_id client_secret].freeze
65
+ missing_args = []
66
+
67
+ required_args.each do |arg|
68
+ missing_args << arg if (opts[arg] || FitbitAPI.send(arg)).nil?
69
+ end
70
+
71
+ return if missing_args.empty?
72
+ raise FitbitAPI::InvalidArgumentError,
73
+ "Required arguments: #{missing_args.join(', ')}"
68
74
  end
69
75
 
70
- def get(path, opts={})
71
- params = opts.delete(:params) || {}
72
- response = token.get(("#{@api_version}/" + path), params: deep_keys_to_camel_case!(params), headers: request_headers).response
73
- object = MultiJson.load(response.body) unless response.status == 204
74
- process_keys!(object, opts)
76
+ def assign_attrs(opts)
77
+ attrs = %i[client_id client_secret redirect_uri site_url
78
+ authorize_url token_url unit_system locale scope
79
+ api_version snake_case_keys symbolize_keys
80
+ auto_refresh_token on_token_refresh].freeze
81
+
82
+ attrs.each do |attr|
83
+ instance_variable_set("@#{attr}", (opts[attr] || FitbitAPI.send(attr)))
84
+ end
85
+
86
+ @user_id = opts[:user_id]
75
87
  end
76
88
 
77
- def post(path, opts={})
78
- response = token.post(("#{@api_version}/" + path), body: deep_keys_to_camel_case!(opts), headers: request_headers).response
79
- object = MultiJson.load(response.body) unless response.status == 204
80
- process_keys!(object, opts)
89
+ def set_client
90
+ @client = OAuth2::Client.new(
91
+ @client_id,
92
+ @client_secret,
93
+ site: @site_url,
94
+ authorize_url: @authorize_url,
95
+ token_url: @token_url
96
+ )
81
97
  end
82
98
 
83
- def delete(path, opts={})
84
- response = token.delete(("#{@api_version}/" + path), headers: request_headers).response
99
+ def establish_token(opts)
100
+ return unless opts[:access_token] || opts[:refresh_token]
101
+
102
+ if opts[:access_token] && !opts[:user_id]
103
+ raise FitbitAPI::InvalidArgumentError,
104
+ 'user_id is required if using existing access token'
105
+ end
106
+
107
+ @token = OAuth2::AccessToken.new(
108
+ @client,
109
+ opts[:access_token],
110
+ refresh_token: opts[:refresh_token],
111
+ expires_at: opts[:expires_at]
112
+ )
113
+
114
+ refresh_token! if @token.token.empty?
115
+ end
116
+
117
+ def request(verb, path, opts={})
118
+ request_path = "#{@api_version}/#{path}"
119
+ request_options = opts.merge(headers: request_headers)
120
+
121
+ deep_keys_to_camel_case!(request_options[:params])
122
+ deep_keys_to_camel_case!(request_options[:body])
123
+
124
+ refresh_token! if auto_refresh_token && token.expired?
125
+
126
+ response = token.public_send(verb, request_path, request_options).response
85
127
  object = MultiJson.load(response.body) unless response.status == 204
128
+
86
129
  process_keys!(object, opts)
87
130
  end
88
131
 
132
+ def auth_headers
133
+ { 'Authorization' => ('Basic ' + Base64.encode64(@client_id + ':' + @client_secret)) }
134
+ end
135
+
136
+ def request_headers
137
+ {
138
+ 'User-Agent' => "fitbit_api gem (v#{FitbitAPI::VERSION})",
139
+ 'Accept-Language' => @unit_system,
140
+ 'Accept-Locale' => @locale
141
+ }
142
+ end
143
+
89
144
  def process_keys!(object, opts={})
90
145
  deep_keys_to_snake_case!(object) if (opts[:snake_case_keys] || snake_case_keys)
91
146
  deep_symbolize_keys!(object) if (opts[:symbolize_keys] || symbolize_keys)
@@ -31,28 +31,30 @@ module FitbitAPI
31
31
  # ==========
32
32
 
33
33
  # Creates or updates a user's daily activity goals and returns a response using units
34
- # in the unit system which corresponds to the Accept-Language header provided.
35
-
36
- # ==== POST Parameters
37
- # * +:caloriesOut+ - calories output goal value; integer
38
- # * +:activeMinutes+ - active minutes goal value; integer
39
- # * +:floors+ - floor goal value; integer
40
- # * +:distance+ - distance goal value; X.XX or integer
41
- # * +:steps+ - steps goal value; integer
34
+ # in the unit system which corresponds to the Accept-Language header provided.
35
+ #
36
+ # create_or_update_daily_goals(body: {calories_out: 2000, active_minutes: 90, floors: 5})
37
+ #
38
+ # @option calories_out [Integer] Calories output goal value
39
+ # @option active_minutes [Integer] Active minutes goal value
40
+ # @option floors [Integer] Floor goal value
41
+ # @option distance [Integer, Float] Distance goal value
42
+ # @option steps [Integer] Steps goal value
42
43
 
43
44
  def create_or_update_daily_goals(opts={})
44
45
  post("user/#{user_id}/activities/goals/daily.json", opts)
45
46
  end
46
47
 
47
48
  # Creates or updates a user's weekly activity goals and returns a response using units
48
- # in the unit system which corresponds to the Accept-Language header provided.
49
-
50
- # ==== POST Parameters
51
- # * +:caloriesOut+ - calories output goal value; integer
52
- # * +:activeMinutes+ - active minutes goal value; integer
53
- # * +:floors+ - floor goal value; integer
54
- # * +:distance+ - distance goal value; X.XX or integer
55
- # * +:steps+ - steps goal value; integer
49
+ # in the unit system which corresponds to the Accept-Language header provided.
50
+ #
51
+ # create_or_update_weekly_goals(body: { active_minutes: 300, floors: 20 })
52
+ #
53
+ # @option calories_out [Integer] Calories output goal value
54
+ # @option active_minutes [Integer] Active minutes goal value
55
+ # @option floors [Integer] Floor goal value
56
+ # @option distance [Integer, Float] Distance goal value
57
+ # @option steps [Integer] Steps goal value
56
58
 
57
59
  def create_or_update_weekly_goals(opts={})
58
60
  post("user/#{user_id}/activities/goals/weekly.json", opts)
@@ -1,25 +1,27 @@
1
- module Configuration
2
- def configure
3
- yield self
4
- end
1
+ module FitbitAPI
2
+ module Configuration
3
+ def configure
4
+ yield self
5
+ end
5
6
 
6
- def define_setting(name, default = nil)
7
- class_variable_set("@@#{name}", default)
7
+ def define_setting(name, default = nil)
8
+ class_variable_set("@@#{name}", default)
8
9
 
9
- define_class_method "#{name}=" do |value|
10
- class_variable_set("@@#{name}", value)
11
- end
10
+ define_class_method "#{name}=" do |value|
11
+ class_variable_set("@@#{name}", value)
12
+ end
12
13
 
13
- define_class_method name do
14
- class_variable_get("@@#{name}")
14
+ define_class_method name do
15
+ class_variable_get("@@#{name}")
16
+ end
15
17
  end
16
- end
17
18
 
18
- private
19
+ private
19
20
 
20
- def define_class_method(name, &block)
21
- (class << self; self; end).instance_eval do
22
- define_method name, &block
21
+ def define_class_method(name, &block)
22
+ (class << self; self; end).instance_eval do
23
+ define_method name, &block
24
+ end
23
25
  end
24
26
  end
25
27
  end
@@ -1,7 +1,7 @@
1
1
  module FitbitAPI
2
2
  class Client
3
3
 
4
- PERIODS = %w(1d 7d 30d 1w 1m 3m 6m 1y max)
4
+ PERIODS = %w(1d 7d 30d 1w 1m 3m 6m 1y max).freeze
5
5
 
6
6
  def format_date(date)
7
7
  if [Date, Time, DateTime].include?(date.class)
@@ -25,9 +25,9 @@ module FitbitAPI
25
25
  end
26
26
 
27
27
  if period
28
- result = get("user/#{user_id}/activities/#{resource}/date/#{format_date(end_date)}/#{period}.json", opts)
28
+ result = get("user/#{user_id}/sleep/#{resource}/date/#{format_date(end_date)}/#{period}.json", opts)
29
29
  else
30
- result = get("user/#{user_id}/activities/#{resource}/date/#{format_date(start_date)}/#{format_date(end_date)}.json", opts)
30
+ result = get("user/#{user_id}/sleep/#{resource}/date/#{format_date(start_date)}/#{format_date(end_date)}.json", opts)
31
31
  end
32
32
  # remove root key from response
33
33
  result.values[0]
@@ -1,4 +1,3 @@
1
1
  module FitbitAPI
2
- VERSION = '0.8.3'
3
- REPO_URL = 'https://github.com/zokioki/fitbit_api'
2
+ VERSION = '0.10.0'
4
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fitbit_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.3
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zoran
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-06-17 00:00:00.000000000 Z
11
+ date: 2022-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oauth2
@@ -24,34 +24,48 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: byebug
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - "~>"
32
46
  - !ruby/object:Gem::Version
33
- version: '1.10'
47
+ version: '2.3'
34
48
  type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
- version: '1.10'
54
+ version: '2.3'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
- - - "~>"
59
+ - - ">="
46
60
  - !ruby/object:Gem::Version
47
- version: '10.0'
61
+ version: 12.3.3
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
- - - "~>"
66
+ - - ">="
53
67
  - !ruby/object:Gem::Version
54
- version: '10.0'
68
+ version: 12.3.3
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rspec
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -118,8 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
118
132
  - !ruby/object:Gem::Version
119
133
  version: '0'
120
134
  requirements: []
121
- rubyforge_project:
122
- rubygems_version: 2.7.6
135
+ rubygems_version: 3.3.13
123
136
  signing_key:
124
137
  specification_version: 4
125
138
  summary: A Ruby interface to the Fitbit Web API.