From 840e87fed07455f26283b4dad433103a4b2a3317 Mon Sep 17 00:00:00 2001 From: Alex Justesen Date: Wed, 15 Jan 2025 21:43:40 -0500 Subject: [PATCH] [Feature] Stats API endpoint (#1994) --- app/Helpers/Bitrate.php | 2 +- app/Http/Controllers/Api/V1/ApiController.php | 3 +- app/Http/Controllers/Api/V1/Stats.php | 41 ++++++++++++++++ app/Http/Resources/V1/StatResource.php | 49 +++++++++++++++++++ routes/api/v1/routes.php | 4 ++ 5 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 app/Http/Controllers/Api/V1/Stats.php create mode 100644 app/Http/Resources/V1/StatResource.php diff --git a/app/Helpers/Bitrate.php b/app/Helpers/Bitrate.php index 307df3cc..ba646cad 100644 --- a/app/Helpers/Bitrate.php +++ b/app/Helpers/Bitrate.php @@ -32,7 +32,7 @@ class Bitrate } // 1 byte = 8 bits - return $bytes * 8; + return round($bytes * 8); } /** diff --git a/app/Http/Controllers/Api/V1/ApiController.php b/app/Http/Controllers/Api/V1/ApiController.php index 00637c38..7c77c242 100644 --- a/app/Http/Controllers/Api/V1/ApiController.php +++ b/app/Http/Controllers/Api/V1/ApiController.php @@ -17,10 +17,11 @@ abstract class ApiController * @param int $code * @return \Illuminate\Http\JsonResponse */ - public static function sendResponse($data, $message = 'ok', $code = 200) + public static function sendResponse($data, $filters = [], $message = 'ok', $code = 200) { $response = array_filter([ 'data' => $data, + 'filters' => $filters, 'message' => $message, ]); diff --git a/app/Http/Controllers/Api/V1/Stats.php b/app/Http/Controllers/Api/V1/Stats.php new file mode 100644 index 00000000..dcf3691e --- /dev/null +++ b/app/Http/Controllers/Api/V1/Stats.php @@ -0,0 +1,41 @@ +selectRaw('count(*) as total_results') + ->selectRaw('avg(ping) as avg_ping') + ->selectRaw('avg(download) as avg_download') + ->selectRaw('avg(upload) as avg_upload') + ->selectRaw('min(ping) as min_ping') + ->selectRaw('min(download) as min_download') + ->selectRaw('min(upload) as min_upload') + ->selectRaw('max(ping) as max_ping') + ->selectRaw('max(download) as max_download') + ->selectRaw('max(upload) as max_upload') + ->AllowedFilters([ + AllowedFilter::operator(name: 'start_at', internalName: 'created_at', filterOperator: FilterOperator::DYNAMIC), + AllowedFilter::operator(name: 'end_at', internalName: 'created_at', filterOperator: FilterOperator::DYNAMIC), + ]) + ->first(); + + return self::sendResponse( + data: new StatResource($stats), + filters: $request->input('filter'), + ); + } +} diff --git a/app/Http/Resources/V1/StatResource.php b/app/Http/Resources/V1/StatResource.php new file mode 100644 index 00000000..8b8033ee --- /dev/null +++ b/app/Http/Resources/V1/StatResource.php @@ -0,0 +1,49 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'ping' => [ + 'avg' => round($this->avg_ping, 2), + 'min' => round($this->min_ping, 2), + 'max' => round($this->max_ping, 2), + ], + 'download' => [ + 'avg' => round($this->avg_download), + 'avg_bits' => $this->when($this->avg_download, fn (): int|float => Bitrate::bytesToBits($this->avg_download)), + 'avg_bits_human' => $this->when($this->avg_download, fn (): string => Bitrate::formatBits(Bitrate::bytesToBits($this->avg_download)).'ps'), + 'min' => round($this->min_download), + 'min_bits' => $this->when($this->min_download, fn (): int|float => Bitrate::bytesToBits($this->min_download)), + 'min_bits_human' => $this->when($this->min_download, fn (): string => Bitrate::formatBits(Bitrate::bytesToBits($this->min_download)).'ps'), + 'max' => round($this->max_download), + 'max_bits' => $this->when($this->max_download, fn (): int|float => Bitrate::bytesToBits($this->max_download)), + 'max_bits_human' => $this->when($this->max_download, fn (): string => Bitrate::formatBits(Bitrate::bytesToBits($this->max_download)).'ps'), + ], + 'upload' => [ + 'avg' => round($this->avg_upload), + 'avg_bits' => $this->when($this->avg_upload, fn (): int|float => Bitrate::bytesToBits($this->avg_upload)), + 'avg_bits_human' => $this->when($this->avg_upload, fn (): string => Bitrate::formatBits(Bitrate::bytesToBits($this->avg_upload)).'ps'), + 'min' => round($this->min_upload), + 'min_bits' => $this->when($this->min_upload, fn (): int|float => Bitrate::bytesToBits($this->min_upload)), + 'min_bits_human' => $this->when($this->min_upload, fn (): string => Bitrate::formatBits(Bitrate::bytesToBits($this->min_upload)).'ps'), + 'max' => round($this->max_upload), + 'max_bits' => $this->when($this->max_upload, fn (): int|float => Bitrate::bytesToBits($this->max_upload)), + 'max_bits_human' => $this->when($this->max_upload, fn (): string => Bitrate::formatBits(Bitrate::bytesToBits($this->max_upload)).'ps'), + ], + 'total_results' => $this->total_results, + ]; + } +} diff --git a/routes/api/v1/routes.php b/routes/api/v1/routes.php index b9609c4b..f553176c 100644 --- a/routes/api/v1/routes.php +++ b/routes/api/v1/routes.php @@ -3,6 +3,7 @@ use App\Http\Controllers\Api\V1\LatestResult; use App\Http\Controllers\Api\V1\ListResults; use App\Http\Controllers\Api\V1\ShowResult; +use App\Http\Controllers\Api\V1\Stats; use Illuminate\Support\Facades\Route; Route::prefix('v1')->name('api.v1.')->group(function () { @@ -14,4 +15,7 @@ Route::prefix('v1')->name('api.v1.')->group(function () { Route::get('/results/{result}', ShowResult::class) ->name('results.show'); + + Route::get('/stats', Stats::class) + ->name('stats'); });