From 3942dd3407a6134c62b82342f54be561de1add8c Mon Sep 17 00:00:00 2001 From: "Daniel (dB.) Doubrovkine" Date: Mon, 20 Oct 2025 12:46:59 -0400 Subject: [PATCH] Added support for page_size for activity_comments. --- CHANGELOG.md | 104 +++--- README.md | 20 +- UPGRADING.md | 25 ++ lib/strava/api/client.rb | 43 ++- lib/strava/api/cursor.rb | 28 +- lib/strava/api/endpoints/activities.rb | 5 +- lib/strava/api/pagination.rb | 3 +- .../strava/client/activity_comments_by_10.yml | 80 +++++ .../client/activity_comments_by_1_limit_2.yml | 302 ++++++++++++++++++ .../strava/client/activity_comments_by_2.yml | 79 +++++ .../strava/client/activity_comments_by_3.yml | 80 +++++ .../activity_comments_page_size_3_by_2.yml | 154 +++++++++ .../activity_comments_per_page_3_by_2.yml | 79 +++++ .../strava/client/activity_photos.yml | 16 +- .../client/all_activity_comments_by_1.yml | 302 ++++++++++++++++++ .../client/all_activity_comments_by_2.yml | 154 +++++++++ .../client/all_activity_comments_by_3.yml | 154 +++++++++ .../client/all_activity_comments_by_5.yml | 80 +++++ .../activities/activity_comments_spec.rb | 139 ++++++-- 19 files changed, 1745 insertions(+), 102 deletions(-) create mode 100644 spec/fixtures/strava/client/activity_comments_by_10.yml create mode 100644 spec/fixtures/strava/client/activity_comments_by_1_limit_2.yml create mode 100644 spec/fixtures/strava/client/activity_comments_by_2.yml create mode 100644 spec/fixtures/strava/client/activity_comments_by_3.yml create mode 100644 spec/fixtures/strava/client/activity_comments_page_size_3_by_2.yml create mode 100644 spec/fixtures/strava/client/activity_comments_per_page_3_by_2.yml create mode 100644 spec/fixtures/strava/client/all_activity_comments_by_1.yml create mode 100644 spec/fixtures/strava/client/all_activity_comments_by_2.yml create mode 100644 spec/fixtures/strava/client/all_activity_comments_by_3.yml create mode 100644 spec/fixtures/strava/client/all_activity_comments_by_5.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 27790f5..c2352c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ ### 3.0.0 (Next) -* [#97](https://github.com/dblock/strava-ruby-client/pull/97): Added `bin/strava-refresh-token` - [@dblock](https://github.com/dblock). -* [#96](https://github.com/dblock/strava-ruby-client/pull/96): Update and refactor models to spec - [@dblock](https://github.com/dblock). +* [#97](https://github.com/dblock/strava-ruby-client/pull/97): Adds `bin/strava-refresh-token` - [@dblock](https://github.com/dblock). +* [#96](https://github.com/dblock/strava-ruby-client/pull/96): Updates and refactor models to spec - [@dblock](https://github.com/dblock). * [#94](https://github.com/dblock/strava-ruby-client/pull/94): Adds video fields to `Strava::Models::Photo` - [@dblock](https://github.com/dblock). * [#92](https://github.com/dblock/strava-ruby-client/pull/92): Fixes `Hashie::Trash` serialization warning for `object_id` of `Strava::Webhooks::Models::Event` - [@simonneutert](https://github.com/simonneutert). -* [#95](https://github.com/dblock/strava-ruby-client/pull/95): Fixed `club_events` returning `Strava::Models::ClubEvent` with an empty string in `created_at` - [@dblock](https://github.com/dblock). +* [#95](https://github.com/dblock/strava-ruby-client/pull/95): Fixes `club_events` returning `Strava::Models::ClubEvent` with an empty string in `created_at` - [@dblock](https://github.com/dblock). +* [#98](https://github.com/dblock/strava-ruby-client/pull/98): Changes all `latlng` properties to return an instance of `Strava::Models::LatLng` - [@dblock](https://github.com/dblock). +* [#99](https://github.com/dblock/strava-ruby-client/pull/99): Adds support for `page_size` on `activity_comments` and adds `limit` - [@dblock](https://github.com/dblock). * [#93](https://github.com/dblock/strava-ruby-client/pull/93): Updates GitHub Actions workflows - [@simonneutert](https://github.com/simonneutert). * Your contribution here. @@ -26,86 +28,86 @@ ### 2.0.0 (2023/6/22) -* [#77](https://github.com/dblock/strava-ruby-client/pull/77): Drops unsupported endpoint/method `activity_photos` - [@simonneutert](https://github.com/simonneutert). +* [#77](https://github.com/dblock/strava-ruby-client/pull/77): Drops unsupported `activity_photos` - [@simonneutert](https://github.com/simonneutert). * [#62](https://github.com/dblock/strava-ruby-client/pull/68): Drops `Activity#type` attribute as it is being deprecated by Strava, dropping `Activity#type_emoji` with it - [@simonneutert](https://github.com/simonneutert). -* [#23](https://github.com/dblock/strava-ruby-client/pull/23): Failed uploads raise Strava::Errors::UploadError - [@ylecuyer](https://github.com/ylecuyer), [@simonneutert](https://github.com/simonneutert). +* [#23](https://github.com/dblock/strava-ruby-client/pull/23): Raises `Strava::Errors::UploadError` on failed uploads - [@ylecuyer](https://github.com/ylecuyer), [@simonneutert](https://github.com/simonneutert). * [#69](https://github.com/dblock/strava-ruby-client/pull/69): Raises `Strava::Api::RatelimitError`, when API ratelimit exceeded - [@simonneutert](https://github.com/simonneutert). * [#74](https://github.com/dblock/strava-ruby-client/pull/74): Fixes serialization causing `stack level too deep` - [@simonneutert](https://github.com/simonneutert). * [#75](https://github.com/dblock/strava-ruby-client/pull/75): Fixes DangerBot deprecation warning for `check` of TOC - [@simonneutert](https://github.com/simonneutert). ### 1.0.0 (2022/12/29) -* [#48](https://github.com/dblock/strava-ruby-client/pull/55): Removed `client.running_races` - [@simonneutert](https://github.com/simonneutert). -* [#56](https://github.com/dblock/strava-ruby-client/pull/56): Removed `client.segment_leaderboard` - [@simonneutert](https://github.com/simonneutert). -* [#58](https://github.com/dblock/strava-ruby-client/pull/58): Update Faraday to `>= 2.0` - [@schinery](https://github.com/schinery). -* [#58](https://github.com/dblock/strava-ruby-client/pull/58): Removed support for Ruby 2.5 and 2.6 - [@schinery](https://github.com/schinery). -* [#58](https://github.com/dblock/strava-ruby-client/pull/58): Upgraded Robocop to 1.3.0 - [@schinery](https://github.com/schinery). -* [#59](https://github.com/dblock/strava-ruby-client/pull/59): Removed deprecated `athlete.email` field - [@francordie](https://github.com/francordie). -* [#63](https://github.com/dblock/strava-ruby-client/pull/63): Added `activity.sport_type` for Strava API V3 specifications - [@simonneutert](https://github.com/simonneutert). +* [#48](https://github.com/dblock/strava-ruby-client/pull/55): Removes `client.running_races` - [@simonneutert](https://github.com/simonneutert). +* [#56](https://github.com/dblock/strava-ruby-client/pull/56): Removes `client.segment_leaderboard` - [@simonneutert](https://github.com/simonneutert). +* [#58](https://github.com/dblock/strava-ruby-client/pull/58): Updates Faraday to `>= 2.0` - [@schinery](https://github.com/schinery). +* [#58](https://github.com/dblock/strava-ruby-client/pull/58): Removes support for Ruby 2.5 and 2.6 - [@schinery](https://github.com/schinery). +* [#58](https://github.com/dblock/strava-ruby-client/pull/58): Upgrades Robocop to 1.3.0 - [@schinery](https://github.com/schinery). +* [#59](https://github.com/dblock/strava-ruby-client/pull/59): Removes deprecated `athlete.email` field - [@francordie](https://github.com/francordie). +* [#63](https://github.com/dblock/strava-ruby-client/pull/63): Adds `activity.sport_type` for Strava API V3 specifications - [@simonneutert](https://github.com/simonneutert). * [#61](https://github.com/dblock/strava-ruby-client/pull/61): Adds ratelimit as a property of model(s) - [@simonneutert](https://github.com/simonneutert), [@adamwolf](https://github.com/adamwolf). ### 0.4.3 (2022/03/21) -* [#51](https://github.com/dblock/strava-ruby-client/pull/51): Ensure support for large Integer IDs - [@simonneutert](https://github.com/simonneutert). -* [#53](https://github.com/dblock/strava-ruby-client/pull/53): Upgraded to RuboCop 1.26.0 - [@dblock](https://github.com/dblock). -* [#46](https://github.com/dblock/strava-ruby-client/pull/46): Added missing `activity:read` scope from `strava-oauth-token` - [@xaviershay](https://github.com/xaviershay). +* [#51](https://github.com/dblock/strava-ruby-client/pull/51): Ensures support for large Integer IDs - [@simonneutert](https://github.com/simonneutert). +* [#53](https://github.com/dblock/strava-ruby-client/pull/53): Upgrades to RuboCop 1.26.0 - [@dblock](https://github.com/dblock). +* [#46](https://github.com/dblock/strava-ruby-client/pull/46): Adds missing `activity:read` scope from `strava-oauth-token` - [@xaviershay](https://github.com/xaviershay). ### 0.4.2 (2021/10/03) -* [#45](https://github.com/dblock/strava-ruby-client/pull/45): Added `Gear#frame_type` `#retired` `#weight` - [@deuber](https://github.com/deuber). +* [#45](https://github.com/dblock/strava-ruby-client/pull/45): Adds `Gear#frame_type` `#retired` `#weight` - [@deuber](https://github.com/deuber). ### 0.4.1 (2021/07/11) -* [#37](https://github.com/dblock/strava-ruby-client/pull/38): Added `Club#club_events` - [@simonneutert](https://github.com/simonneutert). -* [#34](https://github.com/dblock/strava-ruby-client/pull/34): Added `Athlete#ftp` - [@virae](https://github.com/virae). +* [#37](https://github.com/dblock/strava-ruby-client/pull/38): Adds `Club#club_events` - [@simonneutert](https://github.com/simonneutert). +* [#34](https://github.com/dblock/strava-ruby-client/pull/34): Adds `Athlete#ftp` - [@virae](https://github.com/virae). ### 0.4.0 (2020/05/09) -* [#29](https://github.com/dblock/strava-ruby-client/pull/29): Cache `Faraday::Connection` for persistent adapters - [@dblock](https://github.com/dblock). -* [#30](https://github.com/dblock/strava-ruby-client/pull/30): Require Faraday >= 1.0 - [@dblock](https://github.com/dblock). +* [#29](https://github.com/dblock/strava-ruby-client/pull/29): Caches `Faraday::Connection` for persistent adapters - [@dblock](https://github.com/dblock). +* [#30](https://github.com/dblock/strava-ruby-client/pull/30): Requires Faraday >= 1.0 - [@dblock](https://github.com/dblock). ### 0.3.2 (2020/03/28) -* [#26](https://github.com/dblock/strava-ruby-client/pull/26): Corrected `Strava::Webhooks.config.endpoint` - [@dblock](https://github.com/dblock). -* Automatically convert `before` and `after` arguments of `Strava::Api::Client#athlete_activities` from `Time` to `Integer` - [@dblock](https://github.com/dblock). -* [#18](https://github.com/dblock/strava-ruby-client/pull/18): Testing against Ruby 2.5.3 and 2.6.0 - [@lucianosousa](https://github.com/lucianosousa). -* [#18](https://github.com/dblock/strava-ruby-client/pull/18): Upgraded Rubocop to 0.61.1 - [@lucianosousa](https://github.com/lucianosousa). -* [#21](https://github.com/dblock/strava-ruby-client/pull/21): Include headers in error response - [@jameschevalier](https://github.com/jameschevalier). +* [#26](https://github.com/dblock/strava-ruby-client/pull/26): Corrects `Strava::Webhooks.config.endpoint` - [@dblock](https://github.com/dblock). +* Automatically converts `before` and `after` arguments of `Strava::Api::Client#athlete_activities` from `Time` to `Integer` - [@dblock](https://github.com/dblock). +* [#18](https://github.com/dblock/strava-ruby-client/pull/18): Adds testing against Ruby 2.5.3 and 2.6.0 - [@lucianosousa](https://github.com/lucianosousa). +* [#18](https://github.com/dblock/strava-ruby-client/pull/18): Upgrades Rubocop to 0.61.1 - [@lucianosousa](https://github.com/lucianosousa). +* [#21](https://github.com/dblock/strava-ruby-client/pull/21): Includes headers in error response - [@jameschevalier](https://github.com/jameschevalier). ### 0.3.1 (2018/12/05) -* Added `Strava::Api::Client#activity_photos` - [@dblock](https://github.com/dblock). -* [#2](https://github.com/dblock/strava-ruby-client/issues/2): Added `Strava::Api::Client#deauthorize` - [@dblock](https://github.com/dblock). -* [#15](https://github.com/dblock/strava-ruby-client/issues/15): Added `strava-oauth-token` and `strava-webhooks` to gem executables - [@dblock](https://github.com/dblock). -* Fix: `Strava::Models::Activity#total_elevation_gain` no returns blank for negative and zero elevation differences - [@dblock](https://github.com/dblock). -* Fix: `Strava::Models::Split#distance` and `total_elevation_gain` incorrect for `Strava::Models::Activity#splits_standard` - [@dblock](https://github.com/dblock). +* Adds `Strava::Api::Client#activity_photos` - [@dblock](https://github.com/dblock). +* [#2](https://github.com/dblock/strava-ruby-client/issues/2): Adds `Strava::Api::Client#deauthorize` - [@dblock](https://github.com/dblock). +* [#15](https://github.com/dblock/strava-ruby-client/issues/15): Adds `strava-oauth-token` and `strava-webhooks` to gem executables - [@dblock](https://github.com/dblock). +* Fixes `Strava::Models::Activity#total_elevation_gain` no returns blank for negative and zero elevation differences - [@dblock](https://github.com/dblock). +* Fixes `Strava::Models::Split#distance` and `total_elevation_gain` incorrect for `Strava::Models::Activity#splits_standard` - [@dblock](https://github.com/dblock). ### 0.3.0 (2018/12/03) -* Added webhooks support - [@dblock](https://github.com/dblock). -* Added `Strava::Api::Client#athlete_zones`, `athlete_stats` and `update_athlete` - [@dblock](https://github.com/dblock). -* Added `Strava::Api::Client#club`, `club_admins` and `club_members` - [@dblock](https://github.com/dblock). -* Added `Strava::Api::Client#gear` - [@dblock](https://github.com/dblock). -* Added `Strava::Api::Client#route`, `athlete_routes`, `export_route_gpx` and `export_route_tcx` - [@dblock](https://github.com/dblock). -* Added `Strava::Api::Client#running_race` and `running_races` - [@dblock](https://github.com/dblock). -* Added `Strava::Api::Client#segment_effort` and `segment_efforts` - [@dblock](https://github.com/dblock). -* Added `Strava::Api::Client#explore_segments`, `segment_leaderboard`, `starred_segments`, `segment` and `star_segment` - [@dblock](https://github.com/dblock). -* Added `Strava::Api::Client#activity_streams`, `segment_effort_streams` and `segment_streams` - [@dblock](https://github.com/dblock). -* Added `Strava::Api::Client#create_upload` and `upload` - [@dblock](https://github.com/dblock). -* [#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). +* Adds webhooks support - [@dblock](https://github.com/dblock). +* Adds `Strava::Api::Client#athlete_zones`, `athlete_stats` and `update_athlete` - [@dblock](https://github.com/dblock). +* Adds `Strava::Api::Client#club`, `club_admins` and `club_members` - [@dblock](https://github.com/dblock). +* Adds `Strava::Api::Client#gear` - [@dblock](https://github.com/dblock). +* Adds `Strava::Api::Client#route`, `athlete_routes`, `export_route_gpx` and `export_route_tcx` - [@dblock](https://github.com/dblock). +* Adds `Strava::Api::Client#running_race` and `running_races` - [@dblock](https://github.com/dblock). +* Adds `Strava::Api::Client#segment_effort` and `segment_efforts` - [@dblock](https://github.com/dblock). +* Adds `Strava::Api::Client#explore_segments`, `segment_leaderboard`, `starred_segments`, `segment` and `star_segment` - [@dblock](https://github.com/dblock). +* Adds `Strava::Api::Client#activity_streams`, `segment_effort_streams` and `segment_streams` - [@dblock](https://github.com/dblock). +* Adds `Strava::Api::Client#create_upload` and `upload` - [@dblock](https://github.com/dblock). +* [#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). ### 0.2.0 (2018/11/27) -* Added `Strava::Api::Client#activity` with segments, photos, similar activities, trends, laps and gear - [@dblock](https://github.com/dblock). -* Added `Activity#type_emoji` and `Activity#strava_url` - [@dblock](https://github.com/dblock). -* Added `Athlete#name` and `Athlete#strava_url` - [@dblock](https://github.com/dblock). -* Added `Strava::Api::Client#athlete_clubs` - [@dblock](https://github.com/dblock). -* Added `Strava::Api::Client#club_activities` - [@dblock](https://github.com/dblock). -* Added `Strava::Api::Client#create_activity` and `Strava::Api::Client#update_activity` - [@dblock](https://github.com/dblock). -* Added `Strava::Api::Client#activity_comments`, `activity_kudos`, `activity_zones` and `activity_laps` - [@dblock](https://github.com/dblock). -* Added support for converting and displaying activity distance, elevation, speed and pace - [@dblock](https://github.com/dblock). -* [#5](https://github.com/dblock/strava-ruby-client/issues/5): Added support for pagination - [@dblock](https://github.com/dblock). -* [#6](https://github.com/dblock/strava-ruby-client/issues/6): The `strava-oauth-token` tool has been renamed and will handle the redirect in the browser - [@dblock](https://github.com/dblock). +* Adds `Strava::Api::Client#activity` with segments, photos, similar activities, trends, laps and gear - [@dblock](https://github.com/dblock). +* Adds `Activity#type_emoji` and `Activity#strava_url` - [@dblock](https://github.com/dblock). +* Adds `Athlete#name` and `Athlete#strava_url` - [@dblock](https://github.com/dblock). +* Adds `Strava::Api::Client#athlete_clubs` - [@dblock](https://github.com/dblock). +* Adds `Strava::Api::Client#club_activities` - [@dblock](https://github.com/dblock). +* Adds `Strava::Api::Client#create_activity` and `Strava::Api::Client#update_activity` - [@dblock](https://github.com/dblock). +* Adds `Strava::Api::Client#activity_comments`, `activity_kudos`, `activity_zones` and `activity_laps` - [@dblock](https://github.com/dblock). +* Adds support for converting and displaying activity distance, elevation, speed and pace - [@dblock](https://github.com/dblock). +* [#5](https://github.com/dblock/strava-ruby-client/issues/5): Adds support for pagination - [@dblock](https://github.com/dblock). +* [#6](https://github.com/dblock/strava-ruby-client/issues/6): Renames the `strava-oauth-token` tool and handles the redirect in the browser - [@dblock](https://github.com/dblock). ### 0.1.0 (2018/11/23) diff --git a/README.md b/README.md index 5b65090..e3c614b 100644 --- a/README.md +++ b/README.md @@ -790,7 +790,7 @@ See [Strava::Errors::UploadError](lib/strava/errors/upload_failed_error.rb) for ### Pagination -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. +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. If you supply `per_page`, the client will retrieve all pages. You can paginate through items incrementally by supplying a block. Use `limit` to limit the number of items returned. The underlying implementation makes page-sized calls and increments the `page` argument. ```ruby client.athlete_activities(per_page: 30) do |activity| @@ -798,6 +798,24 @@ client.athlete_activities(per_page: 30) do |activity| end ``` +```ruby +client.athlete_activities(per_page: 30) # => [Strava::Models::Activity], all pages +client.athlete_activities(per_page: 30, limit: 50) # => [Strava::Models::Activity], all pages, stop at 50 items +``` + +Some Strava APIs, including [activity-comments](#list-activity-comments) support cursor-based pagination when supplying optional `after_cursor` and `page_size` parameters. By default the client retrieves one page of data, which Strava currently defaults to 30 items. If you supply `page_size`, the client will retrieve all pages. You can paginate through items incrementally by supplying a block. Use `limit` to limit the number of items returned. The underlying implementation makes page-sized calls and uses the returned `cursor` as `after_cursor`. + +```ruby +client.activity_comments(id: 1982980795, page_size: 30) do |comment| + comment # => Strava::Models::Comment +end +``` + +```ruby +client.activity_comments(id: 1982980795, page_size: 30) # => [Strava::Models::Comment], all pages +client.activity_comments(id: 1982980795, page_size: 30, limit: 100) # => [Strava::Models::Comment], paginated, stop at 100 items +``` + ### OAuth #### OAuth Workflow diff --git a/UPGRADING.md b/UPGRADING.md index 76a769e..1b5c8ff 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -93,6 +93,31 @@ This change fixes the `Hashie::Trash` serialization warning that occurred when u See [#92](https://github.com/dblock/strava-ruby-client/pull/92) for details. +#### Always Paginate with `per_page` or `page_size` + +The client will always paginate data if `per_page` or `page_size` are provided. + +**Before (v2.x):** +```ruby +# one request for a page of 10 items +client.activity_comments(id: 1982980795, page_size: 10) # => [Strava::Models::Comment] +``` + +**After (v3.0.0):** +```ruby +# all pages, multiple requests, potentially more than 10 items +client.activity_comments(id: 1982980795, page_size: 10) # => [Strava::Models::Comment] +``` + +Use `limit` in combination with `page_size` to both paginate and retrieve a certain number of items. + +```ruby +# all pages, 10 items per page, no more than 20 items +client.activity_comments(id: 1982980795, page_size: 10, limit: 20) # => [Strava::Models::Comment] +``` + +See [#99](https://github.com/dblock/strava-ruby-client/pull/99) for details. + ### Upgrading to >= 2.3.0 Faraday can optionally exclude HTTP method, path and query params from the errors raised. The client implementation options will now default to `Faraday::Response::RaiseError::DEFAULT_OPTIONS` with `include_request` set to `true`. You can change this behavior by setting `Strava::Web::RaiseResponseError::DEFAULT_OPTIONS`. diff --git a/lib/strava/api/client.rb b/lib/strava/api/client.rb index 1722df3..0c9a41a 100644 --- a/lib/strava/api/client.rb +++ b/lib/strava/api/client.rb @@ -40,7 +40,7 @@ def config private # - # paginates requests + # Paginates requests using per_page. # # @param [String] path url for request # @param [Hash] options hash containing settings @@ -51,21 +51,54 @@ def config # # @return [Strava::Api::Pagination] # - def paginate(path, options, model) + def paginate(path, options, model, &block) + raise ArgumentError, 'per_page' if options.key(:page_size) + + paginate_with_cursor(path, options, model, &block) + end + + # + # Paginates requests using a cursor, using per_page or page_size. + # + # @param [String] path url for request + # @param [Hash] options hash containing settings + # @param [Class] model by Class + # + # @example + # paginate("activity/comments", {page_size: 72}, Strava::Models::Comment) + # paginate("athlete/activities", {per_page: 72}, Strava::Models::Activity) + # + # @return [Strava::Api::Pagination] + # + def paginate_with_cursor(path, options, model) + # avoid retrieving unnecessary items + options = options.merge(per_page: options[:limit]) if options.key?(:per_page) && options.key(:limit) && options[:per_page] > options[:limit] + options = options.merge(page_size: options[:limit]) if options.key?(:page_size) && options.key(:limit) && options[:page_size] > options[:limit] collection = [] web_response = nil - if block_given? + if block_given? || options.key?(:page_size) || options.key(:per_page) Cursor.new(self, path, options).each do |page| web_response = page # response of the last request made page.each do |row| + break if options.key?(:limit) && collection.size >= options[:limit] + m = model.new(row) - yield m + yield m if block_given? collection << m end end else - web_response = get(path, options) + page_options = if options.key?(:limit) + options.dup.tap do |copy| + copy[:page_size] = copy.delete(:limit) + end + else + options + end + web_response = get(path, page_options) collection = web_response.map do |row| + break if options.key?(:limit) && collection.size >= options[:limit] + model.new(row) end end diff --git a/lib/strava/api/cursor.rb b/lib/strava/api/cursor.rb index 43bfe9c..73302a7 100644 --- a/lib/strava/api/cursor.rb +++ b/lib/strava/api/cursor.rb @@ -10,10 +10,16 @@ class Cursor def initialize(client, path, params = {}) @client = client @path = path - @params = params + @params = params.key?(:limit) ? params.dup.tap { |p| p.delete :limit } : params end - def each + def each(&block) + params[:page_size] ? each_cursor(&block) : each_page(&block) + end + + private + + def each_page next_page = 1 loop do query = params.merge(page: next_page) @@ -24,6 +30,24 @@ def each next_page += 1 end end + + def each_cursor + cursor = nil + query = params + loop do + response = client.get(path, query) + break unless response.any? + + yield response + + break if response.size < params[:page_size] + + cursor = response.last&.[]('cursor') + break unless cursor.present? + + query = params.merge(after_cursor: cursor) + end + end end end end diff --git a/lib/strava/api/endpoints/activities.rb b/lib/strava/api/endpoints/activities.rb index 84a4c30..8ec85b2 100644 --- a/lib/strava/api/endpoints/activities.rb +++ b/lib/strava/api/endpoints/activities.rb @@ -29,12 +29,15 @@ def activity(id_or_options, options = {}) # Activity id. # @option options [Integer] :page # Page number. + # @option options [Integer] :page_size + # Number of items per page. Defaults to 30. # @option options [Integer] :per_page # Number of items per page. Defaults to 30. + # @deprecated use {page_size} # def activity_comments(id_or_options, options = {}, &block) id, options = parse_args(id_or_options, options) - paginate "activities/#{id}/comments", options, Strava::Models::Comment, &block + paginate_with_cursor "activities/#{id}/comments", options, Strava::Models::Comment, &block end # diff --git a/lib/strava/api/pagination.rb b/lib/strava/api/pagination.rb index 04e9fac..c10609e 100644 --- a/lib/strava/api/pagination.rb +++ b/lib/strava/api/pagination.rb @@ -29,8 +29,7 @@ def initialize(collection, web_response) # @return [Strava::Web::ApiResponse] # def http_response - @http_response ||= - Strava::Web::ApiResponse.new(deep_copy(@web_response).http_response) + @http_response ||= Strava::Web::ApiResponse.new(deep_copy(@web_response).http_response) end # diff --git a/spec/fixtures/strava/client/activity_comments_by_10.yml b/spec/fixtures/strava/client/activity_comments_by_10.yml new file mode 100644 index 0000000..9ea5d75 --- /dev/null +++ b/spec/fixtures/strava/client/activity_comments_by_10.yml @@ -0,0 +1,80 @@ +--- +http_interactions: +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?page_size=10 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:19:48 GMT + X-Envoy-Upstream-Service-Time: + - '129' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '2,216' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - 7ea35f78-fe0d-4463-a829-04ba404cd962 + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"259744c12678c860f34011e9fe3f044d" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '2,216' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 def26d054ec95b961e8352e3cd4fae7e.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - u037I6HgkVpnQFovLzct6jSm_uYMH58j8O8C4kohGLkRhNuN7rFP5w== + body: + encoding: UTF-8 + string: '[{"id":2257523290,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"you + pass?","mentions_metadata":null,"created_at":"2025-08-27T18:36:59Z","cursor":"eyJpZCI6MjI1NzUyMzI5MCwiY3JlYXRlZEF0IjoxNzU2MzE5ODE5MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Sasha","lastname":"C."},"reaction_count":0,"has_reacted":false},{"id":2257703241,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"Sasha + Caskey I will live another day!","mentions_metadata":null,"created_at":"2025-08-27T21:55:13Z","cursor":"eyJpZCI6MjI1NzcwMzI0MSwiY3JlYXRlZEF0IjoxNzU2MzMxNzEzMDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Daniel","lastname":"D."},"reaction_count":1,"has_reacted":false},{"id":2257782099,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"I + didn''t know they do competitive EKG Stress Test","mentions_metadata":null,"created_at":"2025-08-28T00:38:04Z","cursor":"eyJpZCI6MjI1Nzc4MjA5OSwiY3JlYXRlZEF0IjoxNzU2MzQxNDg0MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Vlad","lastname":"Z."},"reaction_count":0,"has_reacted":false}]' + recorded_at: Mon, 20 Oct 2025 16:19:48 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/strava/client/activity_comments_by_1_limit_2.yml b/spec/fixtures/strava/client/activity_comments_by_1_limit_2.yml new file mode 100644 index 0000000..4a279b5 --- /dev/null +++ b/spec/fixtures/strava/client/activity_comments_by_1_limit_2.yml @@ -0,0 +1,302 @@ +--- +http_interactions: +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?page_size=1 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:57:15 GMT + X-Envoy-Upstream-Service-Time: + - '4458' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '4,227' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - 50d2e6d7-6896-4fd5-860e-3f352175616f + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"abcc8d605273b9e6a025a088581ecf6d" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '4,227' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 ae51343dd6ef5c549d5af91c7efd8f00.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - kLFwVTSxUSptMQhkhGST2Sf-KOGLTNMbBWDVofa5rlQx26P_Q9UCNQ== + body: + encoding: UTF-8 + string: '[{"id":2257523290,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"you + pass?","mentions_metadata":null,"created_at":"2025-08-27T18:36:59Z","cursor":"eyJpZCI6MjI1NzUyMzI5MCwiY3JlYXRlZEF0IjoxNzU2MzE5ODE5MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Sasha","lastname":"C."},"reaction_count":0,"has_reacted":false}]' + recorded_at: Mon, 20 Oct 2025 16:57:15 GMT +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?after_cursor=eyJpZCI6MjI1NzUyMzI5MCwiY3JlYXRlZEF0IjoxNzU2MzE5ODE5MDAwfQ%253D%253D&page_size=1 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:57:19 GMT + X-Envoy-Upstream-Service-Time: + - '4177' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '5,228' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - 8025ae9b-74f7-446f-8d03-25ac24e1b52b + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"4f09c862d1a92b6c83505cf2be655f3d" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '5,228' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 2260f0d6b734b81aaef20a0b1c178318.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - Is1F6t38HqcEtA2_ut_kh-MdrySkdigzvRvUvxKCVrExYRwfrNBaPQ== + body: + encoding: UTF-8 + string: '[{"id":2257703241,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"Sasha + Caskey I will live another day!","mentions_metadata":null,"created_at":"2025-08-27T21:55:13Z","cursor":"eyJpZCI6MjI1NzcwMzI0MSwiY3JlYXRlZEF0IjoxNzU2MzMxNzEzMDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Daniel","lastname":"D."},"reaction_count":1,"has_reacted":false}]' + recorded_at: Mon, 20 Oct 2025 16:57:19 GMT +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?after_cursor=eyJpZCI6MjI1NzcwMzI0MSwiY3JlYXRlZEF0IjoxNzU2MzMxNzEzMDAwfQ%253D%253D&page_size=1 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:57:20 GMT + X-Envoy-Upstream-Service-Time: + - '769' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '6,229' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - 5ba40d52-fbec-4d3d-89a3-667b8461c0be + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"3d7e5ff05291542b5fe10dd7f4d3b113" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '6,229' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 4e1c4d133adc8d8214916eeaddd7af66.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - J9BFIZpglkQPRV6WUnZ6_jdHigvYWl-YgBMprOt9l_dY0TgSkhyxnw== + body: + encoding: UTF-8 + string: '[{"id":2257782099,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"I + didn''t know they do competitive EKG Stress Test","mentions_metadata":null,"created_at":"2025-08-28T00:38:04Z","cursor":"eyJpZCI6MjI1Nzc4MjA5OSwiY3JlYXRlZEF0IjoxNzU2MzQxNDg0MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Vlad","lastname":"Z."},"reaction_count":0,"has_reacted":false}]' + recorded_at: Mon, 20 Oct 2025 16:57:20 GMT +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?after_cursor=eyJpZCI6MjI1Nzc4MjA5OSwiY3JlYXRlZEF0IjoxNzU2MzQxNDg0MDAwfQ%253D%253D&page_size=1 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:57:22 GMT + X-Envoy-Upstream-Service-Time: + - '1107' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '7,230' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - 5c172637-7f52-4d30-a5dd-4fb348794c6f + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"4f53cda18c2baa0c0354bb5f9a3ecbe5" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '7,230' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 2784337ad1bef2f5343cdf0842e12a80.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - gWz1oO-Gyhmp3Ya7G8RO2NI2Oym3Bix2kXKlxOx9w2-0vvbMyvwhHg== + body: + encoding: UTF-8 + string: "[]" + recorded_at: Mon, 20 Oct 2025 16:57:22 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/strava/client/activity_comments_by_2.yml b/spec/fixtures/strava/client/activity_comments_by_2.yml new file mode 100644 index 0000000..78f0d69 --- /dev/null +++ b/spec/fixtures/strava/client/activity_comments_by_2.yml @@ -0,0 +1,79 @@ +--- +http_interactions: +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?page_size=2 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:19:48 GMT + X-Envoy-Upstream-Service-Time: + - '132' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '1,215' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - bb9d8ae3-d944-47ae-94d1-95422df31a5d + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"30aae7abb9e1b9801e05e9187f3e444b" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '1,215' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 a363b826ba48f4e79f7e95839a3bcf3a.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - OH0O5MC0e3Nv_xdQ4OE4OTnIlGOtzCjVin6cy0VpSBmEiCwsOQULYw== + body: + encoding: UTF-8 + string: '[{"id":2257523290,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"you + pass?","mentions_metadata":null,"created_at":"2025-08-27T18:36:59Z","cursor":"eyJpZCI6MjI1NzUyMzI5MCwiY3JlYXRlZEF0IjoxNzU2MzE5ODE5MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Sasha","lastname":"C."},"reaction_count":0,"has_reacted":false},{"id":2257703241,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"Sasha + Caskey I will live another day!","mentions_metadata":null,"created_at":"2025-08-27T21:55:13Z","cursor":"eyJpZCI6MjI1NzcwMzI0MSwiY3JlYXRlZEF0IjoxNzU2MzMxNzEzMDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Daniel","lastname":"D."},"reaction_count":1,"has_reacted":false}]' + recorded_at: Mon, 20 Oct 2025 16:19:48 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/strava/client/activity_comments_by_3.yml b/spec/fixtures/strava/client/activity_comments_by_3.yml new file mode 100644 index 0000000..e51fee6 --- /dev/null +++ b/spec/fixtures/strava/client/activity_comments_by_3.yml @@ -0,0 +1,80 @@ +--- +http_interactions: +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?page_size=3 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:20:08 GMT + X-Envoy-Upstream-Service-Time: + - '175' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '3,217' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - d8da58d5-0b4d-46eb-989a-49ed99f6e25e + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"259744c12678c860f34011e9fe3f044d" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '3,217' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 2784337ad1bef2f5343cdf0842e12a80.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - 3wtJH9noba27cScbcbRc4cCcPaq9_E91YoJ44NuSZyezR5iAq8jNfQ== + body: + encoding: UTF-8 + string: '[{"id":2257523290,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"you + pass?","mentions_metadata":null,"created_at":"2025-08-27T18:36:59Z","cursor":"eyJpZCI6MjI1NzUyMzI5MCwiY3JlYXRlZEF0IjoxNzU2MzE5ODE5MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Sasha","lastname":"C."},"reaction_count":0,"has_reacted":false},{"id":2257703241,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"Sasha + Caskey I will live another day!","mentions_metadata":null,"created_at":"2025-08-27T21:55:13Z","cursor":"eyJpZCI6MjI1NzcwMzI0MSwiY3JlYXRlZEF0IjoxNzU2MzMxNzEzMDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Daniel","lastname":"D."},"reaction_count":1,"has_reacted":false},{"id":2257782099,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"I + didn''t know they do competitive EKG Stress Test","mentions_metadata":null,"created_at":"2025-08-28T00:38:04Z","cursor":"eyJpZCI6MjI1Nzc4MjA5OSwiY3JlYXRlZEF0IjoxNzU2MzQxNDg0MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Vlad","lastname":"Z."},"reaction_count":0,"has_reacted":false}]' + recorded_at: Mon, 20 Oct 2025 16:20:08 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/strava/client/activity_comments_page_size_3_by_2.yml b/spec/fixtures/strava/client/activity_comments_page_size_3_by_2.yml new file mode 100644 index 0000000..51b2e2a --- /dev/null +++ b/spec/fixtures/strava/client/activity_comments_page_size_3_by_2.yml @@ -0,0 +1,154 @@ +--- +http_interactions: +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?page_size=3 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:57:07 GMT + X-Envoy-Upstream-Service-Time: + - '899' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '2,225' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - d24db2d6-e60a-4708-94e1-21dbedbc2b14 + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"259744c12678c860f34011e9fe3f044d" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '2,225' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 90707ba4ec932f1b72abfb5c4f1add2e.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - ZUSSOBszR1KJqdobouGkGhcI8jI98cgma20TqeJuHIKPQwX6KtNfGQ== + body: + encoding: UTF-8 + string: '[{"id":2257523290,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"you + pass?","mentions_metadata":null,"created_at":"2025-08-27T18:36:59Z","cursor":"eyJpZCI6MjI1NzUyMzI5MCwiY3JlYXRlZEF0IjoxNzU2MzE5ODE5MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Sasha","lastname":"C."},"reaction_count":0,"has_reacted":false},{"id":2257703241,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"Sasha + Caskey I will live another day!","mentions_metadata":null,"created_at":"2025-08-27T21:55:13Z","cursor":"eyJpZCI6MjI1NzcwMzI0MSwiY3JlYXRlZEF0IjoxNzU2MzMxNzEzMDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Daniel","lastname":"D."},"reaction_count":1,"has_reacted":false},{"id":2257782099,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"I + didn''t know they do competitive EKG Stress Test","mentions_metadata":null,"created_at":"2025-08-28T00:38:04Z","cursor":"eyJpZCI6MjI1Nzc4MjA5OSwiY3JlYXRlZEF0IjoxNzU2MzQxNDg0MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Vlad","lastname":"Z."},"reaction_count":0,"has_reacted":false}]' + recorded_at: Mon, 20 Oct 2025 16:57:07 GMT +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?after_cursor=eyJpZCI6MjI1Nzc4MjA5OSwiY3JlYXRlZEF0IjoxNzU2MzQxNDg0MDAwfQ%253D%253D&page_size=3 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 17:02:42 GMT + X-Envoy-Upstream-Service-Time: + - '555' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '3,233' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - 27b7fcc2-dd5c-4d3d-a8d9-2450306ff6aa + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"4f53cda18c2baa0c0354bb5f9a3ecbe5" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '3,233' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 145a3c1a881b9a37bb761d4b0890859a.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - 8_TTRLtS7_l4Z0kNa9meul3TEDsrb1A06oqfz1AICxS-Wus2ESJByw== + body: + encoding: UTF-8 + string: "[]" + recorded_at: Mon, 20 Oct 2025 17:02:42 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/strava/client/activity_comments_per_page_3_by_2.yml b/spec/fixtures/strava/client/activity_comments_per_page_3_by_2.yml new file mode 100644 index 0000000..5fb6430 --- /dev/null +++ b/spec/fixtures/strava/client/activity_comments_per_page_3_by_2.yml @@ -0,0 +1,79 @@ +--- +http_interactions: +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?page_size=2&per_page=3 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:57:06 GMT + X-Envoy-Upstream-Service-Time: + - '541' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '1,224' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - cba2fcc3-8392-4d44-8b92-15b35ad60fac + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"30aae7abb9e1b9801e05e9187f3e444b" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '1,224' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 932eefec422d884c28f3c110319f29fe.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - oIYXaqV2H5kItzgZQ4Q_TcsuwYCN0WV5dLh0NeaBcdursqz87YOh4g== + body: + encoding: UTF-8 + string: '[{"id":2257523290,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"you + pass?","mentions_metadata":null,"created_at":"2025-08-27T18:36:59Z","cursor":"eyJpZCI6MjI1NzUyMzI5MCwiY3JlYXRlZEF0IjoxNzU2MzE5ODE5MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Sasha","lastname":"C."},"reaction_count":0,"has_reacted":false},{"id":2257703241,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"Sasha + Caskey I will live another day!","mentions_metadata":null,"created_at":"2025-08-27T21:55:13Z","cursor":"eyJpZCI6MjI1NzcwMzI0MSwiY3JlYXRlZEF0IjoxNzU2MzMxNzEzMDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Daniel","lastname":"D."},"reaction_count":1,"has_reacted":false}]' + recorded_at: Mon, 20 Oct 2025 16:57:06 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/strava/client/activity_photos.yml b/spec/fixtures/strava/client/activity_photos.yml index ed043e2..1f87816 100644 --- a/spec/fixtures/strava/client/activity_photos.yml +++ b/spec/fixtures/strava/client/activity_photos.yml @@ -27,15 +27,15 @@ http_interactions: Connection: - keep-alive Date: - - Sun, 19 Oct 2025 16:33:09 GMT + - Mon, 20 Oct 2025 17:00:51 GMT X-Envoy-Upstream-Service-Time: - - '83' + - '3753' Server: - istio-envoy Status: - 200 OK X-Ratelimit-Usage: - - '2,20' + - '1,231' X-Ratelimit-Limit: - '200,2000' Vary: @@ -49,7 +49,7 @@ http_interactions: X-Xss-Protection: - 1; mode=block X-Request-Id: - - bc7ed6a3-f783-496a-b522-f61c214143a6 + - 8d696c08-bbe8-4afc-bd62-cdeeb853116c X-Readratelimit-Limit: - '100,1000' X-Download-Options: @@ -59,17 +59,17 @@ http_interactions: X-Frame-Options: - DENY X-Readratelimit-Usage: - - '2,20' + - '1,231' X-Content-Type-Options: - nosniff X-Cache: - Miss from cloudfront Via: - - 1.1 def26d054ec95b961e8352e3cd4fae7e.cloudfront.net (CloudFront) + - 1.1 92d8afc92e3597d245b2f6480cd44220.cloudfront.net (CloudFront) X-Amz-Cf-Pop: - JFK52-P3 X-Amz-Cf-Id: - - kN6XO7F597QVt9HTiuaAkxUyOx6bg-zlDu5n_MHIC_faiNvD2jQuAQ== + - 0lgZSNBXy-Uf7xbXO87wGEw_iGFRnemIxLXpc-aEE9r7pGZ-L1l4og== body: encoding: UTF-8 string: '[{"unique_id":"6D1E4A0B-0A46-406A-874A-C3ED694DDE55","athlete_id":26462176,"activity_id":16181809559,"activity_name":"Run @@ -77,5 +77,5 @@ http_interactions: with Artyom","post_id":null,"resource_state":2,"caption":"","type":1,"source":1,"status":3,"uploaded_at":"2025-10-18T15:28:11Z","created_at":"2025-10-16T22:18:39Z","created_at_local":"2025-10-16T18:18:39Z","urls":{"5000":"https://dgtzuqphqg23d.cloudfront.net/37jS2r9iH-D8K53dVzGi6lq-Nv5JRNqpmPnO-Qdtb1s-1536x2048.jpg"},"placeholder_image":null,"sizes":{"5000":[1536,2048]},"default_photo":false,"cursor":null,"location":[40.76137166666667,-73.971825]},{"unique_id":"F5F942E4-DE41-4BFF-842B-99E66D6E4345","athlete_id":26462176,"activity_id":16181809559,"activity_name":"Run with Artyom","post_id":null,"resource_state":2,"caption":"","type":1,"source":1,"status":3,"uploaded_at":"2025-10-18T15:28:11Z","created_at":"2025-10-16T14:07:37Z","created_at_local":"2025-10-16T10:07:37Z","urls":{"5000":"https://dgtzuqphqg23d.cloudfront.net/NSD1vutEd09T0klV_1EPJjvxkRxV2EX7PBTRc26nIQc-1536x2048.jpg"},"placeholder_image":null,"sizes":{"5000":[1536,2048]},"default_photo":false,"cursor":null,"location":[40.755705,-73.99655333333334]},{"unique_id":"077BE6E7-DDA8-4F38-BB4A-DF0F4640C2D5","athlete_id":26462176,"activity_id":16181809559,"activity_name":"Run with Artyom","post_id":null,"resource_state":2,"caption":"","type":2,"source":1,"status":3,"uploaded_at":"2025-10-18T19:52:33Z","created_at":"2025-10-18T19:51:59Z","created_at_local":"2025-10-18T15:51:59Z","urls":{"5000":"https://d35tn3x5zm6xrc.cloudfront.net/LEpTlUieI8wxuKwsdTd_CAHz0i2BccppnSXEfVV5_p4/thumbnails/LEpTlUieI8wxuKwsdTd_CAHz0i2BccppnSXEfVV5_p4_1080x1920.jpg"},"placeholder_image":null,"sizes":{"5000":[1080,1920]},"default_photo":false,"cursor":null,"duration":4.0,"video_url":"https://d35tn3x5zm6xrc.cloudfront.net/LEpTlUieI8wxuKwsdTd_CAHz0i2BccppnSXEfVV5_p4/hls/LEpTlUieI8wxuKwsdTd_CAHz0i2BccppnSXEfVV5_p4.m3u8","location":[40.7557,-73.9966]}]' - recorded_at: Sun, 19 Oct 2025 16:33:09 GMT + recorded_at: Mon, 20 Oct 2025 17:00:51 GMT recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/strava/client/all_activity_comments_by_1.yml b/spec/fixtures/strava/client/all_activity_comments_by_1.yml new file mode 100644 index 0000000..1166742 --- /dev/null +++ b/spec/fixtures/strava/client/all_activity_comments_by_1.yml @@ -0,0 +1,302 @@ +--- +http_interactions: +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?page_size=1 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:07:40 GMT + X-Envoy-Upstream-Service-Time: + - '232' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '70,208' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - 76656e6a-9d20-4c6f-b54d-d64518872210 + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"abcc8d605273b9e6a025a088581ecf6d" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '70,208' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 08c35fba3c05c07f78b1292e4a5f949a.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - 4bqZ0-Y2hOQPzkScB7K8cXSPIni7ZhCdg55XPNoh8kx3Ao8CzBCDBw== + body: + encoding: UTF-8 + string: '[{"id":2257523290,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"you + pass?","mentions_metadata":null,"created_at":"2025-08-27T18:36:59Z","cursor":"eyJpZCI6MjI1NzUyMzI5MCwiY3JlYXRlZEF0IjoxNzU2MzE5ODE5MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Sasha","lastname":"C."},"reaction_count":0,"has_reacted":false}]' + recorded_at: Mon, 20 Oct 2025 16:07:40 GMT +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?after_cursor=eyJpZCI6MjI1NzUyMzI5MCwiY3JlYXRlZEF0IjoxNzU2MzE5ODE5MDAwfQ%253D%253D&page_size=1 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:07:40 GMT + X-Envoy-Upstream-Service-Time: + - '326' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '71,209' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - 01c1c788-253d-4077-b73b-f842cb33d87f + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"4f09c862d1a92b6c83505cf2be655f3d" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '71,209' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 876bec0443fc8f764d98d36e203f84e0.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - pNVOmM7dB2G5JrjCIiKiRXHrGD_I8kNnzr1zo8Ccz_7hhmVs3vw6Jw== + body: + encoding: UTF-8 + string: '[{"id":2257703241,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"Sasha + Caskey I will live another day!","mentions_metadata":null,"created_at":"2025-08-27T21:55:13Z","cursor":"eyJpZCI6MjI1NzcwMzI0MSwiY3JlYXRlZEF0IjoxNzU2MzMxNzEzMDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Daniel","lastname":"D."},"reaction_count":1,"has_reacted":false}]' + recorded_at: Mon, 20 Oct 2025 16:07:40 GMT +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?after_cursor=eyJpZCI6MjI1NzcwMzI0MSwiY3JlYXRlZEF0IjoxNzU2MzMxNzEzMDAwfQ%253D%253D&page_size=1 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:07:41 GMT + X-Envoy-Upstream-Service-Time: + - '312' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '72,210' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - c4fc60c2-5d88-4b87-bcce-bfaab96ea2a3 + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"3d7e5ff05291542b5fe10dd7f4d3b113" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '72,210' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 777bb716b31e5bbc92d320e733371d3a.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - qo0guP242nyMp1XXUhUBjmYhvkn9XV9xqqV-ahZTYTnndj2jY3-IZg== + body: + encoding: UTF-8 + string: '[{"id":2257782099,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"I + didn''t know they do competitive EKG Stress Test","mentions_metadata":null,"created_at":"2025-08-28T00:38:04Z","cursor":"eyJpZCI6MjI1Nzc4MjA5OSwiY3JlYXRlZEF0IjoxNzU2MzQxNDg0MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Vlad","lastname":"Z."},"reaction_count":0,"has_reacted":false}]' + recorded_at: Mon, 20 Oct 2025 16:07:41 GMT +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?after_cursor=eyJpZCI6MjI1Nzc4MjA5OSwiY3JlYXRlZEF0IjoxNzU2MzQxNDg0MDAwfQ%253D%253D&page_size=1 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:07:41 GMT + X-Envoy-Upstream-Service-Time: + - '86' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '73,211' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - 13311a1d-8e58-44ea-b80f-49e03ab68e3d + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"4f53cda18c2baa0c0354bb5f9a3ecbe5" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '73,211' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 8ca7450d970f904109dac7e068234b78.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - XFoXiQcfZ_AMAJ3AcXuxP0TKtqfO3Y9ky2xUMzUF2r12dcIpm2qEAw== + body: + encoding: UTF-8 + string: "[]" + recorded_at: Mon, 20 Oct 2025 16:07:41 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/strava/client/all_activity_comments_by_2.yml b/spec/fixtures/strava/client/all_activity_comments_by_2.yml new file mode 100644 index 0000000..b7c4041 --- /dev/null +++ b/spec/fixtures/strava/client/all_activity_comments_by_2.yml @@ -0,0 +1,154 @@ +--- +http_interactions: +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?page_size=2 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:06:43 GMT + X-Envoy-Upstream-Service-Time: + - '170' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '68,206' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - 4ef24f14-b727-4372-8881-da65ca5e46e2 + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"30aae7abb9e1b9801e05e9187f3e444b" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '68,206' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 8ca7450d970f904109dac7e068234b78.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - J-GzNVOPubBG-a42Ugdb2A-o5lp4xVvpdB4CJb-xtsdqdAZsDVk6_g== + body: + encoding: UTF-8 + string: '[{"id":2257523290,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"you + pass?","mentions_metadata":null,"created_at":"2025-08-27T18:36:59Z","cursor":"eyJpZCI6MjI1NzUyMzI5MCwiY3JlYXRlZEF0IjoxNzU2MzE5ODE5MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Sasha","lastname":"C."},"reaction_count":0,"has_reacted":false},{"id":2257703241,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"Sasha + Caskey I will live another day!","mentions_metadata":null,"created_at":"2025-08-27T21:55:13Z","cursor":"eyJpZCI6MjI1NzcwMzI0MSwiY3JlYXRlZEF0IjoxNzU2MzMxNzEzMDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Daniel","lastname":"D."},"reaction_count":1,"has_reacted":false}]' + recorded_at: Mon, 20 Oct 2025 16:06:43 GMT +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?after_cursor=eyJpZCI6MjI1NzcwMzI0MSwiY3JlYXRlZEF0IjoxNzU2MzMxNzEzMDAwfQ%253D%253D&page_size=2 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:06:43 GMT + X-Envoy-Upstream-Service-Time: + - '281' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '69,207' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - bdc3ac34-04d2-446b-8250-277549431dbf + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"3d7e5ff05291542b5fe10dd7f4d3b113" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '69,207' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 90707ba4ec932f1b72abfb5c4f1add2e.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - MtF7AmacrJfpprHu_e9r86unX09A828ZLPbuJyHN8brZO_Mk96h8Sw== + body: + encoding: UTF-8 + string: '[{"id":2257782099,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"I + didn''t know they do competitive EKG Stress Test","mentions_metadata":null,"created_at":"2025-08-28T00:38:04Z","cursor":"eyJpZCI6MjI1Nzc4MjA5OSwiY3JlYXRlZEF0IjoxNzU2MzQxNDg0MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Vlad","lastname":"Z."},"reaction_count":0,"has_reacted":false}]' + recorded_at: Mon, 20 Oct 2025 16:06:43 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/strava/client/all_activity_comments_by_3.yml b/spec/fixtures/strava/client/all_activity_comments_by_3.yml new file mode 100644 index 0000000..ab00670 --- /dev/null +++ b/spec/fixtures/strava/client/all_activity_comments_by_3.yml @@ -0,0 +1,154 @@ +--- +http_interactions: +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?page_size=3 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:07:41 GMT + X-Envoy-Upstream-Service-Time: + - '247' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '74,212' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - 7f84fc37-b544-4e0e-9100-11addaf74d75 + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"259744c12678c860f34011e9fe3f044d" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '74,212' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 90707ba4ec932f1b72abfb5c4f1add2e.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - Payk24CuL3pkZLJkhbZ1fx2YDc3Pb9B0lZB5tlp3aBoWBB2QwQZ0rg== + body: + encoding: UTF-8 + string: '[{"id":2257523290,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"you + pass?","mentions_metadata":null,"created_at":"2025-08-27T18:36:59Z","cursor":"eyJpZCI6MjI1NzUyMzI5MCwiY3JlYXRlZEF0IjoxNzU2MzE5ODE5MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Sasha","lastname":"C."},"reaction_count":0,"has_reacted":false},{"id":2257703241,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"Sasha + Caskey I will live another day!","mentions_metadata":null,"created_at":"2025-08-27T21:55:13Z","cursor":"eyJpZCI6MjI1NzcwMzI0MSwiY3JlYXRlZEF0IjoxNzU2MzMxNzEzMDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Daniel","lastname":"D."},"reaction_count":1,"has_reacted":false},{"id":2257782099,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"I + didn''t know they do competitive EKG Stress Test","mentions_metadata":null,"created_at":"2025-08-28T00:38:04Z","cursor":"eyJpZCI6MjI1Nzc4MjA5OSwiY3JlYXRlZEF0IjoxNzU2MzQxNDg0MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Vlad","lastname":"Z."},"reaction_count":0,"has_reacted":false}]' + recorded_at: Mon, 20 Oct 2025 16:07:41 GMT +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?after_cursor=eyJpZCI6MjI1Nzc4MjA5OSwiY3JlYXRlZEF0IjoxNzU2MzQxNDg0MDAwfQ%253D%253D&page_size=3 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:07:42 GMT + X-Envoy-Upstream-Service-Time: + - '256' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '75,213' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - 6a3e9c51-34c9-45e4-9209-4bc3dfcf6d71 + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"4f53cda18c2baa0c0354bb5f9a3ecbe5" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '75,213' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 def26d054ec95b961e8352e3cd4fae7e.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - yFTJjFzguXdbqqHs7_GPGRUQH_aIY32K058znPCT3JPYD63Q-jmtog== + body: + encoding: UTF-8 + string: "[]" + recorded_at: Mon, 20 Oct 2025 16:07:42 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/strava/client/all_activity_comments_by_5.yml b/spec/fixtures/strava/client/all_activity_comments_by_5.yml new file mode 100644 index 0000000..0e4644f --- /dev/null +++ b/spec/fixtures/strava/client/all_activity_comments_by_5.yml @@ -0,0 +1,80 @@ +--- +http_interactions: +- request: + method: get + uri: https://www.strava.com/api/v3/activities/15605032352/comments?page_size=5 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/3.0.0 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 20 Oct 2025 16:07:42 GMT + X-Envoy-Upstream-Service-Time: + - '189' + Server: + - istio-envoy + Status: + - 200 OK + X-Ratelimit-Usage: + - '76,214' + X-Ratelimit-Limit: + - '200,2000' + Vary: + - Accept, Origin + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - 1; mode=block + X-Request-Id: + - f60985b8-29f5-4aaf-abf4-628f2c3b5820 + X-Readratelimit-Limit: + - '100,1000' + X-Download-Options: + - noopen + Etag: + - W/"259744c12678c860f34011e9fe3f044d" + X-Frame-Options: + - DENY + X-Readratelimit-Usage: + - '76,214' + X-Content-Type-Options: + - nosniff + X-Cache: + - Miss from cloudfront + Via: + - 1.1 7cd7ee430e44b1f51cd2016b916ffa92.cloudfront.net (CloudFront) + X-Amz-Cf-Pop: + - JFK52-P3 + X-Amz-Cf-Id: + - Tfac0eK4ea-mUiSQiJd1CxoZcqC60ppTtij1tA4ERxz_Xg8Bzdt4rQ== + body: + encoding: UTF-8 + string: '[{"id":2257523290,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"you + pass?","mentions_metadata":null,"created_at":"2025-08-27T18:36:59Z","cursor":"eyJpZCI6MjI1NzUyMzI5MCwiY3JlYXRlZEF0IjoxNzU2MzE5ODE5MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Sasha","lastname":"C."},"reaction_count":0,"has_reacted":false},{"id":2257703241,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"Sasha + Caskey I will live another day!","mentions_metadata":null,"created_at":"2025-08-27T21:55:13Z","cursor":"eyJpZCI6MjI1NzcwMzI0MSwiY3JlYXRlZEF0IjoxNzU2MzMxNzEzMDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Daniel","lastname":"D."},"reaction_count":1,"has_reacted":false},{"id":2257782099,"activity_id":15605032352,"post_id":null,"resource_state":2,"text":"I + didn''t know they do competitive EKG Stress Test","mentions_metadata":null,"created_at":"2025-08-28T00:38:04Z","cursor":"eyJpZCI6MjI1Nzc4MjA5OSwiY3JlYXRlZEF0IjoxNzU2MzQxNDg0MDAwfQ%3D%3D","athlete":{"resource_state":2,"firstname":"Vlad","lastname":"Z."},"reaction_count":0,"has_reacted":false}]' + recorded_at: Mon, 20 Oct 2025 16:07:42 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/strava/api/client/endpoints/activities/activity_comments_spec.rb b/spec/strava/api/client/endpoints/activities/activity_comments_spec.rb index 8f6a3d4..064396d 100644 --- a/spec/strava/api/client/endpoints/activities/activity_comments_spec.rb +++ b/spec/strava/api/client/endpoints/activities/activity_comments_spec.rb @@ -2,49 +2,124 @@ require 'spec_helper' -RSpec.describe 'Strava::Api::Client#activity_comments', vcr: { cassette_name: 'client/activity_comments' } do +RSpec.describe 'Strava::Api::Client#activity_comments' do include_context 'with API client' - let(:activity_comments) do - client.activity_comments(id: 15_605_032_352) + context 'with top comments', vcr: { cassette_name: 'client/activity_comments' } do + let(:activity_comments) do + client.activity_comments(id: 15_605_032_352) + end + + let(:first_activity_comment) do + activity_comments.first + end + + it 'returns activity comments' do + expect(activity_comments).to be_a Enumerable + expect(activity_comments.count).to eq 3 + activity_comment = activity_comments.first + expect(activity_comment.id).to eq 2_257_523_290 + expect(activity_comment.activity_id).to eq 15_605_032_352 + expect(activity_comment.resource_state).to eq 2 + expect(activity_comment.text).to eq 'you pass?' + expect(activity_comment.created_at).to eq Time.parse('2025-08-27 18:36:59 UTC') + athlete = activity_comment.athlete + expect(athlete).to be_a Strava::Models::SummaryAthlete + expect(athlete.resource_state).to eq 2 + expect(athlete.firstname).to eq 'Sasha' + expect(athlete.lastname).to eq 'C.' + end + + it 'returns activity comments by id' do + expect(activity_comments).to be_a Enumerable + expect(activity_comments.count).to eq 3 + expect(activity_comments.map(&:id).uniq.size).to eq 3 + end + + it 'returns ratelimits with paginated comments' do + expect(activity_comments.http_response).to be_a Strava::Web::ApiResponse + expect(activity_comments.http_response.ratelimit).to be_a Strava::Api::Ratelimit + expect(activity_comments.http_response.ratelimit.fifteen_minutes).to eq 200 + expect(activity_comments.http_response.ratelimit.fifteen_minutes_usage).to eq 1 + end + + it 'returns ratelimits with each comment' do + expect(first_activity_comment.http_response).to be_a Strava::Web::ApiResponse + expect(first_activity_comment.http_response.ratelimit).to be_a Strava::Api::Ratelimit + expect(first_activity_comment.http_response.ratelimit.fifteen_minutes).to eq 200 + expect(first_activity_comment.http_response.ratelimit.fifteen_minutes_usage).to eq 1 + end + end + + [1, 2, 3, 5].each do |page_size| + context "with paginated comments by #{page_size}", vcr: { cassette_name: "client/all_activity_comments_by_#{page_size}" } do + let(:activity_comments) { client.activity_comments(id: 15_605_032_352, page_size: page_size) } + + it 'returns all activity comments' do + expect(activity_comments).to be_a Enumerable + expect(activity_comments.count).to eq 3 + expect(activity_comments.map(&:id).uniq.size).to eq 3 + end + end end - let(:first_activity_comment) do - activity_comments.first + context 'with a limit smaller than the number of results', vcr: { cassette_name: 'client/activity_comments_by_2' } do + let(:activity_comments) { client.activity_comments(id: 15_605_032_352, limit: 2) } + + it 'returns 2 activity comments' do + expect(activity_comments).to be_a Enumerable + expect(activity_comments.count).to eq 2 + expect(activity_comments.map(&:id).uniq.size).to eq 2 + end + end + + context 'with a limit smaller than per_page', vcr: { cassette_name: 'client/activity_comments_per_page_3_by_2' } do + let(:activity_comments) { client.activity_comments(id: 15_605_032_352, per_page: 3, limit: 2) } + + it 'returns 2 activity comments' do + expect(activity_comments).to be_a Enumerable + expect(activity_comments.count).to eq 2 + expect(activity_comments.map(&:id).uniq.size).to eq 2 + end end - it 'returns activity comments' do - expect(activity_comments).to be_a Enumerable - expect(activity_comments.count).to eq 3 - activity_comment = activity_comments.first - expect(activity_comment.id).to eq 2_257_523_290 - expect(activity_comment.activity_id).to eq 15_605_032_352 - expect(activity_comment.resource_state).to eq 2 - expect(activity_comment.text).to eq 'you pass?' - expect(activity_comment.created_at).to eq Time.parse('2025-08-27 18:36:59 UTC') - athlete = activity_comment.athlete - expect(athlete).to be_a Strava::Models::SummaryAthlete - expect(athlete.resource_state).to eq 2 - expect(athlete.firstname).to eq 'Sasha' - expect(athlete.lastname).to eq 'C.' + context 'with a limit smaller than page_size', vcr: { cassette_name: 'client/activity_comments_page_size_3_by_2' } do + let(:activity_comments) { client.activity_comments(id: 15_605_032_352, page_size: 3, limit: 2) } + + it 'returns 2 activity comments' do + expect(activity_comments).to be_a Enumerable + expect(activity_comments.count).to eq 2 + expect(activity_comments.map(&:id).uniq.size).to eq 2 + end end - it 'returns activity comments by id' do - expect(activity_comments).to be_a Enumerable - expect(activity_comments.count).to eq 3 + context 'with a limit equal to the number of results', vcr: { cassette_name: 'client/activity_comments_by_3' } do + let(:activity_comments) { client.activity_comments(id: 15_605_032_352, limit: 3) } + + it 'returns 2 activity comments' do + expect(activity_comments).to be_a Enumerable + expect(activity_comments.count).to eq 3 + expect(activity_comments.map(&:id).uniq.size).to eq 3 + end end - it 'returns ratelimits with paginated comments' do - expect(activity_comments.http_response).to be_a Strava::Web::ApiResponse - expect(activity_comments.http_response.ratelimit).to be_a Strava::Api::Ratelimit - expect(activity_comments.http_response.ratelimit.fifteen_minutes).to eq 200 - expect(activity_comments.http_response.ratelimit.fifteen_minutes_usage).to eq 1 + context 'with both a limit and a page size', vcr: { cassette_name: 'client/activity_comments_by_1_limit_2' } do + let(:activity_comments) { client.activity_comments(id: 15_605_032_352, page_size: 1, limit: 2) } + + it 'returns 2 activity comments' do + expect(activity_comments).to be_a Enumerable + expect(activity_comments.count).to eq 2 + expect(activity_comments.map(&:id).uniq.size).to eq 2 + end end - it 'returns ratelimits with each comment' do - expect(first_activity_comment.http_response).to be_a Strava::Web::ApiResponse - expect(first_activity_comment.http_response.ratelimit).to be_a Strava::Api::Ratelimit - expect(first_activity_comment.http_response.ratelimit.fifteen_minutes).to eq 200 - expect(first_activity_comment.http_response.ratelimit.fifteen_minutes_usage).to eq 1 + context 'with a limit larger than the number of results', vcr: { cassette_name: 'client/activity_comments_by_10' } do + let(:activity_comments) { client.activity_comments(id: 15_605_032_352, limit: 10) } + + it 'returns all activity comments' do + expect(activity_comments).to be_a Enumerable + expect(activity_comments.count).to eq 3 + expect(activity_comments.map(&:id).uniq.size).to eq 3 + end end end