feat: try sync calories
This commit is contained in:
parent
3d240eca81
commit
75b2743e75
|
|
@ -5,6 +5,7 @@ namespace App\Http\Controllers;
|
||||||
use App\Enums\WorkoutSource;
|
use App\Enums\WorkoutSource;
|
||||||
use App\Http\Requests\StravaCallbackRequest;
|
use App\Http\Requests\StravaCallbackRequest;
|
||||||
use App\Http\Requests\StravaSyncRequest;
|
use App\Http\Requests\StravaSyncRequest;
|
||||||
|
use App\Models\StravaConnection;
|
||||||
use App\Models\WorkoutSessions;
|
use App\Models\WorkoutSessions;
|
||||||
use App\Services\StravaClient;
|
use App\Services\StravaClient;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
|
|
@ -111,11 +112,13 @@ class StravaController extends Controller
|
||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
$activities = $strava->activities(
|
$activities = collect($strava->activities(
|
||||||
$connection,
|
$connection,
|
||||||
$request->integer('page', 1),
|
$request->integer('page', 1),
|
||||||
$request->integer('per_page', 30),
|
$request->integer('per_page', 30),
|
||||||
);
|
))
|
||||||
|
->map(fn (array $activity): array => $this->activityForStorage($strava, $connection, $activity))
|
||||||
|
->all();
|
||||||
|
|
||||||
$importedCount = collect($activities)
|
$importedCount = collect($activities)
|
||||||
->map(fn (array $activity): WorkoutSessions => $this->storeActivity($request, $activity))
|
->map(fn (array $activity): WorkoutSessions => $this->storeActivity($request, $activity))
|
||||||
|
|
@ -144,6 +147,15 @@ class StravaController extends Controller
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function activityForStorage(StravaClient $strava, StravaConnection $connection, array $activity): array
|
||||||
|
{
|
||||||
|
if (array_key_exists('calories', $activity) || empty($activity['id'])) {
|
||||||
|
return $activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_replace($activity, $strava->activity($connection, $activity['id']));
|
||||||
|
}
|
||||||
|
|
||||||
private function canReadActivities(array $scopes): bool
|
private function canReadActivities(array $scopes): bool
|
||||||
{
|
{
|
||||||
return in_array('activity:read', $scopes, true)
|
return in_array('activity:read', $scopes, true)
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,20 @@ class StravaClient
|
||||||
->json();
|
->json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function activity(StravaConnection $connection, int|string $activityId): array
|
||||||
|
{
|
||||||
|
return Http::baseUrl('https://www.strava.com/api/v3')
|
||||||
|
->withToken($this->accessToken($connection))
|
||||||
|
->timeout(10)
|
||||||
|
->connectTimeout(3)
|
||||||
|
->retry([100, 500, 1000])
|
||||||
|
->get("/activities/{$activityId}", [
|
||||||
|
'include_all_efforts' => false,
|
||||||
|
])
|
||||||
|
->throw()
|
||||||
|
->json();
|
||||||
|
}
|
||||||
|
|
||||||
private function accessToken(StravaConnection $connection): string
|
private function accessToken(StravaConnection $connection): string
|
||||||
{
|
{
|
||||||
if (! $connection->expires_at || $connection->expires_at->lte(now()->addMinutes(5))) {
|
if (! $connection->expires_at || $connection->expires_at->lte(now()->addMinutes(5))) {
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,57 @@ it('syncs strava activities into workouts', function () {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('syncs strava calories from detailed activities when summaries omit them', function () {
|
||||||
|
Http::preventStrayRequests();
|
||||||
|
Http::fake([
|
||||||
|
'https://www.strava.com/api/v3/athlete/activities*' => Http::response([
|
||||||
|
[
|
||||||
|
'id' => 1122334455,
|
||||||
|
'name' => 'Evening ride',
|
||||||
|
'sport_type' => 'Ride',
|
||||||
|
'moving_time' => 2410,
|
||||||
|
'distance' => 20500.2,
|
||||||
|
'start_date' => '2026-05-19T17:45:00Z',
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
'https://www.strava.com/api/v3/activities/1122334455*' => Http::response([
|
||||||
|
'id' => 1122334455,
|
||||||
|
'name' => 'Evening ride',
|
||||||
|
'sport_type' => 'Ride',
|
||||||
|
'moving_time' => 2410,
|
||||||
|
'calories' => 618.4,
|
||||||
|
'distance' => 20500.2,
|
||||||
|
'start_date' => '2026-05-19T17:45:00Z',
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$user = User::factory()->create();
|
||||||
|
StravaConnection::create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'athlete_id' => 123456,
|
||||||
|
'access_token' => 'access-token',
|
||||||
|
'refresh_token' => 'refresh-token',
|
||||||
|
'expires_at' => now()->addHour(),
|
||||||
|
'scopes' => ['read', 'activity:read'],
|
||||||
|
'athlete' => ['id' => 123456],
|
||||||
|
]);
|
||||||
|
|
||||||
|
Sanctum::actingAs($user);
|
||||||
|
|
||||||
|
$this
|
||||||
|
->postJson('/api/strava/sync')
|
||||||
|
->assertOk()
|
||||||
|
->assertJsonPath('data.importedCount', 1)
|
||||||
|
->assertJsonPath('data.fetchedCount', 1);
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('workout_sessions', [
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'source' => WorkoutSource::STRAVA->value,
|
||||||
|
'external_id' => '1122334455',
|
||||||
|
'calories_burned' => 618,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('requires an activity read scope before syncing strava activities', function () {
|
it('requires an activity read scope before syncing strava activities', function () {
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
StravaConnection::create([
|
StravaConnection::create([
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue