Passed
Push — master ( 2d71d3...7ef273 )
by Dmitriy
04:39 queued 01:43
created

DebugController::createHtmlPanelResponse()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
eloc 12
c 0
b 0
f 0
dl 0
loc 24
ccs 0
cts 15
cp 0
rs 9.8666
cc 3
nc 2
nop 3
crap 12
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Debug\Api\Controller;
6
7
use OpenApi\Annotations as OA;
8
use Psr\Container\ContainerInterface;
9
use Psr\Http\Message\ResponseInterface;
10
use Psr\Http\Message\ServerRequestInterface;
11
use Yiisoft\Assets\AssetManager;
0 ignored issues
show
Bug introduced by
The type Yiisoft\Assets\AssetManager was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use Yiisoft\Assets\AssetPublisherInterface;
13
use Yiisoft\DataResponse\DataResponse;
14
use Yiisoft\DataResponse\DataResponseFactoryInterface;
15
use Yiisoft\Router\CurrentRoute;
16
use Yiisoft\Yii\Debug\Api\Exception\NotFoundException;
17
use Yiisoft\Yii\Debug\Api\Exception\PackageNotInstalledException;
18
use Yiisoft\Yii\Debug\Api\HtmlViewProviderInterface;
19
use Yiisoft\Yii\Debug\Api\ModuleFederationProviderInterface;
20
use Yiisoft\Yii\Debug\Api\Repository\CollectorRepositoryInterface;
21
use Yiisoft\Yii\View\ViewRenderer;
22
23
/**
24
 * Debug controller provides endpoints that expose information about requests processed that debugger collected.
25
 *
26
 * @OA\Tag(
27
 *     name="yii-debug-api",
28
 *     description="Yii Debug API"
29
 * )
30
 */
31
final class DebugController
32
{
33
    public function __construct(private DataResponseFactoryInterface $responseFactory, private CollectorRepositoryInterface $collectorRepository)
34
    {
35
    }
36
37
    /**
38
     * List of requests processed.
39
     *
40
     * @OA\Get(
41
     *     tags={"yii-debug-api"},
42
     *     path="/debug/api",
43
     *     description="List of requests processed",
44
     *
45
     *     @OA\Response(
46
     *          response="200",
47
     *          description="Success",
48
     *
49
     *          @OA\JsonContent(
50
     *              allOf={
51
     *
52
     *                  @OA\Schema(ref="#/components/schemas/DebugSuccessResponse")
53
     *              }
54
     *          )
55
     *     )
56
     * )
57
     */
58
    public function index(): ResponseInterface
59
    {
60
        return $this->responseFactory->createResponse($this->collectorRepository->getSummary());
61
    }
62
63
    /**
64
     * Summary about a processed request identified by ID specified.
65
     *
66
     * @OA\Get(
67
     *     tags={"yii-debug-api"},
68
     *     path="/debug/api/summary/{id}",
69
     *     description="Summary about a processed request identified by ID specified",
70
     *
71
     *     @OA\Parameter(
72
     *          name="id",
73
     *          required=true,
74
     *
75
     *          @OA\Schema(type="string"),
76
     *          in="path",
77
     *          parameter="id",
78
     *          description="Request ID for getting the summary"
79
     *     ),
80
     *
81
     *     @OA\Response(
82
     *          response="200",
83
     *          description="Success",
84
     *
85
     *          @OA\JsonContent(
86
     *              allOf={
87
     *
88
     *                  @OA\Schema(ref="#/components/schemas/DebugSuccessResponse")
89
     *              }
90
     *          )
91
     *     ),
92
     *
93
     *     @OA\Response(
94
     *          response="404",
95
     *          description="Not found",
96
     *
97
     *          @OA\JsonContent(
98
     *              allOf={
99
     *
100
     *                  @OA\Schema(ref="#/components/schemas/DebugNotFoundResponse")
101
     *              }
102
     *          )
103
     *     )
104
     * )
105
     */
106
    public function summary(CurrentRoute $currentRoute): ResponseInterface
107
    {
108
        $data = $this->collectorRepository->getSummary($currentRoute->getArgument('id'));
109
        return $this->responseFactory->createResponse($data);
110
    }
111
112
    /**
113
     * Detail information about a processed request identified by ID.
114
     *
115
     * @OA\Get(
116
     *     tags={"yii-debug-api"},
117
     *     path="/debug/api/view/{id}/?collector={collector}",
118
     *     description="Detail information about a processed request identified by ID",
119
     *
120
     *     @OA\Parameter(
121
     *          name="id",
122
     *          required=true,
123
     *
124
     *          @OA\Schema(type="string"),
125
     *          in="path",
126
     *          parameter="id",
127
     *          description="Request ID for getting the detail information"
128
     *     ),
129
     *
130
     *     @OA\Parameter(
131
     *          name="collector",
132
     *          allowEmptyValue=true,
133
     *
134
     *          @OA\Schema(type="string"),
135
     *          in="query",
136
     *          parameter="collector",
137
     *          description="Collector for getting the detail information"
138
     *     ),
139
     *
140
     *     @OA\Response(
141
     *          response="200",
142
     *          description="Success",
143
     *
144
     *          @OA\JsonContent(
145
     *              allOf={
146
     *
147
     *                  @OA\Schema(ref="#/components/schemas/DebugSuccessResponse")
148
     *              }
149
     *          )
150
     *     ),
151
     *
152
     *     @OA\Response(
153
     *          response="404",
154
     *          description="Not found",
155
     *
156
     *          @OA\JsonContent(
157
     *              allOf={
158
     *
159
     *                  @OA\Schema(ref="#/components/schemas/DebugNotFoundResponse")
160
     *              }
161
     *          )
162
     *     )
163
     * )
164
     */
165
    public function view(
166
        CurrentRoute $currentRoute,
167
        ServerRequestInterface $serverRequest,
168
        ContainerInterface $container,
169
    ): ResponseInterface {
170
        $data = $this->collectorRepository->getDetail(
171
            $currentRoute->getArgument('id')
0 ignored issues
show
Bug introduced by
It seems like $currentRoute->getArgument('id') can also be of type null; however, parameter $id of Yiisoft\Yii\Debug\Api\Re...yInterface::getDetail() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

171
            /** @scrutinizer ignore-type */ $currentRoute->getArgument('id')
Loading history...
172
        );
173
174
        $collectorClass = $serverRequest->getQueryParams()['collector'] ?? null;
175
        if ($collectorClass !== null) {
176
            $data = $data[$collectorClass] ?? throw new NotFoundException(
177
                sprintf("Requested collector doesn't exist: %s.", $collectorClass)
178
            );
179
        }
180
        if (is_subclass_of($collectorClass, HtmlViewProviderInterface::class)) {
181
            return $this->createHtmlPanelResponse($container, $collectorClass, $data);
182
        }
183
        if (is_subclass_of($collectorClass, ModuleFederationProviderInterface::class)) {
184
            return $this->createJsPanelResponse($container, $collectorClass, $data);
185
        }
186
187
        return $this->responseFactory->createResponse($data);
188
    }
189
190
    /**
191
     * Dump information about a processed request identified by ID.
192
     *
193
     * @OA\Get(
194
     *     tags={"yii-debug-api"},
195
     *     path="/debug/api/dump/{id}/{collector}",
196
     *     description="Dump information about a processed request identified by ID",
197
     *
198
     *     @OA\Parameter(
199
     *          name="id",
200
     *          required=true,
201
     *
202
     *          @OA\Schema(type="string"),
203
     *          in="path",
204
     *          parameter="id",
205
     *          description="Request ID for getting the dump information"
206
     *     ),
207
     *
208
     *     @OA\Parameter(
209
     *          name="collector",
210
     *          allowEmptyValue=true,
211
     *          required=false,
212
     *
213
     *          @OA\Schema(type="string"),
214
     *          in="path",
215
     *          parameter="collector",
216
     *          description="Collector for getting the dump information"
217
     *     ),
218
     *
219
     *     @OA\Response(
220
     *          response="200",
221
     *          description="Success",
222
     *
223
     *          @OA\JsonContent(
224
     *              allOf={
225
     *
226
     *                  @OA\Schema(ref="#/components/schemas/DebugSuccessResponse")
227
     *              }
228
     *          )
229
     *     ),
230
     *
231
     *     @OA\Response(
232
     *          response="404",
233
     *          description="Not found",
234
     *
235
     *          @OA\JsonContent(
236
     *              allOf={
237
     *
238
     *                  @OA\Schema(ref="#/components/schemas/DebugNotFoundResponse")
239
     *              }
240
     *          )
241
     *     )
242
     * )
243
     *
244
     * @throws NotFoundException
245
     *
246
     * @return ResponseInterface response.
247
     */
248
    public function dump(CurrentRoute $currentRoute): ResponseInterface
249
    {
250
        $data = $this->collectorRepository->getDumpObject(
251
            $currentRoute->getArgument('id')
0 ignored issues
show
Bug introduced by
It seems like $currentRoute->getArgument('id') can also be of type null; however, parameter $id of Yiisoft\Yii\Debug\Api\Re...erface::getDumpObject() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

251
            /** @scrutinizer ignore-type */ $currentRoute->getArgument('id')
Loading history...
252
        );
253
254
        if ($currentRoute->getArgument('collector') !== null) {
255
            if (isset($data[$currentRoute->getArgument('collector')])) {
256
                $data = $data[$currentRoute->getArgument('collector')];
257
            } else {
258
                throw new NotFoundException('Requested collector doesn\'t exists.');
259
            }
260
        }
261
262
        return $this->responseFactory->createResponse($data);
263
    }
264
265
    /**
266
     * Object information about a processed request identified by ID.
267
     *
268
     * @OA\Get(
269
     *     tags={"yii-debug-api"},
270
     *     path="/debug/api/object/{id}/{objectId}",
271
     *     description="Object information about a processed request identified by ID",
272
     *
273
     *     @OA\Parameter(
274
     *          name="id",
275
     *          required=true,
276
     *
277
     *          @OA\Schema(type="string"),
278
     *          in="path",
279
     *          parameter="id",
280
     *          description="Request ID for getting the object information"
281
     *     ),
282
     *
283
     *     @OA\Parameter(
284
     *          name="objectId",
285
     *          required=true,
286
     *
287
     *          @OA\Schema(type="string"),
288
     *          in="path",
289
     *          parameter="objectId",
290
     *          description="ID for getting the object information"
291
     *     ),
292
     *
293
     *     @OA\Response(
294
     *          response="200",
295
     *          description="Success",
296
     *
297
     *          @OA\JsonContent(
298
     *              allOf={
299
     *
300
     *                  @OA\Schema(ref="#/components/schemas/DebugSuccessResponse")
301
     *              }
302
     *          )
303
     *     ),
304
     *
305
     *     @OA\Response(
306
     *          response="404",
307
     *          description="Not found",
308
     *
309
     *          @OA\JsonContent(
310
     *              allOf={
311
     *
312
     *                  @OA\Schema(ref="#/components/schemas/DebugNotFoundResponse")
313
     *              }
314
     *          )
315
     *     )
316
     * )
317
     *
318
     * @return ResponseInterface response.
319
     */
320
    public function object(CurrentRoute $currentRoute): ResponseInterface
321
    {
322
        $data = $this->collectorRepository->getObject(
323
            $currentRoute->getArgument('id'),
0 ignored issues
show
Bug introduced by
It seems like $currentRoute->getArgument('id') can also be of type null; however, parameter $id of Yiisoft\Yii\Debug\Api\Re...yInterface::getObject() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

323
            /** @scrutinizer ignore-type */ $currentRoute->getArgument('id'),
Loading history...
324
            $currentRoute->getArgument('objectId')
0 ignored issues
show
Bug introduced by
It seems like $currentRoute->getArgument('objectId') can also be of type null; however, parameter $objectId of Yiisoft\Yii\Debug\Api\Re...yInterface::getObject() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

324
            /** @scrutinizer ignore-type */ $currentRoute->getArgument('objectId')
Loading history...
325
        );
326
327
        return $this->responseFactory->createResponse($data);
328
    }
329
330
    private function createJsPanelResponse(
331
        ContainerInterface $container,
332
        string $collectorClass,
333
        mixed $data
334
    ): DataResponse {
335
        $asset = $collectorClass::getAsset();
336
        $module = $asset->getModule();
337
        $scope = $asset->getScope();
338
        /**
339
         * @psalm-suppress UndefinedClass
340
         */
341
        if (
342
            !class_exists(AssetManager::class)
343
            || !class_exists(AssetPublisherInterface::class)
344
            || !$container->has(AssetManager::class)
345
            || !$container->has(AssetPublisherInterface::class)
346
        ) {
347
            throw new PackageNotInstalledException(
348
                'yiisoft/assets',
349
                sprintf(
350
                    '"%s" or "%s" is not defined in the dependency container.',
351
                    AssetManager::class,
352
                    AssetPublisherInterface::class,
353
                ),
354
            );
355
        }
356
        /**
357
         * @psalm-suppress UndefinedClass
358
         */
359
        $assetManager = $container->get(AssetManager::class);
360
        $assetManager->register($asset::class);
361
        /**
362
         * @psalm-suppress UndefinedClass
363
         */
364
        $assetPublisher = $container->get(AssetPublisherInterface::class);
365
        $assetPublisher->publish($asset);
366
367
        $js = $assetManager->getJsFiles();
368
369
        $urls = end($js);
370
371
        return $this->responseFactory->createResponse([
372
            '__isPanelRemote__' => true,
373
            'url' => $urls[0],
374
            'module' => $module,
375
            'scope' => $scope,
376
            'data' => $data,
377
        ]);
378
    }
379
380
    private function createHtmlPanelResponse(
381
        ContainerInterface $container,
382
        string $collectorClass,
383
        mixed $data
384
    ): DataResponse {
385
        if (!class_exists(ViewRenderer::class) || !$container->has(ViewRenderer::class)) {
386
            /**
387
             * @psalm-suppress UndefinedClass
388
             */
389
            throw new PackageNotInstalledException(
390
                'yiisoft/yii-view',
391
                sprintf(
392
                    '"%s" is not defined in the dependency container.',
393
                    ViewRenderer::class,
394
                )
395
            );
396
        }
397
        $viewRenderer = $container->get(ViewRenderer::class);
398
        $viewDirectory = dirname($collectorClass::getView());
399
        $viewPath = basename($collectorClass::getView());
400
401
        return $viewRenderer
402
            ->withViewPath($viewDirectory)
403
            ->renderPartial($viewPath, ['data' => $data, 'collectorClass' => $collectorClass]);
404
    }
405
}
406