Skip to content

Commit 840e87f

Browse files
authored
[Feature] Stats API endpoint (alexjustesen#1994)
1 parent e1efde6 commit 840e87f

File tree

5 files changed

+97
-2
lines changed

5 files changed

+97
-2
lines changed

app/Helpers/Bitrate.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public static function bytesToBits(int|float $bytes): int|float
3232
}
3333

3434
// 1 byte = 8 bits
35-
return $bytes * 8;
35+
return round($bytes * 8);
3636
}
3737

3838
/**

app/Http/Controllers/Api/V1/ApiController.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ abstract class ApiController
1717
* @param int $code
1818
* @return \Illuminate\Http\JsonResponse
1919
*/
20-
public static function sendResponse($data, $message = 'ok', $code = 200)
20+
public static function sendResponse($data, $filters = [], $message = 'ok', $code = 200)
2121
{
2222
$response = array_filter([
2323
'data' => $data,
24+
'filters' => $filters,
2425
'message' => $message,
2526
]);
2627

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace App\Http\Controllers\Api\V1;
4+
5+
use App\Http\Resources\V1\StatResource;
6+
use App\Models\Result;
7+
use Illuminate\Http\Request;
8+
use Spatie\QueryBuilder\AllowedFilter;
9+
use Spatie\QueryBuilder\Enums\FilterOperator;
10+
use Spatie\QueryBuilder\QueryBuilder;
11+
12+
class Stats extends ApiController
13+
{
14+
/**
15+
* Handle the incoming request.
16+
*/
17+
public function __invoke(Request $request)
18+
{
19+
$stats = QueryBuilder::for(Result::class)
20+
->selectRaw('count(*) as total_results')
21+
->selectRaw('avg(ping) as avg_ping')
22+
->selectRaw('avg(download) as avg_download')
23+
->selectRaw('avg(upload) as avg_upload')
24+
->selectRaw('min(ping) as min_ping')
25+
->selectRaw('min(download) as min_download')
26+
->selectRaw('min(upload) as min_upload')
27+
->selectRaw('max(ping) as max_ping')
28+
->selectRaw('max(download) as max_download')
29+
->selectRaw('max(upload) as max_upload')
30+
->AllowedFilters([
31+
AllowedFilter::operator(name: 'start_at', internalName: 'created_at', filterOperator: FilterOperator::DYNAMIC),
32+
AllowedFilter::operator(name: 'end_at', internalName: 'created_at', filterOperator: FilterOperator::DYNAMIC),
33+
])
34+
->first();
35+
36+
return self::sendResponse(
37+
data: new StatResource($stats),
38+
filters: $request->input('filter'),
39+
);
40+
}
41+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
namespace App\Http\Resources\V1;
4+
5+
use App\Helpers\Bitrate;
6+
use Illuminate\Http\Request;
7+
use Illuminate\Http\Resources\Json\JsonResource;
8+
9+
class StatResource extends JsonResource
10+
{
11+
/**
12+
* Transform the resource into an array.
13+
*
14+
* @return array<string, mixed>
15+
*/
16+
public function toArray(Request $request): array
17+
{
18+
return [
19+
'ping' => [
20+
'avg' => round($this->avg_ping, 2),
21+
'min' => round($this->min_ping, 2),
22+
'max' => round($this->max_ping, 2),
23+
],
24+
'download' => [
25+
'avg' => round($this->avg_download),
26+
'avg_bits' => $this->when($this->avg_download, fn (): int|float => Bitrate::bytesToBits($this->avg_download)),
27+
'avg_bits_human' => $this->when($this->avg_download, fn (): string => Bitrate::formatBits(Bitrate::bytesToBits($this->avg_download)).'ps'),
28+
'min' => round($this->min_download),
29+
'min_bits' => $this->when($this->min_download, fn (): int|float => Bitrate::bytesToBits($this->min_download)),
30+
'min_bits_human' => $this->when($this->min_download, fn (): string => Bitrate::formatBits(Bitrate::bytesToBits($this->min_download)).'ps'),
31+
'max' => round($this->max_download),
32+
'max_bits' => $this->when($this->max_download, fn (): int|float => Bitrate::bytesToBits($this->max_download)),
33+
'max_bits_human' => $this->when($this->max_download, fn (): string => Bitrate::formatBits(Bitrate::bytesToBits($this->max_download)).'ps'),
34+
],
35+
'upload' => [
36+
'avg' => round($this->avg_upload),
37+
'avg_bits' => $this->when($this->avg_upload, fn (): int|float => Bitrate::bytesToBits($this->avg_upload)),
38+
'avg_bits_human' => $this->when($this->avg_upload, fn (): string => Bitrate::formatBits(Bitrate::bytesToBits($this->avg_upload)).'ps'),
39+
'min' => round($this->min_upload),
40+
'min_bits' => $this->when($this->min_upload, fn (): int|float => Bitrate::bytesToBits($this->min_upload)),
41+
'min_bits_human' => $this->when($this->min_upload, fn (): string => Bitrate::formatBits(Bitrate::bytesToBits($this->min_upload)).'ps'),
42+
'max' => round($this->max_upload),
43+
'max_bits' => $this->when($this->max_upload, fn (): int|float => Bitrate::bytesToBits($this->max_upload)),
44+
'max_bits_human' => $this->when($this->max_upload, fn (): string => Bitrate::formatBits(Bitrate::bytesToBits($this->max_upload)).'ps'),
45+
],
46+
'total_results' => $this->total_results,
47+
];
48+
}
49+
}

routes/api/v1/routes.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use App\Http\Controllers\Api\V1\LatestResult;
44
use App\Http\Controllers\Api\V1\ListResults;
55
use App\Http\Controllers\Api\V1\ShowResult;
6+
use App\Http\Controllers\Api\V1\Stats;
67
use Illuminate\Support\Facades\Route;
78

89
Route::prefix('v1')->name('api.v1.')->group(function () {
@@ -14,4 +15,7 @@
1415

1516
Route::get('/results/{result}', ShowResult::class)
1617
->name('results.show');
18+
19+
Route::get('/stats', Stats::class)
20+
->name('stats');
1721
});

0 commit comments

Comments
 (0)