Completed
Push — master ( 41d0cd...80ed77 )
by FX
13:09
created

SuiteApiController   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 435
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 435
rs 10
wmc 18

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getSuiteAction() 0 3 1
A getSuitesAction() 0 7 1
C postSuiteAction() 0 69 8
B putSuiteLimitsAction() 0 32 6
A deleteSuiteAction() 0 11 2
1
<?php
2
3
/**
4
 * Copyright (c) 2017 Francois-Xavier Soubirou.
5
 *
6
 * This file is part of ci-report.
7
 *
8
 * ci-report is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * ci-report is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with ci-report. If not, see <http://www.gnu.org/licenses/>.
20
 */
21
declare(strict_types=1);
22
23
namespace AppBundle\Controller;
24
25
use AppBundle\DTO\SuiteLimitsDTO;
26
use AppBundle\DTO\SuiteLimitsFilesDTO;
27
use AppBundle\Entity\Campaign;
28
use AppBundle\Entity\Project;
29
use AppBundle\Entity\Suite;
30
use AppBundle\Entity\Test;
31
use AppBundle\Service\FileUploaderService;
32
use AppBundle\Service\JunitParserService;
33
use AppBundle\Service\RefreshService;
34
use DOMDocument;
35
use FOS\RestBundle\Controller\Annotations as Rest;
36
use FOS\RestBundle\View\View;
37
use Nelmio\ApiDocBundle\Annotation as Doc;
38
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
39
use Symfony\Component\HttpFoundation\Request;
40
use Symfony\Component\HttpFoundation\Response;
41
42
/**
43
 * Suite API controller class.
44
 *
45
 * @category  ci-report app
46
 *
47
 * @author    Francois-Xavier Soubirou <[email protected]>
48
 * @copyright 2017 Francois-Xavier Soubirou
49
 * @license   http://www.gnu.org/licenses/   GPLv3
50
 *
51
 * @see      https://www.ci-report.io
52
 *
53
 * @Rest\Route("/api")
54
 */
55
class SuiteApiController extends AbstractApiController
56
{
57
    /**
58
     * Get list of suites for a project and a campaign. Example: </br>
59
     * <pre style="background:black; color:white; font-size:10px;"><code style="background:black;">curl https://www.ci-report.io/api/projects/project-one/campaigns/1/suites -X GET
60
     * </code></pre>.
61
     *
62
     * @param Campaign $campaign Campaign
63
     *
64
     * @return array
65
     *
66
     * @Rest\Get("/projects/{prefid}/campaigns/{crefid}/suites")
67
     * @Rest\View(serializerGroups={"public"})
68
     *
69
     * @ParamConverter("campaign", class="AppBundle:Campaign", options={
70
     *    "repository_method" = "findCampaignByProjectRefidAndRefid",
71
     *    "mapping": {"prefid": "prefid", "crefid": "crefid"},
72
     *    "map_method_signature" = true
73
     * })
74
     *
75
     * @Doc\ApiDoc(
76
     *     section="Suites",
77
     *     description="Get the list of all suites for a campaign.",
78
     *     requirements={
79
     *         {
80
     *             "name"="prefid",
81
     *             "dataType"="string",
82
     *             "requirement"="string",
83
     *             "description"="Unique short name of project defined on project creation."
84
     *         },
85
     *         {
86
     *             "name"="crefid",
87
     *             "dataType"="int",
88
     *             "requirement"="int",
89
     *             "description"="Reference id of the campaign."
90
     *         }
91
     *     },
92
     *     output={
93
     *         "class"=Suite::class,
94
     *         "groups"={"public"},
95
     *         "parsers"={"Nelmio\ApiDocBundle\Parser\JmsMetadataParser"}
96
     *     },
97
     *     statusCodes={
98
     *         200="Returned when successful array of public data of campaigns",
99
     *         404="Returned when project not found"
100
     *     }
101
     * )
102
     */
103
    public function getSuitesAction(Campaign $campaign): array
104
    {
105
        $suites = $this->getDoctrine()
106
            ->getRepository(Suite::class)
107
            ->findSuitesByCampaign($campaign);
108
109
        return $suites;
110
    }
111
112
    /**
113
     * Get suite data. Example: </br>
114
     * <pre style="background:black; color:white; font-size:10px;"><code style="background:black;">curl https://www.ci-report.io/api/projects/project-one/campaigns/1/suites/1 -X GET
115
     * </code></pre>.
116
     *
117
     * @param Suite $suite Suite
118
     *
119
     * @return Suite
120
     *
121
     * @Rest\Get(
122
     *    "/projects/{prefid}/campaigns/{crefid}/suites/{srefid}",
123
     *    requirements={"crefid" = "\d+", "srefid" = "\d+"}
124
     * )
125
     * @Rest\View(serializerGroups={"public"})
126
     *
127
     * @ParamConverter("suite", class="AppBundle:Suite", options={
128
     *    "repository_method" = "findSuiteByProjectRefidCampaignRefidAndRefid",
129
     *    "mapping": {"prefid": "prefid", "crefid": "crefid", "srefid": "srefid"},
130
     *    "map_method_signature" = true
131
     * })
132
     *
133
     * @Doc\ApiDoc(
134
     *     section="Suites",
135
     *     description="Get suite data.",
136
     *     requirements={
137
     *         {
138
     *             "name"="prefid",
139
     *             "dataType"="string",
140
     *             "requirement"="string",
141
     *             "description"="Unique short name of project defined on project creation."
142
     *         },
143
     *         {
144
     *             "name"="crefid",
145
     *             "dataType"="int",
146
     *             "requirement"="int",
147
     *             "description"="Reference id of the campaign."
148
     *         },
149
     *         {
150
     *             "name"="srefid",
151
     *             "dataType"="int",
152
     *             "requirement"="int",
153
     *             "description"="Reference id of the suite."
154
     *         }
155
     *     },
156
     *     output= {
157
     *         "class"=Suite::class,
158
     *         "groups"={"public"},
159
     *         "parsers"={"Nelmio\ApiDocBundle\Parser\JmsMetadataParser"}
160
     *     },
161
     *     statusCodes={
162
     *         200="Returned when successful",
163
     *         404="Returned when project or campaign not found"
164
     *     }
165
     * )
166
     */
167
    public function getSuiteAction(Suite $suite): Suite
168
    {
169
        return $suite;
170
    }
171
172
    /**
173
     * Create suites for a campaign by uploading a junit file. Example: </br>
174
     * <pre style="background:black; color:white; font-size:10px;"><code style="background:black;">curl https://www.ci-report.io/api/projects/project-one/campaigns/1/suites/junit -H "X-CIR-TKN: 1f4ffb19e4b9-02278af07b7d-4e370a76f001" -X POST -F warning=80 -F success=95 -F 'junitfile=@/path/to/junit.xml'
175
     * </code></pre>
176
     * <p style="font-size:10px;">(@ symbol is mandatory at the beginning of the file path)</p>.
177
     *
178
     * @param Project  $project  Project
179
     * @param Campaign $campaign Campaign
180
     * @param Request  $request  The request
181
     *
182
     * @return array|View
183
     *
184
     * @Rest\Post("/projects/{prefid}/campaigns/{crefid}/suites/junit")
185
     * @Rest\View(statusCode=Response::HTTP_CREATED, serializerGroups={"public"})
186
     *
187
     * @ParamConverter("project", options={"mapping": {"prefid": "refid"}})
188
     * @ParamConverter("campaign", class="AppBundle:Campaign", options={
189
     *    "repository_method" = "findCampaignByProjectRefidAndRefid",
190
     *    "mapping": {"prefid": "prefid", "crefid": "crefid"},
191
     *    "map_method_signature" = true
192
     * })
193
     *
194
     * @Doc\ApiDoc(
195
     *     section="Suites",
196
     *     description="Create suites by uploading a junit file.",
197
     *     headers={
198
     *         {
199
     *             "name"="X-CIR-TKN",
200
     *             "required"=true,
201
     *             "description"="Private token"
202
     *         }
203
     *     },
204
     *     requirements={
205
     *         {
206
     *             "name"="prefid",
207
     *             "dataType"="string",
208
     *             "requirement"="string",
209
     *             "description"="Unique short name of project defined on project creation."
210
     *         },
211
     *         {
212
     *             "name"="crefid",
213
     *             "dataType"="int",
214
     *             "requirement"="int",
215
     *             "description"="Reference id of the campaign."
216
     *         }
217
     *     },
218
     *     input= { "class"=SuiteLimitsFilesDTO::class },
219
     *     output= {
220
     *         "class"=Suite::class,
221
     *         "groups"={"public"},
222
     *         "parsers"={"Nelmio\ApiDocBundle\Parser\JmsMetadataParser"}
223
     *     },
224
     *     statusCodes={
225
     *         201="Returned when created",
226
     *         400="Returned when a violation is raised by validation",
227
     *         401="Returned when X-CIR-TKN private token value is invalid",
228
     *         404="Returned when project or campaign not found"
229
     *     },
230
     *     tags={
231
     *         "token" = "#2c3e50"
232
     *     }
233
     * )
234
     */
235
    public function postSuiteAction(Project $project, Campaign $campaign, Request $request)
236
    {
237
        if ($this->isInvalidToken($request, $project->getToken())) {
238
            return $this->getInvalidTokenView();
239
        }
240
241
        $fileUploaderService = $this->get(FileUploaderService::class);
242
        $junitParserService = $this->get(JunitParserService::class);
243
        $validator = $this->get('validator');
244
245
        $suiteLimitsFilesDTO = new SuiteLimitsFilesDTO($project, $request);
246
247
        $violations = $validator->validate($suiteLimitsFilesDTO);
248
        if (count($violations) > 0) {
249
            return $this->view($violations, Response::HTTP_BAD_REQUEST);
250
        }
251
252
        $fileNameUId = $fileUploaderService->upload(
253
            $suiteLimitsFilesDTO->getJunitfile()
254
        );
255
256
        $doc = new DOMDocument();
257
        $doc->load($fileUploaderService->getFullPath($fileNameUId));
258
        $errors = $junitParserService->validate($doc);
259
        if (count($errors) > 0) {
260
            $fileUploaderService->remove($fileNameUId);
261
262
            return $this->view($errors, Response::HTTP_BAD_REQUEST);
263
        }
264
        $suitesArray = $junitParserService->parse($doc);
265
        $fileUploaderService->remove($fileNameUId);
266
267
        $em = $this->getDoctrine()->getManager();
268
269
        $suitesEntity = array();
270
271
        foreach ($suitesArray as $suiteTests) {
272
            $suiteDTO = $suiteTests->getSuite();
273
            $suiteDTO->setWarning($suiteLimitsFilesDTO->getWarning());
274
            $suiteDTO->setSuccess($suiteLimitsFilesDTO->getSuccess());
275
276
            $suite = new Suite($project, $campaign);
277
            $suite->setFromDTO($suiteDTO);
278
279
            $violations = $validator->validate($suite);
280
            if (count($violations) > 0) {
281
                return $this->view($violations, Response::HTTP_BAD_REQUEST);
282
            }
283
            $em->persist($suite);
284
285
            foreach ($suiteTests->getTests() as $testDTO) {
286
                $test = new Test($suite);
287
                $test->setFromDTO($testDTO);
288
289
                $violations = $validator->validate($test);
290
                if (count($violations) > 0) {
291
                    return $this->view($violations, Response::HTTP_BAD_REQUEST);
292
                }
293
                $em->persist($test);
294
            }
295
            $em->flush();
296
            $this->get(RefreshService::class)->refreshCampaign(
297
                $suite->getCampaign(),
298
                true
299
            );
300
            $suitesEntity[] = $suite;
301
        }
302
303
        return $suitesEntity;
304
    }
305
306
    /**
307
     * Update suite warning and success limits. Example: </br>
308
     * <pre style="background:black; color:white; font-size:10px;"><code style="background:black;">curl https://www.ci-report.io/api/projects/project-one/campaigns/1/suites/1/limits -H "Content-Type: application/json" -H "X-CIR-TKN: 1f4ffb19e4b9-02278af07b7d-4e370a76f001" -X PUT --data '{"warning":80, "success":95}'
309
     * </code></pre>.
310
     *
311
     * @param Suite          $suiteDB        Suite to update
312
     * @param SuiteLimitsDTO $suiteLimitsDTO Object containing input data
313
     * @param Request        $request        The request
314
     *
315
     * @return Suite|View
316
     *
317
     * @Rest\Put(
318
     *    "/projects/{prefid}/campaigns/{crefid}/suites/{srefid}/limits",
319
     *    requirements={"crefid" = "\d+", "srefid" = "\d+"}
320
     * )
321
     * @Rest\View(serializerGroups={"public"})
322
     *
323
     * @ParamConverter("suiteDB", class="AppBundle:Suite", options={
324
     *    "repository_method" = "findSuiteByProjectRefidCampaignRefidAndRefid",
325
     *    "mapping": {"prefid": "prefid", "crefid": "crefid", "srefid": "srefid"},
326
     *    "map_method_signature" = true
327
     * })
328
     * @ParamConverter("suiteLimitsDTO", converter="fos_rest.request_body")
329
     *
330
     * @Doc\ApiDoc(
331
     *     section="Suites",
332
     *     description="Update suite warning and success limits.",
333
     *     headers={
334
     *         {
335
     *             "name"="Content-Type",
336
     *             "required"=true,
337
     *             "description"="Type of content: application/json"
338
     *         },
339
     *         {
340
     *             "name"="X-CIR-TKN",
341
     *             "required"=true,
342
     *             "description"="Private token"
343
     *         }
344
     *     },
345
     *     requirements={
346
     *         {
347
     *             "name"="prefid",
348
     *             "dataType"="string",
349
     *             "requirement"="string",
350
     *             "description"="Unique short name of project defined on project creation."
351
     *         },
352
     *         {
353
     *             "name"="crefid",
354
     *             "dataType"="int",
355
     *             "requirement"="int",
356
     *             "description"="Reference id of the campaign."
357
     *         },
358
     *         {
359
     *             "name"="srefid",
360
     *             "dataType"="int",
361
     *             "requirement"="int",
362
     *             "description"="Reference id of the suite."
363
     *         }
364
     *     },
365
     *     input= { "class"=SuiteLimitsDTO::class },
366
     *     output= {
367
     *         "class"=Suite::class,
368
     *         "groups"={"public"},
369
     *         "parsers"={"Nelmio\ApiDocBundle\Parser\JmsMetadataParser"}
370
     *     },
371
     *     statusCodes={
372
     *         200="Returned when successful",
373
     *         400="Returned when a violation is raised by validation",
374
     *         401="Returned when X-CIR-TKN private token value is invalid",
375
     *         404="Returned when suite not found"
376
     *     },
377
     *     tags={
378
     *         "token" = "#2c3e50"
379
     *     }
380
     * )
381
     */
382
    public function putSuiteLimitsAction(Suite $suiteDB, SuiteLimitsDTO $suiteLimitsDTO, Request $request)
383
    {
384
        $project = $suiteDB->getCampaign()->getProject();
385
        if ($this->isInvalidToken($request, $project->getToken())) {
386
            return $this->getInvalidTokenView();
387
        }
388
        // If limit not defined get limit from suite
389
        if (null === $suiteLimitsDTO->getWarning()) {
390
            $suiteLimitsDTO->setWarning($suiteDB->getWarning());
391
        }
392
        if (null === $suiteLimitsDTO->getSuccess()) {
393
            $suiteLimitsDTO->setSuccess($suiteDB->getSuccess());
394
        }
395
396
        $validator = $this->get('validator');
397
        $violationsDTO = $validator->validate($suiteLimitsDTO);
398
399
        if (count($violationsDTO) > 0) {
400
            return $this->view($violationsDTO, Response::HTTP_BAD_REQUEST);
401
        }
402
        $suiteDB->setFromLimitsDTO($suiteLimitsDTO);
403
404
        $violations = $validator->validate($suiteDB);
405
        if (count($violations) > 0) {
406
            return $this->view($violations, Response::HTTP_BAD_REQUEST);
407
        }
408
        $this->getDoctrine()->getManager()->flush();
409
        $this->get(RefreshService::class)->refreshCampaign(
410
            $suiteDB->getCampaign()
411
        );
412
413
        return $suiteDB;
414
    }
415
416
    /**
417
     * Delete a suite. Example: </br>
418
     * <pre style="background:black; color:white; font-size:10px;"><code style="background:black;">curl https://www.ci-report.io/api/projects/project-one/campaigns/1/suites/1 -H "X-CIR-TKN: 1f4ffb19e4b9-02278af07b7d-4e370a76f001" -X DELETE
419
     * </code></pre>.
420
     *
421
     * @param Suite   $suite   Suite to delete
422
     * @param Request $request The request
423
     *
424
     * @return void|View
425
     *
426
     * @Rest\Delete(
427
     *    "/projects/{prefid}/campaigns/{crefid}/suites/{srefid}",
428
     *    requirements={"crefid" = "\d+", "srefid" = "\d+"}
429
     * )
430
     * @Rest\View(statusCode=Response::HTTP_NO_CONTENT)
431
     *
432
     * @ParamConverter("suite", class="AppBundle:Suite", options={
433
     *    "repository_method" = "findSuiteByProjectRefidCampaignRefidAndRefid",
434
     *    "mapping": {"prefid": "prefid", "crefid": "crefid", "srefid": "srefid"},
435
     *    "map_method_signature" = true
436
     * })
437
     *
438
     * @Doc\ApiDoc(
439
     *     section="Suites",
440
     *     description="Delete a campaign.",
441
     *     headers={
442
     *         {
443
     *             "name"="X-CIR-TKN",
444
     *             "required"=true,
445
     *             "description"="Private token"
446
     *         }
447
     *     },
448
     *     requirements={
449
     *         {
450
     *             "name"="prefid",
451
     *             "dataType"="string",
452
     *             "requirement"="string",
453
     *             "description"="Unique short name of project defined on project creation."
454
     *         },
455
     *         {
456
     *             "name"="crefid",
457
     *             "dataType"="int",
458
     *             "requirement"="int",
459
     *             "description"="Reference id of the campaign."
460
     *         },
461
     *         {
462
     *             "name"="srefid",
463
     *             "dataType"="int",
464
     *             "requirement"="int",
465
     *             "description"="Reference id of the suite."
466
     *         }
467
     *     },
468
     *     statusCodes={
469
     *         204="Returned when successful",
470
     *         401="Returned when X-CIR-TKN private token value is invalid",
471
     *         404="Returned when suite not found",
472
     *         405="Returned when suite refid is not set in URL"
473
     *     },
474
     *     tags={
475
     *         "token" = "#2c3e50"
476
     *     }
477
     * )
478
     */
479
    public function deleteSuiteAction(Suite $suite, Request $request)
480
    {
481
        $project = $suite->getCampaign()->getProject();
482
        if ($this->isInvalidToken($request, $project->getToken())) {
483
            return $this->getInvalidTokenView();
484
        }
485
        $em = $this->getDoctrine()->getManager();
486
        $em->remove($suite);
487
        $em->flush();
488
        $this->get(RefreshService::class)->refreshCampaign(
489
            $suite->getCampaign()
490
        );
491
    }
492
}
493