Skip to content

Commit e2362e8

Browse files
authored
Files (#112)
* FileInfo endpoint added * FetchFile content * More niceness * Up to categories . . * Project Type * ProjectType * Cleanup and apps list * app info + files * Get me a file
1 parent a6069e1 commit e2362e8

30 files changed

+761
-69
lines changed

.env.testing

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ APP_URL=http://localhost
88
DB_CONNECTION=mysql
99
DB_HOST=127.0.0.1
1010
DB_PORT=3306
11-
DB_DATABASE=badgeware_test
11+
DB_DATABASE=hatchery
1212
DB_USERNAME=root
13-
DB_PASSWORD=
13+
DB_PASSWORD=root
1414

1515
BROADCAST_DRIVER=log
1616
CACHE_DRIVER=file
+333
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Http\Controllers;
6+
7+
use App\Models\Badge;
8+
use App\Models\Category;
9+
use App\Models\File;
10+
use App\Models\Project;
11+
use App\Models\Version;
12+
use Illuminate\Http\JsonResponse;
13+
use Illuminate\Http\Response;
14+
use OpenApi\Annotations as OA;
15+
16+
class MchController extends Controller
17+
{
18+
/**
19+
* List the available devices.
20+
*
21+
* @OA\Get(
22+
* path="/mch2022/devices",
23+
* @OA\Response(response="default",ref="#/components/responses/undocumented")
24+
* )
25+
*
26+
* @return JsonResponse
27+
*/
28+
public function devices(): JsonResponse
29+
{
30+
$devices = [];
31+
foreach (Badge::pluck('name', 'slug') as $slug => $name) {
32+
$devices[] = [
33+
'slug' => $slug,
34+
'name' => $name
35+
];
36+
}
37+
return response()->json(
38+
$devices,
39+
200,
40+
['Content-Type' => 'application/json'],
41+
JSON_UNESCAPED_SLASHES
42+
);
43+
}
44+
45+
/**
46+
* Get the types of apps a device supports.
47+
*
48+
* @OA\Get(
49+
* path="/mch2022/{device}/types",
50+
* @OA\Parameter(
51+
* name="device",
52+
* in="path",
53+
* required=true,
54+
* @OA\Schema(type="string", format="slug", example="mch2022")
55+
* ),
56+
* tags={"MCH2022"},
57+
* @OA\Response(response="default",ref="#/components/responses/undocumented")
58+
* )
59+
*
60+
* @param string $device
61+
* @return JsonResponse
62+
*/
63+
public function types(string $device): JsonResponse
64+
{
65+
/** @var Badge $badge */
66+
$badge = Badge::whereSlug($device)->firstOrFail();
67+
return response()->json($badge->types, 200, ['Content-Type' => 'application/json'], JSON_UNESCAPED_SLASHES);
68+
}
69+
70+
/**
71+
* Get the types of apps a device supports.
72+
*
73+
* @OA\Get(
74+
* path="/mch2022/{device}/{type}/categories",
75+
* @OA\Parameter(
76+
* name="device",
77+
* in="path",
78+
* required=true,
79+
* @OA\Schema(type="string", format="slug", example="mch2022")
80+
* ),
81+
* @OA\Parameter(
82+
* name="type",
83+
* in="path",
84+
* required=true,
85+
* @OA\Schema(type="string", format="slug", example="esp32")
86+
* ),
87+
* tags={"MCH2022"},
88+
* @OA\Response(response="default",ref="#/components/responses/undocumented")
89+
* )
90+
*
91+
* @param string $device
92+
* @param string $type
93+
* @return JsonResponse
94+
*/
95+
public function categories(string $device, string $type): JsonResponse
96+
{
97+
/** @var Badge $badge */
98+
$badge = Badge::whereSlug($device)->firstOrFail();
99+
100+
$count = $categories = [];
101+
/** @var Project $project */
102+
foreach ($badge->projects()->whereProjectType($type)->get() as $project) {
103+
$count[$project->category_id] =
104+
isset($count[$project->category_id]) ? $count[$project->category_id] + 1 : 1;
105+
}
106+
foreach ($count as $id => $apps) {
107+
/** @var Category $category */
108+
$category = Category::find($id);
109+
$categories[] = [
110+
'name' => $category->name,
111+
'slug' => $category->slug,
112+
'apps' => $apps,
113+
];
114+
}
115+
116+
return response()->json($categories, 200, ['Content-Type' => 'application/json'], JSON_UNESCAPED_SLASHES);
117+
}
118+
119+
/**
120+
* Get the apps from a device / type / category
121+
*
122+
* @OA\Get(
123+
* path="/mch2022/{device}/{type}/{category}",
124+
* @OA\Parameter(
125+
* name="device",
126+
* in="path",
127+
* required=true,
128+
* @OA\Schema(type="string", format="slug", example="mch2022")
129+
* ),
130+
* @OA\Parameter(
131+
* name="type",
132+
* in="path",
133+
* required=true,
134+
* @OA\Schema(type="string", format="slug", example="esp32")
135+
* ),
136+
* @OA\Parameter(
137+
* name="category",
138+
* in="path",
139+
* required=true,
140+
* @OA\Schema(type="string", format="slug", example="fun")
141+
* ),
142+
* tags={"MCH2022"},
143+
* @OA\Response(response="default",ref="#/components/responses/undocumented")
144+
* )
145+
*
146+
* @param string $device
147+
* @param string $type
148+
* @param string $category
149+
* @return JsonResponse
150+
*/
151+
public function apps(string $device, string $type, string $category): JsonResponse
152+
{
153+
/** @var Badge $badge */
154+
$badge = Badge::whereSlug($device)->firstOrFail();
155+
/** @var Category $category */
156+
$categoryId = Category::whereSlug($category)->firstOrFail()->id;
157+
$apps = [];
158+
/** @var Project $project */
159+
foreach ($badge->projects()->whereProjectType($type)->whereCategoryId($categoryId)->get() as $project) {
160+
$apps[] = [
161+
'slug' => $project->slug,
162+
'name' => $project->name,
163+
'author' => $project->author,
164+
'license' => $project->license,
165+
'description' => $project->description,
166+
];
167+
}
168+
return response()->json($apps, 200, ['Content-Type' => 'application/json'], JSON_UNESCAPED_SLASHES);
169+
}
170+
171+
172+
/**
173+
* Get the apps from a device / type / category
174+
*
175+
* @OA\Get(
176+
* path="/mch2022/{device}/{type}/{category}/{app}",
177+
* @OA\Parameter(
178+
* name="device",
179+
* in="path",
180+
* required=true,
181+
* @OA\Schema(type="string", format="slug", example="mch2022")
182+
* ),
183+
* @OA\Parameter(
184+
* name="type",
185+
* in="path",
186+
* required=true,
187+
* @OA\Schema(type="string", format="slug", example="esp32")
188+
* ),
189+
* @OA\Parameter(
190+
* name="category",
191+
* in="path",
192+
* required=true,
193+
* @OA\Schema(type="string", format="slug", example="fun")
194+
* ),
195+
* @OA\Parameter(
196+
* name="app",
197+
* in="path",
198+
* required=true,
199+
* @OA\Schema(type="string", format="slug", example="game_of_life")
200+
* ),
201+
* tags={"MCH2022"},
202+
* @OA\Response(response="default",ref="#/components/responses/undocumented")
203+
* )
204+
*
205+
* @param string $device
206+
* @param string $type
207+
* @param string $category
208+
* @param string $app
209+
* @return JsonResponse
210+
*/
211+
public function app(string $device, string $type, string $category, string $app): JsonResponse
212+
{
213+
/** @var Badge $badge */
214+
$badge = Badge::whereSlug($device)->firstOrFail();
215+
$categoryId = Category::whereSlug($category)->firstOrFail()->id;
216+
/** @var Project $project */
217+
$project = $badge->projects()
218+
->whereProjectType($type)->whereCategoryId($categoryId)->whereSlug($app)->firstOrFail();
219+
220+
/** @var Version $version */
221+
$version = $project->versions()->published()->get()->last();
222+
$files = [];
223+
/** @var File $file */
224+
foreach ($version->files as $file) {
225+
$fileData = new \stdClass();
226+
$fileData->name = $file->name;
227+
$fileData->url = route('mch.file', [
228+
'device' => $badge->slug,
229+
'type' => $project->project_type,
230+
'category' => $category,
231+
'app' => $project->slug,
232+
'file' => $file->name
233+
]);
234+
$fileData->size = $file->size_of_content;
235+
236+
$files[] = $fileData;
237+
}
238+
239+
return response()->json(
240+
[
241+
'slug' => $project->slug,
242+
'name' => $project->name,
243+
'author' => $project->author,
244+
'license' => $project->license,
245+
'description' => $project->description,
246+
'files' => $files,
247+
],
248+
200,
249+
['Content-Type' => 'application/json'],
250+
JSON_UNESCAPED_SLASHES
251+
);
252+
}
253+
254+
/**
255+
* Get app file content
256+
*
257+
* @OA\Get(
258+
* path="/mch2022/{device}/{type}/{category}/{app}/{file}",
259+
* @OA\Parameter(
260+
* name="device",
261+
* in="path",
262+
* required=true,
263+
* @OA\Schema(type="string", format="slug", example="mch2022")
264+
* ),
265+
* @OA\Parameter(
266+
* name="type",
267+
* in="path",
268+
* required=true,
269+
* @OA\Schema(type="string", format="slug", example="esp32")
270+
* ),
271+
* @OA\Parameter(
272+
* name="category",
273+
* in="path",
274+
* required=true,
275+
* @OA\Schema(type="string", format="slug", example="fun")
276+
* ),
277+
* @OA\Parameter(
278+
* name="app",
279+
* in="path",
280+
* required=true,
281+
* @OA\Schema(type="string", format="slug", example="game_of_life")
282+
* ),
283+
* @OA\Parameter(
284+
* name="file",
285+
* in="path",
286+
* required=true,
287+
* @OA\Schema(type="string", format="slug", example="file.py")
288+
* ),
289+
* tags={"MCH2022"},
290+
* @OA\Response(response="default",ref="#/components/responses/undocumented")
291+
* )
292+
*
293+
* @param string $device
294+
* @param string $type
295+
* @param string $category
296+
* @param string $app
297+
* @param string $name
298+
* @return Response|JsonResponse
299+
*/
300+
public function file(
301+
string $device,
302+
string $type,
303+
string $category,
304+
string $app,
305+
string $name
306+
): Response|JsonResponse {
307+
/** @var Badge $badge */
308+
$badge = Badge::whereSlug($device)->firstOrFail();
309+
$categoryId = Category::whereSlug($category)->firstOrFail()->id;
310+
/** @var Project $project */
311+
$project = $badge->projects()
312+
->whereProjectType($type)->whereCategoryId($categoryId)->whereSlug($app)->firstOrFail();
313+
314+
/** @var Version|null $version */
315+
$version = $project->versions()->published()->get()->last();
316+
317+
if ($version === null || empty($version->files)) {
318+
return response()->json(['message' => 'File not found'], 404);
319+
}
320+
321+
/** @var File|null $file */
322+
$file = $version->files()->where('name', $name)->first();
323+
if ($file === null) {
324+
return response()->json(['message' => 'File not found'], 404);
325+
}
326+
327+
return response(
328+
$file->content,
329+
200,
330+
['Content-Type' => $file->mime]
331+
);
332+
}
333+
}

app/Http/Controllers/PublicController.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public function index(Request $request): View
5454
/** @var Builder $projects */
5555
$projects = Project::whereHas(
5656
'versions',
57-
function ($query) {
57+
static function ($query) {
5858
$query->published();
5959
}
6060
);
@@ -98,7 +98,7 @@ private function returnProjectView(Request $request, $projects, string $badge =
9898

9999
$orderField = 'id';
100100
$orderDirection = 'desc';
101-
if ($request->has('order') && in_array($request->get('order'), $this->orderFields)) {
101+
if ($request->has('order') && in_array($request->get('order'), $this->orderFields, true)) {
102102
$orderField = $request->get('order');
103103
if ($request->has('direction') && $request->get('direction') === 'asc') {
104104
$orderDirection = 'asc';

0 commit comments

Comments
 (0)