• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IGNF / validator-api / 22135658047

18 Feb 2026 10:18AM UTC coverage: 53.81% (+0.2%) from 53.594%
22135658047

push

github

web-flow
fix: adding new ways to archive (#83)

* archive 5 days

* fix(test): updateArguments

* fix(test): deleteDataArgument

54 of 113 new or added lines in 16 files covered. (47.79%)

8 existing lines in 4 files now uncovered.

346 of 643 relevant lines covered (53.81%)

2.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.91
/src/Controller/Api/ValidationsController.php
1
<?php
2

3
namespace App\Controller\Api;
4

5
use App\Entity\Validation;
6
use App\Exception\ApiException;
7
use App\Export\CsvReportWriter;
8
use App\Repository\ValidationRepository;
9
use App\Service\MimeTypeGuesserService;
10
use App\Service\ValidatorArgumentsService;
11
use App\Storage\ValidationsStorage;
12
use JMS\Serializer\SerializerInterface;
13
use Psr\Log\LoggerInterface;
14
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
15
use Symfony\Component\Filesystem\Filesystem;
16
use Symfony\Component\HttpFoundation\JsonResponse;
17
use Symfony\Component\HttpFoundation\Request;
18
use Symfony\Component\HttpFoundation\Response;
19
use Symfony\Component\HttpFoundation\StreamedResponse;
20
use Symfony\Component\Routing\Annotation\Route;
21

22
/**
23
 * @Route("/api/validations")
24
 */
25
class ValidationsController extends AbstractController
26
{
27
    public function __construct(
28
        private ValidationRepository $repository,
29
        private SerializerInterface $serializer,
30
        private ValidationsStorage $storage,
31
        private ValidatorArgumentsService $valArgsService,
32
        private MimeTypeGuesserService $mimeTypeGuesserService,
33
        private LoggerInterface $logger,
34
    ) {
35
    }
22✔
36

37
    /**
38
     * @Route(
39
     *      "/",
40
     *      name="validator_api_disabled_routes",
41
     *      methods={"GET","DELETE","PATCH","PUT"}
42
     * )
43
     */
44
    public function disabledRoutes()
45
    {
46
        return new JsonResponse(['error' => 'This route is not allowed'], Response::HTTP_METHOD_NOT_ALLOWED);
1✔
47
    }
48

49
    /**
50
     * @Route(
51
     *      "/{uid}",
52
     *      name="validator_api_get_validation",
53
     *      methods={"GET"}
54
     * )
55
     */
56
    public function getValidation($uid)
57
    {
58
        $validation = $this->repository->findOneByUid($uid);
2✔
59
        if (!$validation) {
2✔
60
            throw new ApiException("No record found for uid=$uid", Response::HTTP_NOT_FOUND);
1✔
61
        }
62

63
        return new JsonResponse($this->serializer->toArray($validation), Response::HTTP_OK);
1✔
64
    }
65

66
    /**
67
     * @Route(
68
     *      "/{uid}/logs",
69
     *      name="validator_api_read_logs",
70
     *      methods={"GET"}
71
     * )
72
     */
73
    public function readConsole($uid)
74
    {
75
        $validation = $this->repository->findOneByUid($uid);
×
76
        if (!$validation) {
×
77
            throw new ApiException("No record found for uid=$uid", Response::HTTP_NOT_FOUND);
×
78
        }
79

NEW
80
        if (Validation::STATUS_ARCHIVED == $validation->getStatus()) {
×
NEW
81
            throw new ApiException('Validation has been archived', Response::HTTP_FORBIDDEN);
×
82
        }
83

84
        $outputDirectory = $this->storage->getOutputDirectory($validation);
×
NEW
85
        $filepath = $outputDirectory.'/validator-debug.log';
×
86

87
        $content = $this->storage->getStorage()->read($filepath);
×
88

89
        return new Response(
×
90
            $content,
×
91
            Response::HTTP_CREATED
×
92
        );
×
93
    }
94

95
    /**
96
     * @Route(
97
     *      "/{uid}/results.csv",
98
     *      name="validator_api_get_validation_csv",
99
     *      methods={"GET"}
100
     * )
101
     */
102
    public function getValidationCsv($uid, CsvReportWriter $csvWriter)
103
    {
104
        $validation = $this->repository->findOneByUid($uid);
×
105
        if (!$validation) {
×
106
            throw new ApiException("No record found for uid=$uid", Response::HTTP_NOT_FOUND);
×
107
        }
108

109
        $response = new StreamedResponse(function () use ($validation, $csvWriter) {
×
110
            $csvWriter->write($validation);
×
111
        });
×
112
        $response->headers->set('Content-Type', 'application/force-download');
×
NEW
113
        $filename = $uid.'-results.csv';
×
NEW
114
        $response->headers->set('Content-Disposition', 'attachment; filename="'.$filename.'"');
×
115

116
        return $response;
×
117
    }
118

119
    /**
120
     * @Route(
121
     *      "/",
122
     *      name="validator_api_upload_dataset",
123
     *      methods={"POST"}
124
     * )
125
     */
126
    public function uploadDataset(Request $request)
127
    {
128
        $files = $request->files;
4✔
129
        /*
130
         * Ensure that input file is submitted
131
         */
132
        $file = $files->get('dataset');
4✔
133
        if (!$file) {
4✔
134
            throw new ApiException('Argument [dataset] is missing', Response::HTTP_BAD_REQUEST);
2✔
135
        }
136

137
        $this->logger->info('handle new upload...', [
2✔
138
            'path_name' => $file->getPathName(),
2✔
139
            'client_original_name' => $file->getClientOriginalName(),
2✔
140
        ]);
2✔
141

142
        /*
143
         * Ensure that input file is a ZIP file.
144
         */
145
        $mimeType = $this->mimeTypeGuesserService->guessMimeType($file->getPathName());
2✔
146
        if ('application/zip' !== $mimeType) {
2✔
147
            throw new ApiException('Dataset must be in a compressed [.zip] file', Response::HTTP_BAD_REQUEST);
1✔
148
        }
149

150
        /*
151
         * create validation and same validation
152
         */
153
        $validation = new Validation();
1✔
154
        // TODO : check getClientOriginalName
155
        $datasetName = str_replace('.zip', '', $file->getClientOriginalName());
1✔
156
        $validation->setDatasetName($datasetName);
1✔
157

158
        // Save file to storage
159
        $uploadDirectory = $this->storage->getUploadDirectory($validation);
1✔
160
        if (!$this->storage->getStorage()->directoryExists($uploadDirectory)) {
1✔
161
            $this->storage->getStorage()->createDirectory($uploadDirectory);
1✔
162
        }
163
        $fileLocation = $uploadDirectory.$validation->getDatasetName().'.zip';
1✔
164
        if ($this->storage->getStorage()->fileExists($fileLocation)) {
1✔
165
            $this->storage->getStorage()->delete($fileLocation);
×
166
        }
167
        $stream = fopen($file->getRealPath(), 'r+');
1✔
168
        $this->storage->getStorage()->writeStream($fileLocation, $stream);
1✔
169
        fclose($stream);
1✔
170

171
        $fs = new Filesystem();
1✔
172
        if ($fs->exists($file->getRealPath())) {
1✔
173
            $this->logger->debug('Validation[{uid}] : rm -rf {path}...', [
1✔
174
                'uid' => $validation->getUid(),
1✔
175
                'path' => $file->getRealPath(),
1✔
176
            ]);
1✔
177
            $fs->remove($file->getRealPath());
1✔
178
        }
179

180
        $em = $this->getDoctrine()->getManager();
1✔
181
        $em->persist($validation);
1✔
182
        $em->flush();
1✔
183
        $em->refresh($validation);
1✔
184

185
        return new JsonResponse(
1✔
186
            $this->serializer->toArray($validation),
1✔
187
            Response::HTTP_CREATED
1✔
188
        );
1✔
189
    }
190

191
    /**
192
     * @Route(
193
     *      "/{uid}",
194
     *      name="validator_api_update_arguments",
195
     *      methods={"PATCH"}
196
     * )
197
     */
198
    public function updateArguments(Request $request, $uid)
199
    {
200
        $data = $request->getContent();
10✔
201

202
        if (!json_decode($data, true)) {
10✔
203
            throw new ApiException('Request body must be a valid JSON string', Response::HTTP_BAD_REQUEST);
2✔
204
        }
205

206
        $validation = $this->repository->findOneByUid($uid);
8✔
207
        if (!$validation) {
8✔
208
            throw new ApiException("No record found for uid=$uid", Response::HTTP_NOT_FOUND);
1✔
209
        }
210

211
        if (Validation::STATUS_ARCHIVED == $validation->getStatus()) {
7✔
212
            throw new ApiException('Validation has been archived', Response::HTTP_FORBIDDEN);
1✔
213
        }
214
        // TODO : review (json_decode in this method and inside of validate)
215
        $arguments = $this->valArgsService->validate($data);
6✔
216

217
        // checks if we need to keep data
218
        $deleteData = $arguments['deleteData'];
1✔
219
        if ($deleteData) {
1✔
NEW
220
            $validation->setDateCreation((new \DateTime())->modify('-5 days'));
×
221
        }
222
        unset($arguments['deleteData']);
1✔
223

224
        $validation->reset();
1✔
225
        $validation->setArguments($arguments);
1✔
226
        $validation->setStatus(Validation::STATUS_PENDING);
1✔
227

228
        $em = $this->getDoctrine()->getManager();
1✔
229
        $em->flush();
1✔
230
        $em->refresh($validation);
1✔
231

232
        return new JsonResponse(
1✔
233
            $this->serializer->toArray($validation),
1✔
234
            Response::HTTP_OK
1✔
235
        );
1✔
236
    }
237

238
    /**
239
     * @Route(
240
     *      "/{uid}",
241
     *      name="validator_api_delete_validation",
242
     *      methods={"DELETE"}
243
     * )
244
     */
245
    public function deleteValidation($uid)
246
    {
247
        $validation = $this->repository->findOneByUid($uid);
1✔
248
        if (!$validation) {
1✔
249
            throw new ApiException("No record found for uid=$uid", Response::HTTP_NOT_FOUND);
1✔
250
        }
251

252
        $this->logger->info('Validation[{uid}] : removing all saved data...', [
1✔
253
            'uid' => $validation->getUid(),
1✔
254
            'datasetName' => $validation->getDatasetName(),
1✔
255
        ]);
1✔
256

257
        $em = $this->getDoctrine()->getManager();
1✔
258
        $em->remove($validation);
1✔
259
        $em->flush();
1✔
260

261
        // Delete from storage
262
        $uploadDirectory = $this->storage->getUploadDirectory($validation);
1✔
263
        if ($this->storage->getStorage()->directoryExists($uploadDirectory)) {
1✔
264
            $this->storage->getStorage()->deleteDirectory($uploadDirectory);
×
265
        }
266
        $outputDirectory = $this->storage->getOutputDirectory($validation);
1✔
267
        if ($this->storage->getStorage()->directoryExists($outputDirectory)) {
1✔
268
            $this->storage->getStorage()->deleteDirectory($outputDirectory);
×
269
        }
270

271
        return new JsonResponse(null, Response::HTTP_NO_CONTENT);
1✔
272
    }
273

274
    /**
275
     * @Route(
276
     *      "/{uid}/files/normalized",
277
     *      name="validator_api_download_normalized_data",
278
     *      methods={"GET"}
279
     * )
280
     */
281
    public function downloadNormalizedData($uid)
282
    {
283
        $validation = $this->repository->findOneByUid($uid);
2✔
284
        if (!$validation) {
2✔
285
            throw new ApiException("No record found for uid=$uid", Response::HTTP_NOT_FOUND);
1✔
286
        }
287

288
        if (Validation::STATUS_ARCHIVED == $validation->getStatus()) {
1✔
289
            throw new ApiException('Validation has been archived', Response::HTTP_FORBIDDEN);
1✔
290
        }
291

292
        if (Validation::STATUS_ERROR == $validation->getStatus()) {
1✔
NEW
293
            throw new ApiException('Validation failed, no normalized data', Response::HTTP_FORBIDDEN);
×
294
        }
295

296
        if (in_array($validation->getStatus(), [Validation::STATUS_PENDING, Validation::STATUS_PROCESSING, Validation::STATUS_WAITING_ARGS])) {
1✔
297
            throw new ApiException("Validation hasn't been executed yet", Response::HTTP_FORBIDDEN);
1✔
298
        }
299

300
        $outputDirectory = $this->storage->getOutputDirectory($validation);
×
NEW
301
        $zipFilepath = $outputDirectory.$validation->getDatasetName().'.zip';
×
302

NEW
303
        return $this->getDownloadResponse($zipFilepath, $validation->getDatasetName().'-normalized.zip');
×
304
    }
305

306
    /**
307
     * @Route(
308
     *      "/{uid}/files/source",
309
     *      name="validator_api_download_source_data",
310
     *      methods={"GET"}
311
     * )
312
     */
313
    public function downloadSourceData($uid)
314
    {
315
        $validation = $this->repository->findOneByUid($uid);
2✔
316
        if (!$validation) {
2✔
317
            throw new ApiException("No record found for uid=$uid", Response::HTTP_NOT_FOUND);
1✔
318
        }
319

320
        if (Validation::STATUS_ARCHIVED == $validation->getStatus()) {
1✔
321
            throw new ApiException('Validation has been archived', Response::HTTP_FORBIDDEN);
1✔
322
        }
323

324
        $uploadDirectory = $this->storage->getUploadDirectory($validation);
×
NEW
325
        $zipFilepath = $uploadDirectory.$validation->getDatasetName().'.zip';
×
326

NEW
327
        return $this->getDownloadResponse($zipFilepath, $validation->getDatasetName().'-source.zip');
×
328
    }
329

330
    /**
331
     * Returns binary response of the specified file.
332
     *
333
     * @param string $filename
334
     *
335
     * @return StreamedResponse
336
     */
337
    private function getDownloadResponse($filepath, $filename)
338
    {
339
        if (!$this->storage->getStorage()->has($filepath)) {
×
NEW
340
            throw new ApiException('Requested files not found for this validation', Response::HTTP_FORBIDDEN);
×
341
        }
342

343
        $stream = $this->storage->getStorage()->readStream($filepath);
×
344

345
        return new StreamedResponse(function () use ($stream) {
×
346
            fpassthru($stream);
×
NEW
347
            exit;
×
348
        }, 200, [
×
349
            'Content-Transfer-Encoding',
×
350
            'binary',
×
351
            'Content-Type' => 'application/zip',
×
352
            'Content-Disposition' => sprintf('attachment; filename="%s"', $filename),
×
NEW
353
            'Content-Length' => fstat($stream)['size'],
×
354
        ]);
×
355
    }
356
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc