strava-ruby-client 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/README.md +710 -88
  4. data/bin/strava-webhooks.rb +56 -0
  5. data/lib/strava-ruby-client.rb +37 -0
  6. data/lib/strava/api/client.rb +11 -141
  7. data/lib/strava/api/endpoints/activities.rb +120 -0
  8. data/lib/strava/api/endpoints/athletes.rb +42 -0
  9. data/lib/strava/api/endpoints/clubs.rb +75 -0
  10. data/lib/strava/api/endpoints/gears.rb +18 -0
  11. data/lib/strava/api/endpoints/routes.rb +55 -0
  12. data/lib/strava/api/endpoints/running_races.rb +30 -0
  13. data/lib/strava/api/endpoints/segment_efforts.rb +33 -0
  14. data/lib/strava/api/endpoints/segments.rb +111 -0
  15. data/lib/strava/api/endpoints/streams.rb +61 -0
  16. data/lib/strava/api/endpoints/uploads.rb +40 -0
  17. data/lib/strava/logger.rb +1 -1
  18. data/lib/strava/models/achievement.rb +9 -0
  19. data/lib/strava/models/activity.rb +11 -5
  20. data/lib/strava/models/activity_stats.rb +17 -0
  21. data/lib/strava/models/activity_total.rb +17 -0
  22. data/lib/strava/models/athlete.rb +10 -0
  23. data/lib/strava/models/club.rb +6 -0
  24. data/lib/strava/models/club_admin.rb +13 -0
  25. data/lib/strava/models/club_member.rb +16 -0
  26. data/lib/strava/models/explorer_segment.rb +19 -0
  27. data/lib/strava/models/gear.rb +5 -0
  28. data/lib/strava/models/heart_rate_zone_ranges.rb +8 -0
  29. data/lib/strava/models/kudoser.rb +10 -0
  30. data/lib/strava/models/lap.rb +3 -0
  31. data/lib/strava/models/mixins/distance.rb +18 -0
  32. data/lib/strava/models/power_zone_ranges.rb +7 -0
  33. data/lib/strava/models/route.rb +27 -0
  34. data/lib/strava/models/running_race.rb +33 -0
  35. data/lib/strava/models/segment.rb +15 -3
  36. data/lib/strava/models/segment_effort.rb +7 -2
  37. data/lib/strava/models/segment_leaderboard.rb +10 -0
  38. data/lib/strava/models/segment_leaderboard_entry.rb +12 -0
  39. data/lib/strava/models/segment_stats.rb +15 -0
  40. data/lib/strava/models/stream.rb +10 -0
  41. data/lib/strava/models/stream_set.rb +17 -0
  42. data/lib/strava/models/upload.rb +11 -0
  43. data/lib/strava/models/zone_range.rb +8 -0
  44. data/lib/strava/models/zones.rb +8 -0
  45. data/lib/strava/version.rb +1 -1
  46. data/lib/strava/web/client.rb +11 -1
  47. data/lib/strava/web/connection.rb +1 -1
  48. data/lib/strava/webhooks/client.rb +58 -0
  49. data/lib/strava/webhooks/config.rb +33 -0
  50. data/lib/strava/webhooks/models/challenge.rb +15 -0
  51. data/lib/strava/webhooks/models/event.rb +15 -0
  52. data/lib/strava/webhooks/models/subscription.rb +14 -0
  53. metadata +37 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f8e9ff7032aba29792001ccf52b8b089cd567ad747d23ed91351e81d7c2042f7
4
- data.tar.gz: d3585d62e08b3ed1217e9eb59ca0a8df476ea9a5b4bdec6834849ff3f26aef61
3
+ metadata.gz: 9a1b298638723dd74bcfe279fcaacb537f5026db4ecb2e92e990381b5dafded1
4
+ data.tar.gz: 7186ed4468c5d027cac4a3dec6874f27e5d19ff0067b5a84cbd004d069d22127
5
5
  SHA512:
6
- metadata.gz: 9d3eae7d155dd7acfa59da08d61cde2ccc102afb4410835c3f4618aebf0652088ab67c8039ada51042d972d0a83d8096e27f61dc23bc4207a43acd2a9bee8628
7
- data.tar.gz: 572b60ac371b105ff0912ad3643779e168bb44f838010e8ee70dd2cfa1d2855a137a6926b0cc6c67c14965cb8bb4ef85ace8156e0b8fb30ec56a443279f76d34
6
+ metadata.gz: 5355242600a38d9d72156a11b80f7e86b4c08d59696ace79db384d99819ca4358fc562e658f15f14e2bbd4c3a4ed42776fd828569972fef8da90ff396e74819e
7
+ data.tar.gz: 485676cac51d01d18071132be42ff798dc12dfa127c8b4552baa7eb2f1189dd637869320b039057d1e6295ec2c81c71b6e45b101068d6dd691943c5bf13f12d0
@@ -1,3 +1,17 @@
1
+ ### 0.3.0 (2018/12/03)
2
+
3
+ * Added webhooks support - [@dblock](https://github.com/dblock).
4
+ * Added `Strava::Api::Client#athlete_zones`, `athlete_stats` and `update_athlete` - [@dblock](https://github.com/dblock).
5
+ * Added `Strava::Api::Client#club`, `club_admins` and `club_members` - [@dblock](https://github.com/dblock).
6
+ * Added `Strava::Api::Client#gear` - [@dblock](https://github.com/dblock).
7
+ * Added `Strava::Api::Client#route`, `athlete_routes`, `export_route_gpx` and `export_route_tcx` - [@dblock](https://github.com/dblock).
8
+ * Added `Strava::Api::Client#running_race` and `running_races` - [@dblock](https://github.com/dblock).
9
+ * Added `Strava::Api::Client#segment_effort` and `segment_efforts` - [@dblock](https://github.com/dblock).
10
+ * Added `Strava::Api::Client#explore_segments`, `segment_leaderboard`, `starred_segments`, `segment` and `star_segment` - [@dblock](https://github.com/dblock).
11
+ * Added `Strava::Api::Client#activity_streams`, `segment_effort_streams` and `segment_streams` - [@dblock](https://github.com/dblock).
12
+ * Added `Strava::Api::Client#create_upload` and `upload` - [@dblock](https://github.com/dblock).
13
+ * [#9](https://github.com/dblock/strava-ruby-client/issues/9): All methods that take `id` can take it directly or via options hash - [@dblock](https://github.com/dblock).
14
+
1
15
  ### 0.2.0 (2018/11/27)
2
16
 
3
17
  * Added `Strava::Api::Client#activity` with segments, photos, similar activities, trends, laps and gear - [@dblock](https://github.com/dblock).
data/README.md CHANGED
@@ -4,32 +4,68 @@ Strava Ruby Client
4
4
  [![Gem Version](https://badge.fury.io/rb/strava-ruby-client.svg)](https://badge.fury.io/rb/strava-ruby-client)
5
5
  [![Build Status](https://travis-ci.org/dblock/strava-ruby-client.svg?branch=master)](https://travis-ci.org/dblock/strava-ruby-client)
6
6
 
7
- A newer Ruby client for the [Strava API v3](https://developers.strava.com).
7
+ A complete Ruby client for the [Strava API v3](https://developers.strava.com).
8
8
 
9
- Unlike [strava-api-v3](https://github.com/jaredholdcroft/strava-api-v3) provides complete OAuth refresh token flow support, a richer first class interface to Strava models, natively supports pagination and implements more consistent error handling.
9
+ Unlike other clients, including [strava-api-v3](https://github.com/jaredholdcroft/strava-api-v3), provides complete OAuth refresh token flow support, webhooks support, a richer first class interface to Strava models, conversion helpers for distance, time and elevation, natively supports pagination, implements more consistent error handling and is built with thorough test coverage using actual Strava data.
10
10
 
11
- # Table of Contents
11
+ ## Table of Contents
12
12
 
13
13
  - [Installation](#installation)
14
14
  - [Usage](#usage)
15
- - [API](#api)
16
- - [Athlete](#athlete)
17
- - [Athlete Activities](#athlete-activities)
18
- - [Athlete Clubs](#athlete-clubs)
19
- - [Club Activities](#club-activities)
20
- - [Create Activity](#create-activity)
21
- - [Update Activity](#update-activity)
15
+ - [Activities](#activities)
16
+ - [Create an Activity](#create-an-activity)
22
17
  - [Get Activity](#get-activity)
23
- - [Activity Comments](#activity-comments)
24
- - [Activity Kudoers](#activity-kudoers)
25
- - [Activity Laps](#activity-laps)
26
- - [Activity Zones](#activity-zones)
27
- - [Pagination](#pagination)
18
+ - [List Activity Comments](#list-activity-comments)
19
+ - [List Activity Kudoers](#list-activity-kudoers)
20
+ - [List Activity Laps](#list-activity-laps)
21
+ - [List Athlete Activities](#list-athlete-activities)
22
+ - [Get Activity Zones](#get-activity-zones)
23
+ - [Update Activity](#update-activity)
24
+ - [Athletes](#athletes)
25
+ - [Get Authenticated Athlete](#get-authenticated-athlete)
26
+ - [Get Zones](#get-zones)
27
+ - [Get Athlete Stats](#get-athlete-stats)
28
+ - [Update Athlete](#update-athlete)
29
+ - [Clubs](#clubs)
30
+ - [List Club Activities](#list-club-activities)
31
+ - [List Club Administrators](#list-club-administrators)
32
+ - [Get Club](#get-club)
33
+ - [List Club Members](#list-club-members)
34
+ - [List Athlete Clubs](#list-athlete-clubs)
35
+ - [Gears](#gears)
36
+ - [Get Equipment](#get-equipment)
37
+ - [Routes](#routes)
38
+ - [Export Route GPX](#export-route-gpx)
39
+ - [Export Route TCX](#export-route-tcx)
40
+ - [Get Route](#get-route)
41
+ - [List Athlete Routes](#list-athlete-routes)
42
+ - [Running Races](#running-races)
43
+ - [Get Running Race](#get-running-race)
44
+ - [List Running Races](#list-running-races)
45
+ - [Segment Efforts](#segment-efforts)
46
+ - [List Segment Efforts](#list-segment-efforts)
47
+ - [Get Segment Effort](#get-segment-effort)
48
+ - [Segments](#segments)
49
+ - [Explore Segments](#explore-segments)
50
+ - [Get Segment Leaderboard](#get-segment-leaderboard)
51
+ - [List Starred Segments](#list-starred-segments)
52
+ - [Get Segment](#get-segment)
53
+ - [Star Segment](#star-segment)
54
+ - [Streams](#streams)
55
+ - [Get Activity Streams](#get-activity-streams)
56
+ - [Get Segment Effort Streams](#get-segment-effort-streams)
57
+ - [Get Segment Streams](#get-segment-streams)
58
+ - [Uploads](#uploads)
59
+ - [Upload Activity](#upload-activity)
60
+ - [Get Upload](#get-upload)
61
+ - [Pagination](#pagination)
28
62
  - [OAuth](#oauth)
63
+ - [Webhooks](#webhooks)
29
64
  - [Configuration](#configuration)
30
65
  - [Web Client Options](#web-client-options)
31
66
  - [API Client Options](#api-client-options)
32
67
  - [OAuth Client Options](#oauth-client-options)
68
+ - [Webhooks Client Options](#webhooks-client-options)
33
69
  - [Errors](#errors)
34
70
  - [Tools](#tools)
35
71
  - [Strava OAuth Token](#strava-oauth-token)
@@ -48,8 +84,6 @@ Run `bundle install`.
48
84
 
49
85
  ## Usage
50
86
 
51
- ### API
52
-
53
87
  Use an access token obtained from [My API Application](https://www.strava.com/settings/api) in the Strava UI, the [strava-oauth-token tool](#strava-oauth-token) or the [OAuth Workflow](#oauth) in your application.
54
88
 
55
89
  ```ruby
@@ -58,19 +92,104 @@ client = Strava::Api::Client.new(
58
92
  )
59
93
  ```
60
94
 
61
- #### Athlete
95
+ ### Activities
62
96
 
63
- Get currently logged-in athlete.
97
+ #### Create an Activity
98
+
99
+ Creates a manual activity for an athlete.
64
100
 
65
101
  ```ruby
66
- client.athlete # => Strava::Models::Athlete
102
+ activity = client.create_activity(
103
+ name: 'Afternoon Run',
104
+ type: 'Run',
105
+ start_date_local: Time.now,
106
+ elapsed_time: 1234, # in seconds
107
+ description: 'Test run.',
108
+ distance: 1000 # in meters
109
+ )
110
+
111
+ activity.name # => 'Afternoon Run'
112
+ activity.strava_url # => 'https://www.strava.com/activities/1982980795'
113
+ ```
114
+
115
+ See [Strava::Models::Activity](lib/strava/models/activity.rb) for all available properties.
116
+
117
+ #### Get Activity
118
+
119
+ Returns the given activity that is owned by the authenticated athlete.
120
+
121
+ ```ruby
122
+ activity = client.activity(1982980795)
123
+
124
+ activity.name # => 'Afternoon Run'
125
+ activity.strava_url # => 'https://www.strava.com/activities/1982980795'
126
+ ```
127
+
128
+ See [Strava::Models::Activity](lib/strava/models/activity.rb) for all available properties.
129
+
130
+ Use `map.summary_polyline` and combine with [polylines](https://github.com/joshuaclayton/polylines) to parse the activity map and to construct a Google maps URL with start and end markers.
131
+
132
+ ```ruby
133
+ map = activity.map # => Strava::Models::Map
134
+
135
+ decoded_summary_polyline = Polylines::Decoder.decode_polyline(map.summary_polyline)
136
+ start_latlng = decoded_summary_polyline[0]
137
+ end_latlng = decoded_summary_polyline[-1]
138
+
139
+ google_maps_api_key = ENV['GOOGLE_STATIC_MAPS_API_KEY']
140
+
141
+ google_image_url = "https://maps.googleapis.com/maps/api/staticmap?maptype=roadmap&path=enc:#{map.summary_polyline}&key=#{google_maps_api_key}&size=800x800&markers=color:yellow|label:S|#{start_latlng[0]},#{start_latlng[1]}&markers=color:green|label:F|#{end_latlng[0]},#{end_latlng[1]}"
142
+ ```
143
+
144
+ See [Strava::Models::Map](lib/strava/models/map.rb) for all available properties.
145
+
146
+ #### List Activity Comments
147
+
148
+ Returns the comments on the given activity.
149
+
150
+ ```ruby
151
+ comments = client.activity_comments(1982980795) # => Array[Strava::Models::Comment]
152
+
153
+ comment = comments.first # => Strava::Models::Comment
154
+
155
+ comment.text # => 'Молодчина!'
156
+ comment.athlete.username # => 'zolotov'
157
+ ```
158
+
159
+ See [Strava::Models::Comment](lib/strava/models/comment.rb) for all available properties.
160
+
161
+ #### List Activity Kudoers
162
+
163
+ Returns the athletes who kudoed an activity identified by an identifier.
164
+
165
+ ```ruby
166
+ kudoers = client.activity_kudos(1982980795) # => Array[Strava::Models::Athlete]
167
+
168
+ kodoer = kudoers.first # => Strava::Models::Athlete
169
+
170
+ kudoer.username # => 'zolotov'
67
171
  ```
68
172
 
69
173
  See [Strava::Models::Athlete](lib/strava/models/athlete.rb) for all available properties.
70
174
 
71
- #### Athlete Activities
175
+ #### List Activity Laps
176
+
177
+ Returns the laps of an activity identified by an identifier.
178
+
179
+ ```ruby
180
+ laps = client.activity_laps(1982980795) # => Array[Strava::Models::Lap]
181
+
182
+ lap = laps.first # => Strava::Models::Lap
183
+
184
+ lap.name # => 'Lap 1'
185
+ ```
186
+
187
+ See [Strava::Models::Lap](lib/strava/models/lap.rb) for all available properties.
188
+
72
189
 
73
- Get currently logged-in athlete activities.
190
+ #### List Athlete Activities
191
+
192
+ Returns the currently logged-in athlete's activities.
74
193
 
75
194
  ```ruby
76
195
  activities = client.athlete_activities # => Array[Strava::Models::Activity]
@@ -93,14 +212,167 @@ activity.total_elevation_gain_in_feet_s # => '888.8ft'
93
212
 
94
213
  See [Strava::Models::Activity](lib/strava/models/activity.rb), [Strava::Models::Mixins::Distance](lib/strava/models/mixins/distance.rb), [Strava::Models::Mixins::Elevation](lib/strava/models/mixins/elevation.rb) and [Strava::Models::Mixins::Time](lib/strava/models/mixins/time.rb) for all available properties.
95
214
 
96
- #### Athlete Clubs
215
+ #### Get Activity Zones
216
+
217
+ Returns the zones of a given activity.
218
+
219
+ ```ruby
220
+ zones = client.activity_zones(1982980795) # => Array[Strava::Models::ActivityZone]
221
+
222
+ zone = zones.first # => Strava::Models::ActivityZone
223
+ zones.type # => 'heartrate'
224
+
225
+ distribution_buckets = activity_zone.distribution_buckets # => Array[Strava::Models::TimedZoneRange]
226
+
227
+ distribution_bucket = distribution_buckets.first # => Strava::Models::TimedZoneRange
228
+
229
+ distribution_bucket.min # => 0
230
+ distribution_bucket.max # => 123
231
+ distribution_bucket.time # => 20
232
+ ```
233
+
234
+ See [Strava::Models::ActivityZone](lib/strava/models/activity_zone.rb) and [Strava::Models::TimedZoneRange](lib/strava/models/timed_zone_range.rb) for all available properties.
235
+
236
+ #### Update Activity
237
+
238
+ Update an activity.
239
+
240
+ ```ruby
241
+ activity = client.update_activity(
242
+ id: 1982980795,
243
+ name: 'Afternoon Run (Updated)',
244
+ type: 'Run',
245
+ description: 'It was cold.'
246
+ )
247
+
248
+ activity.name # => 'Afternoon Run (Updated)'
249
+ activity.strava_url # => 'https://www.strava.com/activities/1982980795'
250
+ ```
251
+
252
+ ### Athletes
253
+
254
+ #### Get Authenticated Athlete
97
255
 
98
- Get currently logged-in athlete clubs.
256
+ Returns the currently authenticated athlete.
257
+
258
+ ```ruby
259
+ client.athlete # => Strava::Models::Athlete
260
+ ```
261
+
262
+ See [Strava::Models::Athlete](lib/strava/models/athlete.rb) for all available properties.
263
+
264
+ #### Get Zones
265
+
266
+ Returns the the authenticated athlete's heart rate and power zones.
267
+
268
+ ```ruby
269
+ athlete_zones = client.athlete_zones # => Strava::Models::Zones
270
+
271
+ heart_rate = athlete_zones.heart_rate # => Strava::Models::HeartRateZoneRanges
272
+ heart_rate.custom_zone # => false
273
+
274
+ zone = heart_rate.zones.first # => Strava::Models::ZoneRange
275
+ zone.min # => 0
276
+ zone.max # => 123
277
+ ```
278
+
279
+ See [Strava::Models::Zones](lib/strava/models/zones.rb), [Strava::Models::HeartRateZoneRanges](lib/strava/models/heart_rate_zone_ranges.rb), [Strava::Models::PowerZoneRanges](lib/strava/models/power_zone_ranges.rb) and [Strava::Models::ZoneRange](lib/strava/models/zone_range.rb) for all available properties.
280
+
281
+ #### Get Athlete Stats
282
+
283
+ Returns the activity stats of an athlete.
284
+
285
+ ```ruby
286
+ athlete_stats = client.athlete_stats(26462176) # => Strava::Models::ActivityStats
287
+
288
+ recent_run_totals = athlete_stats.recent_ride_totals # => Strava::Models::ActivityTotal
289
+
290
+ recent_run_totals.count # => 7
291
+ recent_run_totals.distance # => 78049.90087890625
292
+ recent_run_totals.distance_s # => '78.05km'
293
+ recent_run_totals.moving_time # => 25647
294
+ recent_run_totals.moving_time_in_hours_s # => '7h7m27s'
295
+ recent_run_totals.elapsed_time # => 26952
296
+ recent_run_totals.elapsed_time_in_hours_s # => '7h29m12s'
297
+ recent_run_totals.elevation_gain # => 595.4644241333008
298
+ recent_run_totals.total_elevation_gain_s # => '595.5m'
299
+ recent_run_totals.achievement_count # => 19
300
+ ```
301
+
302
+ See [Strava::Models::ActivityStats](lib/strava/models/activity_stats.rb) and [Strava::Models::ActivityTotal](lib/strava/models/activity_total.rb) for all available properties.
303
+
304
+ #### Update Athlete
305
+
306
+ Update the currently authenticated athlete.
307
+
308
+ ```ruby
309
+ athlete = client.update_athlete(weight: 90.1) # => Strava::Models::Athlete
310
+ ```
311
+
312
+ See [Strava::Models::Athlete](lib/strava/models/athlete.rb) for all available returned properties.
313
+
314
+ ### Clubs
315
+
316
+ #### List Club Activities
317
+
318
+ Retrieve recent activities from members of a specific club.
319
+
320
+ ```ruby
321
+ activities = client.club_activities(108605) # => Array[Strava::Models::Activity]
322
+
323
+ activity = activities.first # => Strava::Models::Activity
324
+
325
+ activity.name # => 'Afternoon Run'
326
+ ```
327
+
328
+ See [Strava::Models::Activity](lib/strava/models/activity.rb) for all available properties. Note that Strava does not return activity or athlete ID via this API.
329
+
330
+ #### List Club Administrators
331
+
332
+ Returns a list of the administrators of a given club.
333
+
334
+ ```ruby
335
+ admins = client.club_admins(108605) # => Array[Strava::Models::ClubAdmin]
336
+
337
+ admin = admins.first # => Strava::Models::ClubAdmin
338
+ admin.name # => 'Peter Ciaccia'
339
+ ```
340
+
341
+ See [Strava::Models::ClubAdmin](lib/strava/models/club_admin.rb) for all available properties.
342
+
343
+ #### Get Club
344
+
345
+ Returns a given club using its identifier.
346
+
347
+ ```ruby
348
+ club = client.club(108605) # => Strava::Models::Club
349
+
350
+ club.name # => 'NYRR'
351
+ ```
352
+
353
+ See [Strava::Models::Club](lib/strava/models/club.rb) for all available properties.
354
+
355
+ #### List Club Members
356
+
357
+ Returns a list of the members of a given club.
358
+
359
+ ```ruby
360
+ members = client.club_members(108605) # => Array[Strava::Models::ClubMember]
361
+
362
+ member = members.first # => Strava::Models::ClubMember
363
+ member.name # => 'Peter Ciaccia'
364
+ ```
365
+
366
+ See [Strava::Models::ClubMember](lib/strava/models/club_member.rb) for all available properties.
367
+
368
+ #### List Athlete Clubs
369
+
370
+ Returns a list of the clubs whose membership includes the authenticated athlete.
99
371
 
100
372
  ```ruby
101
373
  clubs = client.athlete_clubs # => Array[Strava::Models::Club]
102
374
 
103
- club = clubs.first # => Strava::Models::Activity
375
+ club = clubs.first # => Strava::Models::Club
104
376
 
105
377
  activity.name # => 'NYRR'
106
378
  activity.strava_url # => 'https://www.strava.com/clubs/nyrr'
@@ -108,127 +380,377 @@ activity.strava_url # => 'https://www.strava.com/clubs/nyrr'
108
380
 
109
381
  See [Strava::Models::Club](lib/strava/models/club.rb) for all available properties.
110
382
 
111
- #### Club Activities
383
+ ### Gears
384
+
385
+ #### Get Equipment
112
386
 
113
- Get club activities.
387
+ Returns an equipment using its identifier.
114
388
 
115
389
  ```ruby
116
- activities = client.club_activities(id: 108605) # => Array[Strava::Models::Activity]
390
+ gear = client.gear('g3844087') # => Strava::Models::Gear
391
+
392
+ gear.id # => 'g3844087'
393
+ gear.name # => 'Adidas Supernova ST'
394
+ gear.distance # => 380939.0
395
+ gear.distance_s # => '380.94km'
396
+ gear.brand_name # => 'Adidas'
397
+ gear.model_name # => 'Supernova'
398
+ gear.description # => 'My NYC TCS Marathon 2018 shoes.'
399
+ ```
117
400
 
118
- activity = activities.first # => Strava::Models::Activity
401
+ See [Strava::Models::Gear](lib/strava/models/gear.rb) for all available properties.
119
402
 
120
- activity.name # => 'Afternoon Run'
403
+ ### Routes
404
+
405
+ #### Export Route GPX
406
+
407
+ Returns [GPS Exchange Format](https://en.wikipedia.org/wiki/GPS_Exchange_Format) (GPX) data of the route. Combine with [multi_xml](https://github.com/sferik/multi_xml) or [gpx](https://github.com/dougfales/gpx) to parse it.
408
+
409
+ ```ruby
410
+ data = client.export_route_gpx(16341573) # => String
411
+
412
+ require 'multi_xml'
413
+ xml = MultiXml.parse(data) # => parsed GPX
414
+
415
+ require 'gpx'
416
+ gpx = GPX::GPXFile.new(gpx_data: data) # => GPX::GPXFile
417
+
418
+ gpx.name # => 'Lower Manhattan Loop'
419
+ gpx.description # => 'My usual long run when I am too lazy to go to Central Park.'
420
+ gpx.tracks # => Array[GPX::Track]
121
421
  ```
122
422
 
123
- See [Strava::Models::Activity](lib/strava/models/activity.rb) for all available properties. Note that Strava does not return activity or athlete ID via this API.
423
+ #### Export Route TCX
424
+
425
+ Returns a [Training Center XML](https://en.wikipedia.org/wiki/Training_Center_XML) (TCX) data of the route. Combine with [multi_xml](https://github.com/sferik/multi_xml) to parse it.
426
+
427
+ ```ruby
428
+ data = client.export_route_tcx(16341573) # => String
124
429
 
125
- #### Create Activity
430
+ require 'multi_xml'
431
+ xml = MultiXml.parse(data) # => parsed TCX
432
+ ```
433
+
434
+ #### Get Route
126
435
 
127
- Create an activity.
436
+ Returns a route using its identifier.
128
437
 
129
438
  ```ruby
130
- activity = client.create_activity(
131
- name: 'Afternoon Run',
132
- type: 'Run',
133
- start_date_local: Time.now,
134
- elapsed_time: 1234, # in seconds
135
- description: 'Test run.',
136
- distance: 1000 # in meters
137
- )
439
+ route = client.route(16341573) # => Strava::Models::Route
138
440
 
139
- activity.name # => 'Afternoon Run'
140
- activity.strava_url # => 'https://www.strava.com/activities/1982980795'
441
+ route.name # => 'Lower Manhattan Loop'
442
+ route.description # => 'My usual long run when I am too lazy to go to Central Park.'
141
443
  ```
142
444
 
143
- #### Update Activity
445
+ See [Strava::Models::Route](lib/strava/models/route.rb) for all available properties.
144
446
 
145
- Update an activity.
447
+ #### List Athlete Routes
448
+
449
+ Returns a list of the routes by athlete ID.
146
450
 
147
451
  ```ruby
148
- activity = client.update_activity(
149
- id: 1982980795,
150
- name: 'Afternoon Run (Updated)',
151
- type: 'Run',
152
- description: 'It was cold.'
153
- )
452
+ routes = client.athlete_routes(26462176) # => Array[Strava::Models::Route]
154
453
 
155
- activity.name # => 'Afternoon Run (Updated)'
156
- activity.strava_url # => 'https://www.strava.com/activities/1982980795'
454
+ route = routes.first # => Strava::Models::Route
455
+
456
+ route.name # => 'Lower Manhattan Loop'
457
+ route.description # => 'My usual long run when I am too lazy to go to Central Park.'
458
+ route.moving_time_in_hours_s # => '1h21m5s'
157
459
  ```
158
460
 
159
- #### Get Activity
461
+ See [Strava::Models::Route](lib/strava/models/route.rb) for all available properties.
462
+
463
+ ### Running Races
160
464
 
161
- Get a detailed activity by ID, including description, photos, gear, splits, segments and laps.
465
+ #### Get Running Race
466
+
467
+ Returns a running race for a given identifier.
162
468
 
163
469
  ```ruby
164
- activity = client.activity(id: 1982980795)
470
+ running_race = client.running_race(1577) # => Strava::Models::RunningRace
471
+
472
+ running_race.name # => 'Walt Disney World Marathon 10k'
473
+ running_race.distance # => 10_000.0
474
+ running_race.distance_s # => '10km'
475
+ running_race.city # => 'Orlando'
476
+ running_race.state # => 'FL'
477
+ running_race.country # => 'United States'
478
+ running_race.strava_url # => 'https://www.strava.com/running-races/2018-walt-disney-world-marathon-10k'
479
+ running_race.website_url # => 'https://www.rundisney.com/disneyworld-marathon/'
480
+ ```
165
481
 
166
- activity.name # => 'Afternoon Run'
167
- activity.strava_url # => 'https://www.strava.com/activities/1982980795'
482
+ See [Strava::Models::RunningRace](lib/strava/models/running_race.rb) for all available properties.
483
+
484
+ #### List Running Races
485
+
486
+ Returns a list running races based on a set of search criteria.
487
+
488
+ ```ruby
489
+ running_races = client.running_races
490
+
491
+ running_race = running_races.first # => Strava::Models::RunningRace
492
+
493
+ running_race.name # => 'Walt Disney World Half Marathon'
494
+ running_race.distance # => 21_097.0
495
+ running_race.measurement_preference # => 'feet'
496
+ running_race.distance_s # => '13.11mi'
168
497
  ```
169
498
 
170
- #### Activity Comments
499
+ See [Strava::Models::RunningRace](lib/strava/models/running_race.rb) for all available properties.
500
+
501
+ ### Segment Efforts
171
502
 
172
- Get activity comments.
503
+ #### List Segment Efforts
504
+
505
+ Returns a set of the authenticated athlete's segment efforts for a given segment.
173
506
 
174
507
  ```ruby
175
- comments = client.activity_comments(id: 1982980795) # => Array[Strava::Models::Comment]
508
+ segment_efforts = client.segment_efforts(1109718)
176
509
 
177
- comment = comments.first # => Strava::Models::Comment
510
+ segment_effort = segment_efforts.first # => Strava::Models::SegmentEffort
178
511
 
179
- comment.text # => 'Молодчина!'
180
- comment.athlete.username # => 'zolotov'
512
+ segment_effort.name # => 'E 14th St Climb'
513
+ segment_effort.activity # => Strava::Models::Activity
514
+ segment_effort.athlete # => Strava::Models::Athlete
515
+ segment_effort.elapsed_time # => 116
516
+ segment_effort.distance # => 398.6
517
+ segment_effort.distance_s # => '0.4km'
518
+ segment_effort.average_heartrate # => 152.2
519
+ segment_effort.max_heartrate # => 158.0
520
+
521
+ segment_effort.achievements # => Enumerable
522
+
523
+ achievement = segment_effort.achievements.first # => Strava::Models::Achievement
524
+ achievement.rank # => 1
525
+ achievement.type # => 'pr'
526
+ achievement.type_id # => 3
181
527
  ```
182
528
 
183
- See [Strava::Models::Comment](lib/strava/models/comment.rb) for all available properties.
529
+ See [Strava::Models::SegmentEffort](lib/strava/models/segment_effort.rb) and [Strava::Models::Achievement](lib/strava/models/achievement.rb) for all available properties.
184
530
 
185
- #### Activity Kudoers
531
+ #### Get Segment Effort
186
532
 
187
- Get activity kodoers.
533
+ Returns a segment effort from an activity that is owned by the authenticated athlete.
188
534
 
189
535
  ```ruby
190
- kudoers = client.activity_kudos(id: 1982980795) # => Array[Strava::Models::Athlete]
536
+ segment_effort = client.segment_effort(41494197089) # => Strava::Models::SegmentEffort
191
537
 
192
- kodoer = kudoers.first # => Strava::Models::Athlete
538
+ segment_effort.name # => 'E 14th St Climb'
539
+ segment_effort.activity # => Strava::Models::Activity
540
+ segment_effort.elapsed_time # => 116
193
541
 
194
- kudoer.username # => 'zolotov'
542
+ segment_stats = segment_effort.athlete_segment_stats # => Strava::Models::SegmentStats
543
+ segment_stats.pr_elapsed_time # => 116
544
+ segment_stats.elapsed_time_in_hours_s # => '1m56s'
545
+ segment_stats.pr_date # => Date
546
+ segment_stats.effort_count # => 3
195
547
  ```
196
548
 
197
- #### Activity Laps
549
+ See [Strava::Models::SegmentEffort](lib/strava/models/segment_effort.rb) and [Strava::Models::SegmentStats](lib/strava/models/segment_stats.rb) for all available properties.
550
+
551
+ ### Segments
552
+
553
+ #### Explore Segments
198
554
 
199
- Get activity laps.
555
+ Returns the top 10 segments matching a specified query.
200
556
 
201
557
  ```ruby
202
- laps = client.activity_laps(id: 1982980795) # => Array[Strava::Models::Lap]
558
+ segments = client.explore_segments(bounds: [36.372975, -94.220234, 36.415949, -94.183670], activity_type: 'running')
559
+
560
+ segment = segments.first # => Strava::Models::ExplorerSegment
561
+ segment.name # => 'Compton Gardens hill'
562
+ segment.avg_grade # => 4.6
563
+ segment.start_latlng # => [36.377702, -94.207242]
564
+ segment.end_latlng # => [36.375948, -94.207689]
565
+ segment.elev_difference # => 9.6
566
+ ```
203
567
 
204
- lap = laps.first # => Strava::Models::Lap
568
+ See [Strava::Models::ExplorerSegment](lib/strava/models/explorer_segment.rb) for all available properties.
205
569
 
206
- lap.name # => 'Lap 1'
570
+ #### Get Segment Leaderboard
571
+
572
+ Returns the specified segment leaderboard.
573
+
574
+ ```ruby
575
+ segment_leaderboard = client.segment_leaderboard(1109718) # => Strava::Models::SegmentLeaderboard
576
+
577
+ segment_leaderboard.effort_count # => 204
578
+ segment_leaderboard.entry_count # => 204
579
+ segment_leaderboard.kom_type # => 'kom'
580
+ segment_leaderboard.entries # => Enumerable
581
+
582
+ entry = segment_leaderboard.entries.first # => Strava::Models::SegmentLeaderboardEntry
583
+ entry.athlete_name # => 'Etan B.'
584
+ entry.moving_time_in_hours_s # => '1m32s'
585
+ entry.start_date # => Time
586
+ entry.start_date_local # => Time
587
+ entry.rank # => 1
207
588
  ```
208
589
 
209
- See [Strava::Models::Lap](lib/strava/models/lap.rb) for all available properties.
590
+ This API supports pagination through the entire segment leaderboard but wraps entries into a [Strava::Models::SegmentLeaderboard](lib/strava/models/segment_leaderboard.rb) object.
591
+
592
+ ```ruby
593
+ client.segment_leaderboard(1109718) do |row|
594
+ row # => Strava::Models::SegmentLeaderboard
595
+ row.entries # => Array[Strava::Models::SegmentLeaderboardEntry]
596
+ end
597
+ ```
210
598
 
211
- #### Activity Zones
599
+ See [Strava::Models::SegmentLeaderboard](lib/strava/models/segment_leaderboard.rb) and [Strava::Models::SegmentLeaderboardEntry](lib/strava/models/segment_leaderboard_entry.rb) for all available properties.
212
600
 
213
- Get activity zones.
601
+ #### List Starred Segments
602
+
603
+ List of the authenticated athlete's starred segments.
214
604
 
215
605
  ```ruby
216
- zones = client.activity_zones(id: 1982980795) # => Array[Strava::Models::ActivityZone]
606
+ segments = client.starred_segments
217
607
 
218
- zone = zones.first # => Strava::Models::ActivityZone
219
- zones.type # => 'heartrate'
608
+ segment = segments.first # => Strava::Models::Segment
220
609
 
221
- distribution_bucket = activity_zone.distribution_buckets.first # => Strava::Models::TimedZoneRange
222
- distribution_bucket.min # => 0
223
- distribution_bucket.max # => 123
224
- distribution_bucket.time # => 20
610
+ segment.pr_time # => 256
611
+ segment.elapsed_time_in_hours_s # => '4m16s'
612
+ segment.starred_date # => Time
613
+ segment.athlete_pr_effort # => Strava::Models::SegmentEffort
225
614
  ```
226
615
 
227
- See [Strava::Models::ActivityZone](lib/strava/models/activity_zone.rb) and [Strava::Models::TimedZoneRange](lib/strava/models/timed_zone_range.rb) for all available properties.
616
+ See [Strava::Models::Segment](lib/strava/models/segment.rb) and [Strava::Models::SegmentEffort](lib/strava/models/segment_effort.rb) for all available properties.
617
+
618
+ #### Get Segment
619
+
620
+ Returns the specified segment.
621
+
622
+ ```ruby
623
+ segment = client.segment(1109718) # => Strava::Models::Segment
624
+
625
+ segment.name # => 'E 14th St Climb'
626
+ segment.city # => 'New York'
627
+ segment.state # => 'NY'
628
+ segment.country # => 'United States'
629
+ segment.map # => Strava::Models::Map
630
+ segment.effort_count # => 750
631
+ segment.athlete_count # => 206
632
+ segment.star_count # => 1
633
+ segment.athlete_segment_stats # => Strava::Models::SegmentStats
634
+ ```
635
+
636
+ See [Strava::Models::Segment](lib/strava/models/segment.rb) for all available properties.
637
+
638
+ #### Star Segment
639
+
640
+ Stars/unstars the given segment for the authenticated athlete.
641
+
642
+ ```ruby
643
+ segment = client.star_segment(50272077110, starred: true) # => Strava::Models::Segment
644
+
645
+ segment.name # => 'E 14th St Climb'
646
+ segment.starred # => true
647
+ ```
648
+
649
+ See [Strava::Models::Segment](lib/strava/models/segment.rb) for all available properties.
650
+
651
+ ### Streams
652
+
653
+ Stream APIs can return various streams by key(s).
654
+
655
+ ```ruby
656
+ streams = client.segment_streams(1109718, keys: %w[distance latlng altitude]) # => Strava::Models::StrewamSet
657
+
658
+ streams.distance # => Strava::Models::Stream
659
+ streams.latlng # => Strava::Models::Stream
660
+ streams.altitude # => Strava::Models::Stream
661
+ ```
662
+
663
+ #### Get Activity Streams
664
+
665
+ Returns the given activity's streams.
666
+
667
+ ```ruby
668
+ streams = client.activity_streams(1946417534) # => Strava::Models::StreamSet
228
669
 
229
- #### Pagination
670
+ distance = streams.distance # => Strava::Models::Stream
671
+ distance.original_size # => 13_129
672
+ distance.resolution # => 'high'
673
+ distance.series_type # => 'distance'
674
+ distance.data # => Array[Float]
675
+ ```
676
+
677
+ See [Strava::Models::StreamSet](lib/strava/models/stream_set.rb) and [Strava::Models::Stream](lib/strava/models/stream.rb) for all available properties.
678
+
679
+ #### Get Segment Effort Streams
680
+
681
+ Returns a set of streams for a segment effort completed by the authenticated athlete.
682
+
683
+ ```ruby
684
+ streams = client.segment_effort_streams(41494197089)
230
685
 
231
- Some Strava APIs, including [athlete_activities](#athlete-activities) support pagination when supplying an optional `page` and `per_page` parameter. By default the client retrieves one page of data, which Strava currently defaults to 30 items. You can paginate through more data by supplying a block and an optional `per_page` parameter. The underlying implementation makes page-sized calls and increments the `page` argument.
686
+ distance = streams.distance # => Strava::Models::Stream
687
+ distance.original_size # => 117
688
+ distance.resolution # => 'high'
689
+ distance.series_type # => 'distance'
690
+ distance.data # => Array[Float]
691
+ ```
692
+
693
+ See [Strava::Models::StreamSet](lib/strava/models/stream_set.rb) and [Strava::Models::Stream](lib/strava/models/stream.rb) for all available properties.
694
+
695
+ #### Get Segment Streams
696
+
697
+ Returns the given segment's streams.
698
+
699
+ ```ruby
700
+ streams = client.segment_streams(1109718) # => Strava::Models::StreamSet
701
+
702
+ distance = streams.distance # => Strava::Models::Stream
703
+ distance.original_size # => 32
704
+ distance.resolution # => 'high'
705
+ distance.series_type # => 'distance'
706
+ distance.data # => Array[Float]
707
+ ```
708
+
709
+ See [Strava::Models::StreamSet](lib/strava/models/stream_set.rb) and [Strava::Models::Stream](lib/strava/models/stream.rb) for all available properties.
710
+
711
+ ### Uploads
712
+
713
+ #### Upload Activity
714
+
715
+ Uploads a new data file to create an activity from.
716
+
717
+ ```ruby
718
+ upload = client.create_upload(
719
+ file: Faraday::UploadIO.new('17611540601.tcx', 'application/tcx+xml'),
720
+ name: 'Uploaded Activity',
721
+ description: 'Uploaded by strava-ruby-client.',
722
+ data_type: 'tcx',
723
+ external_id: 'strava-ruby-client-upload-1'
724
+ ) # => Strava::Models::Upload
725
+
726
+ upload.id # => 2136460097
727
+ upload.external_id # => 'strava-ruby-client-upload-1.tcx'
728
+ upload.error # => nil
729
+ upload.status # => 'Your activity is still being processed.'
730
+ upload.activity_id # => nil
731
+ ```
732
+
733
+ See [Strava::Models::Upload](lib/strava/models/upload.rb) for all available properties.
734
+
735
+ #### Get Upload
736
+
737
+ Returns an upload for a given identifier.
738
+
739
+ ```ruby
740
+ upload = client.upload(2136460097) # => Strava::Models::Upload
741
+
742
+ upload.id # => 2_136_460_097
743
+ upload.external_id # => 'strava-ruby-client-upload-1.tcx'
744
+ upload.error # => nil
745
+ upload.status # => 'Your activity is ready.'
746
+ upload.activity_id # => 1_998_557_443
747
+ ```
748
+
749
+ See [Strava::Models::Upload](lib/strava/models/upload.rb) for all available properties.
750
+
751
+ ### Pagination
752
+
753
+ Some Strava APIs, including [athlete-activities](#list-athlete-activities) support pagination when supplying an optional `page` and `per_page` parameter. By default the client retrieves one page of data, which Strava currently defaults to 30 items. You can paginate through more data by supplying a block and an optional `per_page` parameter. The underlying implementation makes page-sized calls and increments the `page` argument.
232
754
 
233
755
  ```ruby
234
756
  client.athlete_activities(per_page: 30) do |activity|
@@ -283,6 +805,79 @@ response.refresh_token # new refresh token
283
805
  response.expires_at # new timestamp when the access token expires
284
806
  ```
285
807
 
808
+ ### Webhooks
809
+
810
+ Strava provides a [Webhook Event API](https://developers.strava.com/docs/webhooks/) that requires special access obtained by emailing [developers@strava.com](mailto:developers@strava.com).
811
+
812
+ A complete example that handles subscription creation, deletion and handling can be found in [strava-webhooks.rb](bin/strava-webhooks.rb). Run `strava-webhooks` to see current registrations, `strava-webhooks handle` to run an HTTP server that handles both challenges and event data, `strava-webhooks create [url]` to create a new subscription and `strava-webhooks delete [id]` to delete it.
813
+
814
+ Before creating a webhook subscription you must implement and run an HTTP server that will handle a `GET` challenge at the subscription URL.
815
+
816
+ ```ruby
817
+ challenge = Strava::Webhooks::Models::Challenge.new(request.query)
818
+ raise 'Bad Request' unless challenge.verify_token == 'token'
819
+ response.content_type = 'application/json'
820
+ response.body = challenge.response.to_json
821
+ ```
822
+
823
+ See [Strava::Webhooks::Models::Challenge](lib/strava/webhooks/models/challenge.rb) for details.
824
+
825
+ An existing subscription must be handled in the same HTTP server's `POST` request to the subscription URL.
826
+
827
+ ```ruby
828
+ event = Strava::Webhooks::Models::Event.new(JSON.parse(request.body))
829
+
830
+ event # => Strava::Webhooks::Models::Event
831
+ event.object_type # => 'activity'
832
+ event.object_id # => 1991813808
833
+ event.aspect_type # => 'update'
834
+ event.updates # => { 'type' => 'Walk' }
835
+ event.owner_id # => 29323238
836
+ event.subscription_id # => 131302
837
+ event.event_time # => DateTime
838
+ ```
839
+
840
+ See [Strava::Webhooks::Models::Event](lib/strava/webhooks/models/event.rb) for details.
841
+
842
+ Subscriptions can be created, listed and deleted.
843
+
844
+ Create a client.
845
+
846
+ ```ruby
847
+ client = Strava::Webhooks::Client.new(
848
+ client_id: "12345",
849
+ client_secret: "12345678987654321"
850
+ )
851
+ ```
852
+
853
+ Create a subscription.
854
+
855
+ ```ruby
856
+ subscription = client.create_push_subscription(callback_url: 'http://example.com/strava', verify_token: 'token')
857
+
858
+ subscription # => Strava::Webhooks::Models::Subscription
859
+ subscription.id # => 131300
860
+ subscription.callback_url # => 'http://example.com/strava'
861
+ ```
862
+
863
+ See [Strava::Webhooks::Models::Subscription](lib/strava/webhooks/models/subscription.rb) for details.
864
+
865
+ List an existing subscription. Strava seems to only allow one.
866
+
867
+ ```ruby
868
+ subscriptions = client.push_subscriptions
869
+
870
+ subscription = subscriptions.first # => Strava::Webhooks::Models::Subscription
871
+ subscription.id # => 131300
872
+ subscription.callback_url # => 'http://example.com/strava'
873
+ ```
874
+
875
+ Delete an existing subscription.
876
+
877
+ ```ruby
878
+ client.delete_push_subscription(131300) # => nil
879
+ ```
880
+
286
881
  ## Configuration
287
882
 
288
883
  ### Web Client Options
@@ -358,6 +953,33 @@ client_id | Application client ID.
358
953
  client_secret | Application client secret.
359
954
  endpoint | Defaults to `https://www.strava.com/oauth`.
360
955
 
956
+ ### Webhooks Client Options
957
+
958
+ The Webhooks client inherits web client options and provides additional application configuration. These can be configured globally or for a client instance.
959
+
960
+ ```ruby
961
+ Strava::Webhooks.configure do |config|
962
+ config.client_id = "..." # Strava client ID
963
+ config.client_secret = "..." # Strava client secret
964
+ end
965
+ ```
966
+
967
+ ```ruby
968
+ client = Strava::Webhooks::Client.new(
969
+ client_id: "...",
970
+ client_secret: "...",
971
+ user_agent: "..."
972
+ )
973
+ ```
974
+
975
+ The following settings are supported.
976
+
977
+ setting | description
978
+ --------------------|------------
979
+ client_id | Application client ID.
980
+ client_secret | Application client secret.
981
+ endpoint | Defaults to `https://api.strava.com/api/v3`.
982
+
361
983
  ## Errors
362
984
 
363
985
  All errors that return HTTP codes 400-600 result in either `Faraday::Error::ResourceNotFound`, `Faraday::Error::ConnectionFailed` or [Strava::Errors::Fault](lib/strava/errors/fault.rb) exceptions.