strava-ruby-client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +15 -0
  5. data/.rubocop_todo.yml +47 -0
  6. data/.travis.yml +9 -0
  7. data/CHANGELOG.md +3 -0
  8. data/CONTRIBUTING.md +125 -0
  9. data/Dangerfile +2 -0
  10. data/Gemfile +15 -0
  11. data/LICENSE.md +22 -0
  12. data/README.md +231 -0
  13. data/RELEASING.md +61 -0
  14. data/Rakefile +17 -0
  15. data/bin/oauth-token.rb +28 -0
  16. data/lib/strava-ruby-client.rb +30 -0
  17. data/lib/strava/api/client.rb +38 -0
  18. data/lib/strava/api/config.rb +31 -0
  19. data/lib/strava/errors/fault.rb +13 -0
  20. data/lib/strava/logger.rb +13 -0
  21. data/lib/strava/models/activity.rb +57 -0
  22. data/lib/strava/models/athlete.rb +25 -0
  23. data/lib/strava/models/map.rb +9 -0
  24. data/lib/strava/models/model.rb +5 -0
  25. data/lib/strava/models/token.rb +12 -0
  26. data/lib/strava/oauth/client.rb +70 -0
  27. data/lib/strava/oauth/config.rb +33 -0
  28. data/lib/strava/version.rb +3 -0
  29. data/lib/strava/web/client.rb +31 -0
  30. data/lib/strava/web/config.rb +41 -0
  31. data/lib/strava/web/connection.rb +35 -0
  32. data/lib/strava/web/raise_error.rb +29 -0
  33. data/lib/strava/web/request.rb +38 -0
  34. data/spec/fixtures/strava/client_athlete.yml +59 -0
  35. data/spec/fixtures/strava/client_athlete_activities.yml +121 -0
  36. data/spec/fixtures/strava/oauth_token_authorization_code.yml +53 -0
  37. data/spec/fixtures/strava/oauth_token_invalid_client.yml +50 -0
  38. data/spec/fixtures/strava/oauth_token_invalid_code.yml +50 -0
  39. data/spec/fixtures/strava/oauth_token_refresh_token.yml +52 -0
  40. data/spec/spec_helper.rb +10 -0
  41. data/spec/strava/api/client_spec.rb +36 -0
  42. data/spec/strava/api/config_spec.rb +22 -0
  43. data/spec/strava/oauth/client_spec.rb +120 -0
  44. data/spec/strava/oauth/config_spec.rb +24 -0
  45. data/spec/strava/version_spec.rb +7 -0
  46. data/spec/strava/web/client_spec.rb +9 -0
  47. data/spec/strava/web/config_spec.rb +4 -0
  48. data/spec/support/shared/it_behaves_like_web_client.rb +131 -0
  49. data/spec/support/vcr.rb +12 -0
  50. data/strava-ruby-client.gemspec +21 -0
  51. metadata +150 -0
@@ -0,0 +1,3 @@
1
+ module Strava
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,31 @@
1
+ module Strava
2
+ module Web
3
+ class Client
4
+ include Web::Connection
5
+ include Web::Request
6
+
7
+ attr_accessor(*Config::ATTRIBUTES)
8
+
9
+ def initialize(options = {})
10
+ Strava::Web::Config::ATTRIBUTES.each do |key|
11
+ send("#{key}=", options[key] || Strava::Web.config.send(key))
12
+ end
13
+ @logger ||= Strava::Logger.default
14
+ end
15
+
16
+ def endpoint
17
+ raise NotImplementedError
18
+ end
19
+
20
+ class << self
21
+ def configure
22
+ block_given? ? yield(Config) : Config
23
+ end
24
+
25
+ def config
26
+ Config
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,41 @@
1
+ module Strava
2
+ module Web
3
+ module Config
4
+ extend self
5
+
6
+ ATTRIBUTES = %i[
7
+ proxy
8
+ user_agent
9
+ ca_path
10
+ ca_file
11
+ logger
12
+ timeout
13
+ open_timeout
14
+ ].freeze
15
+
16
+ attr_accessor(*Config::ATTRIBUTES)
17
+
18
+ def reset
19
+ self.user_agent = "Strava Ruby Client/#{Strava::VERSION}"
20
+ self.ca_path = defined?(OpenSSL) ? OpenSSL::X509::DEFAULT_CERT_DIR : nil
21
+ self.ca_file = defined?(OpenSSL) ? OpenSSL::X509::DEFAULT_CERT_FILE : nil
22
+ self.proxy = nil
23
+ self.logger = nil
24
+ self.timeout = nil
25
+ self.open_timeout = nil
26
+ end
27
+ end
28
+
29
+ class << self
30
+ def configure
31
+ block_given? ? yield(Config) : Config
32
+ end
33
+
34
+ def config
35
+ Config
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ Strava::Web::Config.reset
@@ -0,0 +1,35 @@
1
+ module Strava
2
+ module Web
3
+ module Connection
4
+ private
5
+
6
+ def headers
7
+ {}
8
+ end
9
+
10
+ def connection
11
+ options = {
12
+ headers: headers.merge('Accept' => 'application/json; charset=utf-8')
13
+ }
14
+
15
+ options[:headers]['User-Agent'] = user_agent if user_agent
16
+ options[:proxy] = proxy if proxy
17
+ options[:ssl] = { ca_path: ca_path, ca_file: ca_file } if ca_path || ca_file
18
+
19
+ request_options = {}
20
+ request_options[:timeout] = timeout if timeout
21
+ request_options[:open_timeout] = open_timeout if open_timeout
22
+ options[:request] = request_options if request_options.any?
23
+
24
+ ::Faraday::Connection.new(endpoint, options) do |connection|
25
+ connection.use ::Faraday::Request::Multipart
26
+ connection.use ::Faraday::Request::UrlEncoded
27
+ connection.use ::Strava::Web::Response::RaiseError
28
+ connection.use ::FaradayMiddleware::ParseJson
29
+ connection.response :logger, logger if logger
30
+ connection.adapter ::Faraday.default_adapter
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,29 @@
1
+ module Strava
2
+ module Web
3
+ module Response
4
+ class RaiseError < ::Faraday::Response::Middleware
5
+ ClientErrorStatuses = (400...600).freeze
6
+
7
+ def on_complete(env)
8
+ case env[:status]
9
+ when 404
10
+ raise Faraday::Error::ResourceNotFound, response_values(env)
11
+ when 407
12
+ # mimic the behavior that we get with proxy requests with HTTPS
13
+ raise Faraday::Error::ConnectionFailed, %(407 "Proxy Authentication Required ")
14
+ when ClientErrorStatuses
15
+ raise Strava::Errors::Fault, response_values(env)
16
+ end
17
+ end
18
+
19
+ def response_values(env)
20
+ {
21
+ status: env.status,
22
+ headers: env.response_headers,
23
+ body: env.body
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,38 @@
1
+ module Strava
2
+ module Web
3
+ module Request
4
+ def get(path, options = {})
5
+ request(:get, path, options)
6
+ end
7
+
8
+ def post(path, options = {})
9
+ request(:post, path, options)
10
+ end
11
+
12
+ def put(path, options = {})
13
+ request(:put, path, options)
14
+ end
15
+
16
+ def delete(path, options = {})
17
+ request(:delete, path, options)
18
+ end
19
+
20
+ private
21
+
22
+ def request(method, path, options)
23
+ path = [endpoint, path].join('/')
24
+ response = connection.send(method) do |request|
25
+ case method
26
+ when :get, :delete
27
+ request.url(path, options)
28
+ when :post, :put
29
+ request.path = path
30
+ request.body = options unless options.empty?
31
+ end
32
+ request.options.merge!(options.delete(:request)) if options.key?(:request)
33
+ end
34
+ response.body
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,59 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://www.strava.com/api/v3/athlete
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Authorization:
11
+ - Bearer access-token
12
+ Accept:
13
+ - application/json; charset=utf-8
14
+ User-Agent:
15
+ - Strava Ruby Client/0.1.0
16
+ Accept-Encoding:
17
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Date:
24
+ - Thu, 22 Nov 2018 22:16:54 GMT
25
+ Content-Type:
26
+ - application/json; charset=UTF-8
27
+ Transfer-Encoding:
28
+ - chunked
29
+ Connection:
30
+ - keep-alive
31
+ Cache-Control:
32
+ - max-age=0, private, must-revalidate
33
+ Via:
34
+ - 1.1 linkerd
35
+ Status:
36
+ - 200 OK
37
+ X-Ratelimit-Limit:
38
+ - '600,30000'
39
+ X-Request-Id:
40
+ - 170d3e7a-ba23-4ffa-a6de-c262ed082294
41
+ Etag:
42
+ - W/"aec66d60b5541f026b3bdd4becd4a394"
43
+ X-Ratelimit-Usage:
44
+ - '1,11466'
45
+ X-Frame-Options:
46
+ - DENY
47
+ X-Content-Type-Options:
48
+ - nosniff
49
+ Vary:
50
+ - Origin
51
+ X-Xss-Protection:
52
+ - 1; mode=block
53
+ body:
54
+ encoding: ASCII-8BIT
55
+ string: '{"id":26462176,"username":"dblockdotorg","resource_state":2,"firstname":"Daniel","lastname":"Block","city":"New
56
+ York","state":"NY","country":"","sex":"M","premium":true,"summit":true,"created_at":"2017-11-28T19:05:35Z","updated_at":"2018-11-19T01:44:15Z","badge_type_id":1,"profile_medium":"https://dgalywyr863hv.cloudfront.net/pictures/athletes/26462176/7677115/2/medium.jpg","profile":"https://dgalywyr863hv.cloudfront.net/pictures/athletes/26462176/7677115/2/large.jpg","friend":null,"follower":null,"email":"dblock@example.com"}'
57
+ http_version:
58
+ recorded_at: Thu, 22 Nov 2018 22:16:54 GMT
59
+ recorded_with: VCR 4.0.0
@@ -0,0 +1,121 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://www.strava.com/api/v3/athlete/activities
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Authorization:
11
+ - Bearer access-token
12
+ Accept:
13
+ - application/json; charset=utf-8
14
+ User-Agent:
15
+ - Strava Ruby Client/0.1.0
16
+ Accept-Encoding:
17
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Date:
24
+ - Fri, 23 Nov 2018 15:36:57 GMT
25
+ Content-Type:
26
+ - application/json; charset=UTF-8
27
+ Transfer-Encoding:
28
+ - chunked
29
+ Connection:
30
+ - keep-alive
31
+ Cache-Control:
32
+ - max-age=0, private, must-revalidate
33
+ Via:
34
+ - 1.1 linkerd
35
+ Status:
36
+ - 200 OK
37
+ X-Ratelimit-Limit:
38
+ - '600,30000'
39
+ X-Request-Id:
40
+ - 8155569b-2c01-43aa-b741-23c5f3be6ee2
41
+ Etag:
42
+ - W/"9c40510fd0b8764b541b21e329a7ac51"
43
+ X-Ratelimit-Usage:
44
+ - '89,8130'
45
+ X-Frame-Options:
46
+ - DENY
47
+ X-Content-Type-Options:
48
+ - nosniff
49
+ Strava-Athlete-Upload-Version:
50
+ - fd960260952cfb09201c3ff8bbcfde30
51
+ Vary:
52
+ - Origin
53
+ X-Xss-Protection:
54
+ - 1; mode=block
55
+ body:
56
+ encoding: UTF-8
57
+ string: '[{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Central
58
+ Park with L","distance":9681.1,"moving_time":3150,"elapsed_time":3150,"total_elevation_gain":105.5,"type":"Run","workout_type":0,"id":1972463847,"external_id":"18177667835.tcx","upload_id":2109231946,"start_date":"2018-11-18T16:30:45Z","start_date_local":"2018-11-18T11:30:45Z","timezone":"(GMT-05:00)
59
+ America/New_York","utc_offset":-18000.0,"start_latlng":[40.77,-73.98],"end_latlng":[40.77,-73.98],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.77,"start_longitude":-73.98,"achievement_count":9,"kudos_count":7,"comment_count":0,"athlete_count":3,"photo_count":0,"map":{"id":"a1972463847","summary_polyline":"_iywFn`pbMmBgB\\uKgNwK`@aNwKgOeEcCmHd@eGwH}OeFqQ{NuDoQiYoTmLsBqHfAmLuKwLaAwFsG}@lAfAhEg@pCeMcFcG~I}@dFr@tCpDr@`CgFdJSpDxIxD~CfC`IpLtHnCdFnInBfFhMlFpC|K}@hK`L`IdApRhXvPlBzAvFvPrI|JvNxAVlIuF~At@","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":3.073,"max_speed":4.1,"has_heartrate":true,"average_heartrate":157.2,"max_heartrate":188.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":38.1,"elev_low":5.7,"pr_count":3,"total_photo_count":0,"has_kudoed":false,"suffer_score":93},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Central
60
+ Park with L","distance":8449.8,"moving_time":3194,"elapsed_time":3201,"total_elevation_gain":74.2,"type":"Run","workout_type":0,"id":1961422138,"external_id":"18058932742.tcx","upload_id":2097849306,"start_date":"2018-11-12T22:12:47Z","start_date_local":"2018-11-12T17:12:47Z","timezone":"(GMT-05:00)
61
+ America/New_York","utc_offset":-18000.0,"start_latlng":[40.77,-73.98],"end_latlng":[40.77,-73.98],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.77,"start_longitude":-73.98,"achievement_count":1,"kudos_count":3,"comment_count":0,"athlete_count":2,"photo_count":0,"map":{"id":"a1961422138","summary_polyline":"skywF`apbMd@yNmNeLXoNmMmPaD}AwG|@_HiJwQuEePmNkBaRo\\}TiSb@gNiMeLHfCnNaCjInChJvKzGnEzH~HnAxFrNjFfBvJy@|L|LrG`@|SlYxOtAbBpFdQ`JfLrNnEk@xC{E","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":2.646,"max_speed":3.4,"has_heartrate":true,"average_heartrate":137.3,"max_heartrate":167.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":37.3,"elev_low":15.0,"pr_count":0,"total_photo_count":0,"has_kudoed":false,"suffer_score":27},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Back
62
+ in Central Park with Ilan","distance":5045.1,"moving_time":2064,"elapsed_time":2946,"total_elevation_gain":44.2,"type":"Run","workout_type":0,"id":1956926998,"external_id":"18015760096.tcx","upload_id":2093222580,"start_date":"2018-11-10T16:03:24Z","start_date_local":"2018-11-10T11:03:24Z","timezone":"(GMT-05:00)
63
+ America/New_York","utc_offset":-18000.0,"start_latlng":[40.77,-73.98],"end_latlng":[40.78,-73.97],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.77,"start_longitude":-73.98,"achievement_count":0,"kudos_count":5,"comment_count":0,"athlete_count":1,"photo_count":0,"map":{"id":"a1956926998","summary_polyline":"gsywFtdpbM~E}GdAiIeOuL\\kN_LcOkFqBoFjAiH{JiT}BgC}G}ATOkCwJsL^iFoA{BqXuQgNa@p@xJeArH`CbIj@lQo@x@`AbCtGxDjKqAbLbMbIbAvKzM","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":2.444,"max_speed":4.5,"has_heartrate":true,"average_heartrate":123.4,"max_heartrate":164.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":37.4,"elev_low":15.0,"pr_count":0,"total_photo_count":3,"has_kudoed":false,"suffer_score":8},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"TCS
64
+ NYC Marathon 2018","distance":42268.1,"moving_time":13085,"elapsed_time":13333,"total_elevation_gain":270.9,"type":"Run","workout_type":1,"id":1946417534,"external_id":"17900304917.tcx","upload_id":2082412856,"start_date":"2018-11-04T14:53:46Z","start_date_local":"2018-11-04T09:53:46Z","timezone":"(GMT-05:00)
65
+ America/New_York","utc_offset":-18000.0,"start_latlng":[40.6,-74.06],"end_latlng":[40.77,-73.98],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.6,"start_longitude":-74.06,"achievement_count":7,"kudos_count":33,"comment_count":8,"athlete_count":301,"photo_count":0,"map":{"id":"a1946417534","summary_polyline":"aayvFjz_cMyu@_lDwS}VyNgI{@bCzEfGeDvIkATe{Bwq@uoDcxDqKiFmQ{Qka@g[so@i`@oIqCiI~CmPgsCucAhK{g@rj@cSaAm[yP_x@a~@se@dS_B}Nim@jG_a@wIeDrLgWqFkCoCzCgWIsGhCyL_@wIaDsFsFF_CgCiGdIiVfz@wLd[aOrj@jAsEeC}EsGcBoKqIiEe@{G}HwKeCu@iDwYmPqL_LwYeMaE}EeJHsCaM{wByuA}Qm_@oGeFwCy@wDhJsK_DeDvGmLoCEtCvFtG{J|QgDr\\zu@fh@{@`JdLtHbDwEfC\\tlB`nA|D~FjM`JhBzRdTvP~LbCvFdIlMNbP|TrGbBdJgBfG|Dw@xFoQhf@qAc@","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":3.17,"max_speed":5.6,"has_heartrate":true,"average_heartrate":170.1,"max_heartrate":187.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":54.7,"elev_low":-8.1,"pr_count":0,"total_photo_count":9,"has_kudoed":false,"suffer_score":656},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Dash
66
+ to TFK Breakfast","distance":3161.2,"moving_time":959,"elapsed_time":973,"total_elevation_gain":28.7,"type":"Run","workout_type":0,"id":1943331548,"external_id":"17878873978.tcx","upload_id":2079194427,"start_date":"2018-11-03T13:20:58Z","start_date_local":"2018-11-03T09:20:58Z","timezone":"(GMT-05:00)
67
+ America/New_York","utc_offset":-14400.0,"start_latlng":[40.77,-73.98],"end_latlng":[40.78,-73.98],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.77,"start_longitude":-73.98,"achievement_count":2,"kudos_count":2,"comment_count":0,"athlete_count":2,"photo_count":0,"map":{"id":"a1943331548","summary_polyline":"wkywFp_pbMpIiNmBdMmFdGw@hElK{[e@q@wBvDwPgMoAoCpAiJgReTwD`@eBdXoC`E_@rEyBr@","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":3.296,"max_speed":3.9,"has_heartrate":true,"average_heartrate":159.6,"max_heartrate":174.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":25.7,"elev_low":14.6,"pr_count":0,"total_photo_count":2,"has_kudoed":false,"suffer_score":30},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Shake
68
+ It with Rob","distance":5476.4,"moving_time":1917,"elapsed_time":1935,"total_elevation_gain":44.3,"type":"Run","workout_type":0,"id":1942082742,"external_id":"17855361691.tcx","upload_id":2077904428,"start_date":"2018-11-02T21:16:19Z","start_date_local":"2018-11-02T17:16:19Z","timezone":"(GMT-05:00)
69
+ America/New_York","utc_offset":-14400.0,"start_latlng":[40.77,-73.98],"end_latlng":[40.77,-73.98],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.77,"start_longitude":-73.98,"achievement_count":0,"kudos_count":3,"comment_count":0,"athlete_count":1,"photo_count":0,"map":{"id":"a1942082742","summary_polyline":"ywywFhdpbMaKeLvDbGnHbE|EkFlBoKeNgK{@mEfAmIeMmPkMc@{G{ImIiAe@~@zFrEuFQwL|P~MtQbJObC|AxIkKvOzEbEfGdLbIgArJnBjB","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":2.857,"max_speed":3.8,"has_heartrate":true,"average_heartrate":146.4,"max_heartrate":169.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":32.9,"elev_low":14.8,"pr_count":0,"total_photo_count":0,"has_kudoed":false,"suffer_score":30},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Final
70
+ TFK Practice","distance":3968.2,"moving_time":1278,"elapsed_time":1414,"total_elevation_gain":27.8,"type":"Run","workout_type":0,"id":1938547049,"external_id":"17835067430.tcx","upload_id":2074260838,"start_date":"2018-10-31T22:53:18Z","start_date_local":"2018-10-31T18:53:18Z","timezone":"(GMT-05:00)
71
+ America/New_York","utc_offset":-14400.0,"start_latlng":[40.77,-73.98],"end_latlng":[40.77,-73.98],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.77,"start_longitude":-73.98,"achievement_count":0,"kudos_count":6,"comment_count":0,"athlete_count":23,"photo_count":0,"map":{"id":"a1938547049","summary_polyline":"{kzwFptobMbAmAjJfMtGxDjGoGfBsHi@iCiNgKbAaJu@sE_GgEyB}EsFiCuBzBrAqBhC`@`MnMtBlEqAnF\\nDhO~MuDbOyDxBeT}R","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":3.105,"max_speed":4.1,"has_heartrate":true,"average_heartrate":147.3,"max_heartrate":169.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":26.4,"elev_low":14.8,"pr_count":0,"total_photo_count":1,"has_kudoed":false,"suffer_score":25},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Run
72
+ with Ilan","distance":3225.6,"moving_time":1442,"elapsed_time":1489,"total_elevation_gain":0.0,"type":"Run","workout_type":0,"id":1932465273,"external_id":"17769555662.tcx","upload_id":2067987957,"start_date":"2018-10-28T14:48:45Z","start_date_local":"2018-10-28T10:48:45Z","timezone":"(GMT-05:00)
73
+ America/New_York","utc_offset":-14400.0,"start_latlng":[40.75,-74.0],"end_latlng":[40.75,-74.0],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.75,"start_longitude":-74.0,"achievement_count":6,"kudos_count":4,"comment_count":0,"athlete_count":1,"photo_count":0,"map":{"id":"a1932465273","summary_polyline":"i_vwFfhtbMg@hFnAgDuAUCbEnAeD{@m@m@nCf@x@|@qCq@cAo@jDh@`@z@yC}@q@q@fDt@l@t@_Dq@c@g@~DfB}Cg@y@w@dEvBuCiA[y@dCj@~@fAwDm@]iAdDn@fAxAqD{@k@aAnCp@hAz@yE{BdCnAx@XeE","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":2.237,"max_speed":3.5,"has_heartrate":true,"average_heartrate":168.2,"max_heartrate":194.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":5.5,"elev_low":4.2,"pr_count":2,"total_photo_count":1,"has_kudoed":false,"suffer_score":66},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Last
74
+ Long Run before Death at the TCS 2018 Marathon","distance":27650.7,"moving_time":8510,"elapsed_time":8619,"total_elevation_gain":160.1,"type":"Run","workout_type":0,"id":1917054256,"external_id":"8277592741780812.gpx","upload_id":2052043593,"start_date":"2018-10-20T16:39:20Z","start_date_local":"2018-10-20T12:39:20Z","timezone":"(GMT-05:00)
75
+ America/New_York","utc_offset":-14400.0,"start_latlng":[40.73,-73.98],"end_latlng":[40.74,-73.99],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.73,"start_longitude":-73.98,"achievement_count":18,"kudos_count":6,"comment_count":1,"athlete_count":1,"photo_count":0,"map":{"id":"a1917054256","summary_polyline":"o~rwFn{pbMIaCbD_KwDuEbNs^u@yBhTyNty@vInr@lTzDxFXzC_@xz@tChG`@lGoBbBdDrP^zMdF~EjCbHWnCtBbGh@jJpJ`IjCdIbMdRo@vGjAnBW`G_DFf@r@oC~GuDB{BzGMjIwAj@_BdGsAR_GmGmDdCcMmAcFmB}@_EaE}A{EhG}CkBwTaAh@gSiS_Ei}@sIwn@iAgOaGae@{GwFxDsNNiKaDgs@ge@wFyKaSwOqx@o[mCcGeb@mSsh@g\\f@}CyHmE`NtDCcKvC_Er@qIdD{EnM{a@|MdGtBKtBgDxMjOtMTrCtHdLdF`NzOrEnAvGmJVuFgMmKsA}FvByBtOQq@_Ed@pBjThDVzApJ~C`@jBxCWfDdLfIA~ZzVfIlDrAhDtAa@hKnJzj@~[","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":3.249,"max_speed":18.9,"has_heartrate":true,"average_heartrate":162.7,"max_heartrate":175.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":32.1,"elev_low":-19.7,"pr_count":4,"total_photo_count":0,"has_kudoed":false,"suffer_score":313},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Staten
76
+ Island Half","distance":21116.8,"moving_time":5985,"elapsed_time":5985,"total_elevation_gain":92.8,"type":"Run","workout_type":1,"id":1904418549,"external_id":"17474884162.tcx","upload_id":2038985443,"start_date":"2018-10-14T12:07:56Z","start_date_local":"2018-10-14T08:07:56Z","timezone":"(GMT-05:00)
77
+ America/New_York","utc_offset":-14400.0,"start_latlng":[40.64,-74.08],"end_latlng":[40.64,-74.08],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.64,"start_longitude":-74.08,"achievement_count":3,"kudos_count":19,"comment_count":0,"athlete_count":221,"photo_count":0,"map":{"id":"a1904418549","summary_polyline":"al`wFt{bcMxO`CtRoEbU|HdLuAdTgJfd@s^bfAcs@~KnXtE|BhWgFxW_QdFlEnMjBnPlUn]`ZxUvWdTfWj]bs@~MnOeK{K{`@ex@gi@um@{^m[}PoUyMoBgFcEeNpJcNki@qGpE`AhL{CvNuG_T~RiOeDaM{LhC_MpNX`Q{j@ja@aDe@aG}MuBzA}EtOcQxYuDsAgVvSaZfE}Kf@uK}CkBiDmKl@oBdBvAlIq@r@","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":3.528,"max_speed":4.6,"has_heartrate":true,"average_heartrate":175.1,"max_heartrate":183.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":35.3,"elev_low":1.8,"pr_count":0,"total_photo_count":3,"has_kudoed":false,"suffer_score":364},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Unfinished
78
+ Business","distance":3011.0,"moving_time":941,"elapsed_time":970,"total_elevation_gain":8.5,"type":"Run","workout_type":0,"id":1888473597,"external_id":"17273735646.tcx","upload_id":2022360788,"start_date":"2018-10-06T19:54:06Z","start_date_local":"2018-10-06T15:54:06Z","timezone":"(GMT-05:00)
79
+ America/New_York","utc_offset":-14400.0,"start_latlng":[40.71,-74.0],"end_latlng":[40.73,-73.99],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.71,"start_longitude":-74.0,"achievement_count":0,"kudos_count":3,"comment_count":0,"athlete_count":1,"photo_count":0,"map":{"id":"a1888473597","summary_polyline":"_ynwFr|tbM{IuDiCx@kB_EeBTqZkZku@}f@OcE{Am@t@wAa@kCaVwMX_D","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":3.2,"max_speed":4.8,"has_heartrate":true,"average_heartrate":163.9,"max_heartrate":177.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":13.4,"elev_low":4.7,"pr_count":0,"total_photo_count":0,"has_kudoed":false,"suffer_score":36},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Two
80
+ Bridges","distance":13363.4,"moving_time":4395,"elapsed_time":4421,"total_elevation_gain":84.4,"type":"Run","workout_type":0,"id":1888474044,"external_id":"17273735647.tcx","upload_id":2022361157,"start_date":"2018-10-06T18:30:44Z","start_date_local":"2018-10-06T14:30:44Z","timezone":"(GMT-05:00)
81
+ America/New_York","utc_offset":-14400.0,"start_latlng":[40.73,-73.99],"end_latlng":[40.71,-74.0],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.73,"start_longitude":-73.99,"achievement_count":4,"kudos_count":5,"comment_count":1,"athlete_count":1,"photo_count":0,"map":{"id":"a1888474044","summary_polyline":"ovrwF|hqbM`FoS~BJxCqEvMga@aQiL|IwJl\\p@r`@~FfRjFdA|EnI|C}Rj{@rSihArT{_A`B^]bGrQ`CrBbF~KkCtCvBtUuZhMn@{@mOzYoDrE~z@oBxyA_LnDcAbZyVPgo@p|@","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":3.041,"max_speed":4.2,"has_heartrate":true,"average_heartrate":165.8,"max_heartrate":182.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":37.6,"elev_low":-0.8,"pr_count":2,"total_photo_count":0,"has_kudoed":false,"suffer_score":182},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Slow
82
+ Burn","distance":8020.2,"moving_time":2839,"elapsed_time":3388,"total_elevation_gain":55.1,"type":"Run","workout_type":0,"id":1884816921,"external_id":"1612944491207599.gpx","upload_id":2018591009,"start_date":"2018-10-05T00:14:28Z","start_date_local":"2018-10-04T20:14:28Z","timezone":"(GMT-05:00)
83
+ America/New_York","utc_offset":-14400.0,"start_latlng":[40.76,-73.98],"end_latlng":[40.77,-73.97],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.76,"start_longitude":-73.98,"achievement_count":0,"kudos_count":5,"comment_count":0,"athlete_count":1,"photo_count":0,"map":{"id":"a1884816921","summary_polyline":"{bwwFt~pbMuhCy~@cTe[iIqA_DoFg^}JuCsHvB_MYqWlBiAvJdAdZ|P~BnOlBlDfRtNpMvBvFhJzCKnCgDhK`OjDjBfDnHhEjArJwB","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":2.825,"max_speed":4.7,"has_heartrate":true,"average_heartrate":166.4,"max_heartrate":177.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":37.0,"elev_low":14.9,"pr_count":0,"total_photo_count":0,"has_kudoed":false,"suffer_score":121},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Bronx
84
+ 10M","distance":16227.6,"moving_time":4515,"elapsed_time":4515,"total_elevation_gain":93.5,"type":"Run","workout_type":1,"id":1875527224,"external_id":"17179620217.tcx","upload_id":2009044310,"start_date":"2018-09-30T12:17:07Z","start_date_local":"2018-09-30T08:17:07Z","timezone":"(GMT-05:00)
85
+ America/New_York","utc_offset":-14400.0,"start_latlng":[40.83,-73.92],"end_latlng":[40.83,-73.93],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.83,"start_longitude":-73.92,"achievement_count":3,"kudos_count":18,"comment_count":2,"athlete_count":296,"photo_count":0,"map":{"id":"a1875527224","summary_polyline":"ewexF|rdbMaOsIcd@}`@mc@iM{Um[ye@y^sq@k\\og@k^wQ{Yyi@kR{JbL}C~AeA_A|KmF`CmDxUcKbb@oIir@tTrg@rShNpVbk@|a@ns@`^jf@d_@`KdRhE~Czc@~Lbg@`d@p`@xQhBnBoErV","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":3.594,"max_speed":4.8,"has_heartrate":true,"average_heartrate":174.5,"max_heartrate":187.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":47.0,"elev_low":6.5,"pr_count":0,"total_photo_count":3,"has_kudoed":false,"suffer_score":260},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Revenge
86
+ on Central Park","distance":22732.6,"moving_time":7148,"elapsed_time":7294,"total_elevation_gain":198.7,"type":"Run","workout_type":2,"id":1870132397,"external_id":"7083999924361706.gpx","upload_id":2003403893,"start_date":"2018-09-27T22:02:14Z","start_date_local":"2018-09-27T18:02:14Z","timezone":"(GMT-05:00)
87
+ America/New_York","utc_offset":-14400.0,"start_latlng":[40.73,-73.99],"end_latlng":[40.77,-73.98],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.73,"start_longitude":-73.99,"achievement_count":4,"kudos_count":9,"comment_count":0,"athlete_count":1,"photo_count":0,"map":{"id":"a1870132397","summary_polyline":"mwrwFdgqbMmDiDDoDyIkHkwColBsDrFyG`T}MjToSdB{CkAuOyTcMM_G_JaPqDqN_LmDsHKeLsVkLLkAkGmEkRl@mNuMiLm@_FgHaBlAxApFw@jCwLqGmFnHmAnI|@bC~BFtByEpKQxCrInEhCfDfKtJfFtDlGxHhApCpJzDzDtE|AnIgAvJ|KhJdAfSjY`PrAxBtGjOvHpK~MfE^pGeJSaHkMmKS{PsJqLaEgBwHTwF{HuPoEeQqOyA}Pg]aVyLaA_FrAuNaMiLl@dDnMqCbInCnJ~MhJxBnE|HbBjHjOnDtArK{@dKdLzHl@hSfYbPtApCxG|OrIdLpNrF{@~GsM","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":3.18,"max_speed":22.3,"has_heartrate":true,"average_heartrate":166.6,"max_heartrate":181.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":37.0,"elev_low":5.0,"pr_count":3,"total_photo_count":0,"has_kudoed":false,"suffer_score":310},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Back
88
+ in the Saddle","distance":15110.6,"moving_time":4417,"elapsed_time":4417,"total_elevation_gain":32.0,"type":"Run","workout_type":0,"id":1861817097,"external_id":"17028948957.tcx","upload_id":1994874138,"start_date":"2018-09-23T20:37:24Z","start_date_local":"2018-09-23T16:37:24Z","timezone":"(GMT-05:00)
89
+ America/New_York","utc_offset":-14400.0,"start_latlng":[40.73,-73.99],"end_latlng":[40.74,-73.99],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.73,"start_longitude":-73.99,"achievement_count":22,"kudos_count":8,"comment_count":0,"athlete_count":1,"photo_count":0,"map":{"id":"a1861817097","summary_polyline":"mwrwFriqbM?yAyDgDRaEdEaKuGkIdBsKnCiEzCyO|EUxMuNn_AlKjm@hS~DrGKx{@|CbJ]lGhEfi@~B|KdEtChD`NpMpPn@zKxANsBfDxK|SXjHwDbFsIj@]rM_E~Cf@tAqBfCqHqGyBhDqWcDGmF_Ce@iC?gDhEaZaD\\qRoA_B}mAeNyn@s@a\\yIt@wEaBkCvDuJBkE|CsDlJuZmAkD`AuEjCoHvBb@rKm\\aHeC","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":3.421,"max_speed":4.9,"has_heartrate":true,"average_heartrate":170.3,"max_heartrate":179.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":12.2,"elev_low":-3.6,"pr_count":16,"total_photo_count":0,"has_kudoed":false,"suffer_score":225},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"TCSNYCMarathonTraining
90
+ 18M","distance":29735.9,"moving_time":10645,"elapsed_time":11519,"total_elevation_gain":294.0,"type":"Run","workout_type":1,"id":1845907424,"external_id":"6051605617006620
91
+ (1).gpx","upload_id":1978331068,"start_date":"2018-09-16T11:01:18Z","start_date_local":"2018-09-16T07:01:18Z","timezone":"(GMT-05:00)
92
+ America/New_York","utc_offset":-14400.0,"start_latlng":[40.79,-73.96],"end_latlng":[40.79,-73.96],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.79,"start_longitude":-73.96,"achievement_count":2,"kudos_count":10,"comment_count":2,"athlete_count":86,"photo_count":0,"map":{"id":"a1845907424","summary_polyline":"km~wF`jkbMuFaAaEoG}A`BzAxFiArB_MmGmEvFeArLhCtCnEuGjJBlCfInE~CfDxJjJdFhE~GbHbAhGtNpFzB`Lw@fItK|Ix@nRfYdQdBjDrHnLnFvO~P|D_AxEwJW{F}MeK`@qM}K{O_FoBcHv@cGaJwO}DgQ}NoAmEEoIcBuCgY}QcL_BmHxAuLyLqLu@}FeHsAdArA|G}@zAiIkFmEZ{CxFo@tK|CzBvC{F~JOtN|WvKpHdEbGrHlBhIhOrP^dJdKtJzAlQfXfQxAnChHjNbHvLvN`Ed@nGsJb@wEs@kBiNwIWeFnA_D_AoFwgAqz@g@eG}ZwSuLmBaHnBiMmMkL{@yF}GkAjArAzGmAlBuHwFgEF{C`Gw@rK~CvBlDeHfJM~ChIxExDbBxHdN|I|BxEbHjAjJtQlR`@tIrKbHn@hUrYtMD|ClIzOtHnKlNlFMdFeK{@eG_NyJl@uNgFoDyB_G{FqD}Hl@}EoHsP_EwGsHqEcBsCyDe@kNuDaEoZoRqQz@aNkMoJWu@fC","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":2.581,"max_speed":5.2,"has_heartrate":true,"average_heartrate":162.4,"max_heartrate":188.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":37.0,"elev_low":6.0,"pr_count":1,"total_photo_count":1,"has_kudoed":false,"suffer_score":414},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"September
93
+ 11","distance":11510.3,"moving_time":3769,"elapsed_time":3870,"total_elevation_gain":33.3,"type":"Run","workout_type":0,"id":1835710357,"external_id":"16789586516.tcx","upload_id":1967819200,"start_date":"2018-09-11T23:28:56Z","start_date_local":"2018-09-11T19:28:56Z","timezone":"(GMT-05:00)
94
+ America/New_York","utc_offset":-14400.0,"start_latlng":[40.73,-73.98],"end_latlng":[40.74,-74.0],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.73,"start_longitude":-73.98,"achievement_count":3,"kudos_count":7,"comment_count":0,"athlete_count":1,"photo_count":0,"map":{"id":"a1835710357","summary_polyline":"yrrwFpcqbM|u@fe@xVvJ|@kBjJdDBlCzLjGg@lEpKrGt[aFbEhQnGrH~AdKzGnGvFbOhKbNhBnVgC^MtDuBbDcONkBnBiG}JmJb@wGxTOfF{HmBJ_FmFuAeAf@_@zEk]kDfA_UsqAkN_o@_B}QeFRiAoC}EbFcHzBmHE}HtEyG","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":3.054,"max_speed":3.9,"has_heartrate":true,"average_heartrate":164.3,"max_heartrate":175.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":14.9,"elev_low":-3.6,"pr_count":0,"total_photo_count":0,"has_kudoed":false,"suffer_score":147},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Sunset
95
+ in Paris","distance":16217.2,"moving_time":4956,"elapsed_time":4961,"total_elevation_gain":261.1,"type":"Run","workout_type":0,"id":1815133429,"external_id":"16590818329.tcx","upload_id":1946632754,"start_date":"2018-09-02T16:29:26Z","start_date_local":"2018-09-02T18:29:26Z","timezone":"(GMT+01:00)
96
+ Europe/Paris","utc_offset":7200.0,"start_latlng":[48.86,2.36],"end_latlng":[48.86,2.36],"location_city":null,"location_state":null,"location_country":"","start_latitude":48.86,"start_longitude":2.36,"achievement_count":2,"kudos_count":8,"comment_count":0,"athlete_count":1,"photo_count":0,"map":{"id":"a1815133429","summary_polyline":"asfiHm`lM`JnNrd@|^l@zBo@vDtLid@jDpA|FeQtDU~CvSeB`FzDkSvU{[yBxAFrCaEbLuObZsXlgAqVlf@uArTm^leBLpY`Ad@}@b@lAjgAZzAfO`BpAxQvCnE_CnDgBcBkEtAkCqGqBw@aBsL_CknBjO{z@zIgX`EcXVmJmG{C~@}QzA}OzOav@gGyCQ}BmYwXkJuN}DhG^fBxEy@xDqM{CqFmBdD","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":3.272,"max_speed":4.1,"has_heartrate":true,"average_heartrate":167.2,"max_heartrate":176.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":56.7,"elev_low":24.0,"pr_count":1,"total_photo_count":1,"has_kudoed":false,"suffer_score":219},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"#NB5thAveMile
97
+ to Jim Morrison’s Grave","distance":2714.2,"moving_time":1012,"elapsed_time":2883,"total_elevation_gain":33.4,"type":"Run","workout_type":1,"id":1814549396,"external_id":"16588927458.tcx","upload_id":1946025739,"start_date":"2018-09-01T10:01:39Z","start_date_local":"2018-09-01T12:01:39Z","timezone":"(GMT+01:00)
98
+ Europe/Paris","utc_offset":7200.0,"start_latlng":[48.86,2.36],"end_latlng":[48.86,2.39],"location_city":null,"location_state":null,"location_country":"","start_latitude":48.86,"start_longitude":2.36,"achievement_count":0,"kudos_count":5,"comment_count":0,"athlete_count":1,"photo_count":0,"map":{"id":"a1814549396","summary_polyline":"krfiHyalMu@gAxC_LlDqGvBUb@sKjBuA_Jif@pAmBmD_HlCvF~@m@_DgJpAiEoIuUH{QvB{FImH|AkFbGgCVoHfD{BaBuLdDiCtAZn@xCiC[x@mFzB^pA{Cx@rB","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3372292","from_accepted_tag":false,"average_speed":0.941,"max_speed":4.0,"has_heartrate":true,"average_heartrate":136.1,"max_heartrate":151.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":78.0,"elev_low":43.0,"pr_count":0,"total_photo_count":2,"has_kudoed":false,"suffer_score":8},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Jog
99
+ to the Louvre w/Veronica","distance":4505.8,"moving_time":1707,"elapsed_time":1859,"total_elevation_gain":105.2,"type":"Run","workout_type":0,"id":1814549379,"external_id":"16588927457.tcx","upload_id":1946025771,"start_date":"2018-08-31T09:43:12Z","start_date_local":"2018-08-31T11:43:12Z","timezone":"(GMT+01:00)
100
+ Europe/Paris","utc_offset":7200.0,"start_latlng":[48.86,2.36],"end_latlng":[48.86,2.35],"location_city":null,"location_state":null,"location_country":"","start_latitude":48.86,"start_longitude":2.36,"achievement_count":0,"kudos_count":1,"comment_count":0,"athlete_count":2,"photo_count":0,"map":{"id":"a1814549379","summary_polyline":"qrfiHg`lMnI`Mh\\l\\dHrCaCzJcDM{DdRYxCnBrAaF~Vj@|AmD`[{FcBwBfDmDvRyBaEg@}HhJgVN_FpCmIiDoFnAoO}@iGjFgV","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3372292","from_accepted_tag":false,"average_speed":2.64,"max_speed":3.6,"has_heartrate":true,"average_heartrate":134.5,"max_heartrate":155.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":57.0,"elev_low":28.0,"pr_count":0,"total_photo_count":0,"has_kudoed":false,"suffer_score":10},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Steak
101
+ au Poivre, Paris 1","distance":7420.3,"moving_time":2206,"elapsed_time":2247,"total_elevation_gain":154.6,"type":"Run","workout_type":0,"id":1807186864,"external_id":"16511462429.tcx","upload_id":1938461612,"start_date":"2018-08-30T10:00:44Z","start_date_local":"2018-08-30T12:00:44Z","timezone":"(GMT+01:00)
102
+ Europe/Paris","utc_offset":7200.0,"start_latlng":[48.86,2.36],"end_latlng":[48.86,2.36],"location_city":null,"location_state":null,"location_country":"","start_latitude":48.86,"start_longitude":2.36,"achievement_count":0,"kudos_count":5,"comment_count":0,"athlete_count":1,"photo_count":0,"map":{"id":"a1807186864","summary_polyline":"qqfiHs_lMbSfVtZjXuQfbAsDlb@eHzd@}Rn{@c@q@hOst@oDuDTuBlDiMnHsChGmr@pJ}g@zHeXaMwGgVuXoF{BgCmH}@T","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3372292","from_accepted_tag":false,"average_speed":3.364,"max_speed":4.3,"has_heartrate":true,"average_heartrate":166.7,"max_heartrate":182.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":56.8,"elev_low":26.6,"pr_count":0,"total_photo_count":2,"has_kudoed":false,"suffer_score":95},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Waking
103
+ up in Amsterdam","distance":3197.5,"moving_time":1233,"elapsed_time":1256,"total_elevation_gain":12.6,"type":"Run","workout_type":0,"id":1802519799,"external_id":"16469104195.tcx","upload_id":1933681629,"start_date":"2018-08-28T09:07:14Z","start_date_local":"2018-08-28T11:07:14Z","timezone":"(GMT+01:00)
104
+ Europe/Amsterdam","utc_offset":7200.0,"start_latlng":[52.36,4.87],"end_latlng":[52.36,4.87],"location_city":null,"location_state":null,"location_country":"","start_latitude":52.36,"start_longitude":4.87,"achievement_count":0,"kudos_count":2,"comment_count":0,"athlete_count":2,"photo_count":0,"map":{"id":"a1802519799","summary_polyline":"ysq~Hgdv\\c@eFlCmFeBoC{@mI|AiEvI~HpLlp@xBb^_DvIy@`JyEyZz@iKr@qAnA`D}DbFs@yBoHaa@","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3372292","from_accepted_tag":false,"average_speed":2.593,"max_speed":3.5,"has_heartrate":true,"average_heartrate":131.9,"max_heartrate":144.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":7.0,"elev_low":-1.9,"pr_count":0,"total_photo_count":1,"has_kudoed":false,"suffer_score":6},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Berlin
105
+ 3, Hasenheide Park with Veronica","distance":3498.5,"moving_time":1326,"elapsed_time":1393,"total_elevation_gain":26.1,"type":"Run","workout_type":0,"id":1798000742,"external_id":"16432690640.tcx","upload_id":1929039813,"start_date":"2018-08-26T08:18:47Z","start_date_local":"2018-08-26T10:18:47Z","timezone":"(GMT+01:00)
106
+ Europe/Berlin","utc_offset":7200.0,"start_latlng":[52.48,13.43],"end_latlng":[52.49,13.42],"location_city":null,"location_state":null,"location_country":"","start_latitude":52.48,"start_longitude":13.43,"achievement_count":1,"kudos_count":4,"comment_count":0,"athlete_count":2,"photo_count":0,"map":{"id":"a1798000742","summary_polyline":"e}i_Imz}pA`@t@qJhUj@|AcF~Wt@bLzEnBwAtQ|TgCrEcVE_C}UqAeClUjIrDtB}LUsKkG{@kCxLqEw@","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3372292","from_accepted_tag":false,"average_speed":2.638,"max_speed":4.0,"has_heartrate":true,"average_heartrate":132.3,"max_heartrate":149.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":55.0,"elev_low":39.0,"pr_count":0,"total_photo_count":0,"has_kudoed":false,"suffer_score":6},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Berlin
107
+ 2","distance":16244.6,"moving_time":5005,"elapsed_time":5005,"total_elevation_gain":109.8,"type":"Run","workout_type":0,"id":1795666854,"external_id":"16409473391.tcx","upload_id":1926641043,"start_date":"2018-08-25T07:34:19Z","start_date_local":"2018-08-25T09:34:19Z","timezone":"(GMT+01:00)
108
+ Europe/Berlin","utc_offset":7200.0,"start_latlng":[52.49,13.43],"end_latlng":[52.49,13.43],"location_city":null,"location_state":null,"location_country":"","start_latitude":52.49,"start_longitude":13.43,"achievement_count":3,"kudos_count":3,"comment_count":0,"athlete_count":1,"photo_count":0,"map":{"id":"a1795666854","summary_polyline":"{hj_Iqz}pAc[\\lh@?ePnm@LxNlNpJ|IjBv@`JbJu@yL|jAbRdI[z]jAvLrGxJnVnN~Ju@zH_G~@gHyA_cCcAmTkDyC}PnE~FrhDzKqC|EqHwB_wCy[pCgL{AoBtKnBnOiP|[oFgA~Hkm@qAsF|BgUqCqCcKz@yLaCqEfFeC{@[sGvA{NhMk^cJcAZsErBtC","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3372292","from_accepted_tag":false,"average_speed":3.246,"max_speed":4.1,"has_heartrate":true,"average_heartrate":167.6,"max_heartrate":177.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":56.0,"elev_low":34.0,"pr_count":1,"total_photo_count":1,"has_kudoed":false,"suffer_score":225},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Berlin
109
+ 1","distance":11723.7,"moving_time":3570,"elapsed_time":3671,"total_elevation_gain":86.6,"type":"Run","workout_type":0,"id":1791365082,"external_id":"16365739967.tcx","upload_id":1922194153,"start_date":"2018-08-23T04:49:41Z","start_date_local":"2018-08-23T06:49:41Z","timezone":"(GMT+01:00)
110
+ Europe/Berlin","utc_offset":7200.0,"start_latlng":[52.48,13.43],"end_latlng":[52.49,13.43],"location_city":null,"location_state":null,"location_country":"","start_latitude":52.48,"start_longitude":13.43,"achievement_count":0,"kudos_count":4,"comment_count":0,"athlete_count":1,"photo_count":0,"map":{"id":"a1791365082","summary_polyline":"i|i_Iaz}pAeOzi@XdSrE`E{@hQta@oDuG`j@~EfBy@g@YvG{@M@wGwBxCyGpl@mNuBo@|g@sMxATypBrB{HpHcBbEec@`UjAd@}DmA{UlWoHDmMdBkBuEws@zJ}InNmDnHmFrVyAyU`C{a@hRqUtXqMv_@dEWyKi@","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3372292","from_accepted_tag":false,"average_speed":3.284,"max_speed":4.3,"has_heartrate":true,"average_heartrate":161.5,"max_heartrate":181.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":61.1,"elev_low":39.0,"pr_count":0,"total_photo_count":1,"has_kudoed":false,"suffer_score":124},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Flight
111
+ to Berlin Warmup","distance":5827.4,"moving_time":1642,"elapsed_time":1645,"total_elevation_gain":35.2,"type":"Run","workout_type":0,"id":1787324494,"external_id":"16319438595.tcx","upload_id":1918060247,"start_date":"2018-08-21T12:03:43Z","start_date_local":"2018-08-21T08:03:43Z","timezone":"(GMT-05:00)
112
+ America/New_York","utc_offset":-14400.0,"start_latlng":[40.74,-73.98],"end_latlng":[40.74,-73.99],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.74,"start_longitude":-73.98,"achievement_count":0,"kudos_count":4,"comment_count":0,"athlete_count":1,"photo_count":0,"map":{"id":"a1787324494","summary_polyline":"ycswFtvpbMwAqAzFuLtK{`@}C{@aNtCsGcCwPCcA{HkHTa@_CyFtBuAqHiBTaAyCmPoMfUvPeDxCd@vE{FxLqCrLpFvFqAtGfBx@y@~HyEdJ`KnGyCrBa@dCfRrOdTxC","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":3.549,"max_speed":4.7,"has_heartrate":true,"average_heartrate":159.9,"max_heartrate":171.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":14.1,"elev_low":-7.5,"pr_count":0,"total_photo_count":0,"has_kudoed":false,"suffer_score":57},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"BK
113
+ Equinox Is Hottttt","distance":7129.1,"moving_time":2252,"elapsed_time":2252,"total_elevation_gain":26.6,"type":"Run","workout_type":0,"id":1775222127,"external_id":"16217957932.tcx","upload_id":1905578194,"start_date":"2018-08-15T23:50:19Z","start_date_local":"2018-08-15T19:50:19Z","timezone":"(GMT-05:00)
114
+ America/New_York","utc_offset":-14400.0,"start_latlng":[40.68,-73.92],"end_latlng":[40.72,-73.96],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.68,"start_longitude":-73.92,"achievement_count":1,"kudos_count":4,"comment_count":1,"athlete_count":1,"photo_count":0,"map":{"id":"a1775222127","summary_polyline":"yyhwFz}cbMjHftAtDtkBo@~\\{yBbZ_l@zk@uSiCsXgNmFwF","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":3.166,"max_speed":4.1,"has_heartrate":true,"average_heartrate":166.9,"max_heartrate":182.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":20.1,"elev_low":2.9,"pr_count":0,"total_photo_count":0,"has_kudoed":false,"suffer_score":100},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"TCSNYCMarathonTraining
115
+ 15M","distance":25195.7,"moving_time":7876,"elapsed_time":7879,"total_elevation_gain":162.5,"type":"Run","workout_type":1,"id":1767296035,"external_id":"16129954357.tcx","upload_id":1897398512,"start_date":"2018-08-12T11:07:02Z","start_date_local":"2018-08-12T07:07:02Z","timezone":"(GMT-05:00)
116
+ America/New_York","utc_offset":-14400.0,"start_latlng":[40.79,-73.96],"end_latlng":[40.79,-73.96],"location_city":null,"location_state":null,"location_country":"","start_latitude":40.79,"start_longitude":-73.96,"achievement_count":25,"kudos_count":9,"comment_count":0,"athlete_count":90,"photo_count":0,"map":{"id":"a1767296035","summary_polyline":"us}wFjxkbMsFqAcJwJuLs@qEyGkAjB~@zFi@zAgJmFsD`@kCxEgAbL|CnClDgH~IGvOjZbLxGtCrFlHjAbHxOjFjBnK_A`JzKnKhClRxWbPrBnDwG|A_XoCA{GcJeOuDuFkG_HwC{C}IQoJs^cVmQx@mNkMmMmAuDgGcBxAtAnFu@fCiJcGqEv@wDlQfDnDfD{GtI]fEfJ|DbC|D|KnIlE|EhHtGx@jHfOhG|BtHoAtKhL~IpAhRlXfRvCjDkGjA_XqDa@_FiIqO{DgPyLyB_EDyJiBuDc\\{SuJk@kEhBcNuLiD[","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":3.198,"max_speed":4.3,"has_heartrate":true,"average_heartrate":168.8,"max_heartrate":187.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":37.0,"elev_low":6.0,"pr_count":10,"total_photo_count":2,"has_kudoed":false,"suffer_score":373},{"resource_state":2,"athlete":{"id":26462176,"resource_state":1},"name":"Martha’s
117
+ Vineyard 2","distance":10265.6,"moving_time":3193,"elapsed_time":3200,"total_elevation_gain":27.0,"type":"Run","workout_type":0,"id":1758181483,"external_id":"16048698588.tcx","upload_id":1887763550,"start_date":"2018-08-08T10:58:49Z","start_date_local":"2018-08-08T06:58:49Z","timezone":"(GMT-05:00)
118
+ America/New_York","utc_offset":-14400.0,"start_latlng":[41.38,-70.53],"end_latlng":[41.38,-70.53],"location_city":null,"location_state":null,"location_country":"","start_latitude":41.38,"start_longitude":-70.53,"achievement_count":0,"kudos_count":4,"comment_count":0,"athlete_count":1,"photo_count":0,"map":{"id":"a1758181483","summary_polyline":"c`q{F`knmLeBeUmIaY}JeRzAsC`b@eOpYaPfUjCti@qGzs@i\\pCz@|EtO`NtC}MyCqEyOiCaAus@r[yk@`HcKwC_HTkXnOy_@tMgFlDhJ~OlLj_@v@dR","resource_state":2},"trainer":false,"commute":false,"manual":false,"private":false,"visibility":"everyone","flagged":false,"gear_id":"g3423618","from_accepted_tag":false,"average_speed":3.215,"max_speed":4.0,"has_heartrate":true,"average_heartrate":166.5,"max_heartrate":179.0,"heartrate_opt_out":false,"display_hide_heartrate_option":false,"elev_high":10.0,"elev_low":-0.3,"pr_count":0,"total_photo_count":2,"has_kudoed":false,"suffer_score":138}]'
119
+ http_version:
120
+ recorded_at: Fri, 23 Nov 2018 15:36:57 GMT
121
+ recorded_with: VCR 4.0.0
@@ -0,0 +1,53 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://www.strava.com/oauth/token
6
+ body:
7
+ encoding: UTF-8
8
+ string: client_id=12345&client_secret=secret&code=deadbeef585a6edb1f00309d3e1e0fec74973fb0&grant_type=authorization_code&token
9
+ headers:
10
+ Accept:
11
+ - application/json; charset=utf-8
12
+ User-Agent:
13
+ - Strava Ruby Client/0.1.0
14
+ Content-Type:
15
+ - application/x-www-form-urlencoded
16
+ Accept-Encoding:
17
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Date:
24
+ - Thu, 22 Nov 2018 20:29:59 GMT
25
+ Content-Type:
26
+ - application/json; charset=UTF-8
27
+ Transfer-Encoding:
28
+ - chunked
29
+ Connection:
30
+ - keep-alive
31
+ Cache-Control:
32
+ - max-age=0, private, must-revalidate
33
+ Via:
34
+ - 1.1 linkerd
35
+ Status:
36
+ - 200 OK
37
+ X-Request-Id:
38
+ - bd4a7ae2-d65e-4f94-bc0f-f01095da297c
39
+ Etag:
40
+ - W/"46fcdd016c51d7e953514dfcdb131e2c"
41
+ X-Frame-Options:
42
+ - SAMEORIGIN,DENY
43
+ X-Content-Type-Options:
44
+ - nosniff
45
+ X-Xss-Protection:
46
+ - 1; mode=block
47
+ body:
48
+ encoding: ASCII-8BIT
49
+ string: '{"token_type":"Bearer","expires_at":1542940199,"expires_in":21600,"refresh_token":"deadbeef9d64b225d0c50db4854a8d6c8757702d","access_token":"deadbeefd7ab44704fb2a146bd98c7a349a2b43d","athlete":{"id":12342176,"username":"dblockdotorg","resource_state":2,"firstname":"Daniel","lastname":"Block","city":"New
50
+ York","state":"NY","country":"","sex":"M","premium":true,"summit":true,"created_at":"2017-11-28T19:05:35Z","updated_at":"2018-11-19T01:44:15Z","badge_type_id":1,"profile_medium":"https://dgalywyr863hv.cloudfront.net/pictures/athletes/26462176/7677115/2/medium.jpg","profile":"https://dgalywyr863hv.cloudfront.net/pictures/athletes/26462176/7677115/2/large.jpg","friend":null,"follower":null,"email":"dblock@example.com"}}'
51
+ http_version:
52
+ recorded_at: Thu, 22 Nov 2018 20:29:59 GMT
53
+ recorded_with: VCR 4.0.0
@@ -0,0 +1,50 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://www.strava.com/oauth/token
6
+ body:
7
+ encoding: UTF-8
8
+ string: client_id=client-id&client_secret=client-secret&code=code&grant_type=authorization_code&token
9
+ headers:
10
+ Accept:
11
+ - application/json; charset=utf-8
12
+ User-Agent:
13
+ - Strava Ruby Client/0.1.0
14
+ Content-Type:
15
+ - application/x-www-form-urlencoded
16
+ Accept-Encoding:
17
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
18
+ response:
19
+ status:
20
+ code: 400
21
+ message: Bad Request
22
+ headers:
23
+ Date:
24
+ - Thu, 22 Nov 2018 20:16:41 GMT
25
+ Content-Type:
26
+ - application/json; charset=UTF-8
27
+ Transfer-Encoding:
28
+ - chunked
29
+ Connection:
30
+ - keep-alive
31
+ Cache-Control:
32
+ - no-cache
33
+ Via:
34
+ - 1.1 linkerd
35
+ Status:
36
+ - 400 Bad Request
37
+ X-Request-Id:
38
+ - 168ffeae-9029-4aec-bd50-3a0430e594bd
39
+ X-Frame-Options:
40
+ - SAMEORIGIN,DENY
41
+ X-Content-Type-Options:
42
+ - nosniff
43
+ X-Xss-Protection:
44
+ - 1; mode=block
45
+ body:
46
+ encoding: ASCII-8BIT
47
+ string: '{"message":"Bad Request","errors":[{"resource":"Application","field":"client_id","code":"invalid"}]}'
48
+ http_version:
49
+ recorded_at: Thu, 22 Nov 2018 20:16:41 GMT
50
+ recorded_with: VCR 4.0.0