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

IGNF / validator-api / 22141694852

18 Feb 2026 01:28PM UTC coverage: 53.715% (-0.1%) from 53.81%
22141694852

push

github

web-flow
feat(dev): deleteData field (#84)

5 of 26 new or added lines in 3 files covered. (19.23%)

347 of 646 relevant lines covered (53.72%)

2.73 hits per line

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

67.15
/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
    ) {}
22✔
35

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

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

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

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

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

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

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

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

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

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

115
        return $response;
×
116
    }
117

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

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

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

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

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

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

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

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

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

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

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

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

216
        // checks if we need to keep data
217
        $validation->setDeleteData($arguments['deleteData']);
1✔
218
        unset($arguments['deleteData']);
1✔
219

220
        $validation->reset();
1✔
221
        $validation->setArguments($arguments);
1✔
222
        $validation->setStatus(Validation::STATUS_PENDING);
1✔
223

224
        $em = $this->getDoctrine()->getManager();
1✔
225
        $em->flush();
1✔
226
        $em->refresh($validation);
1✔
227

228
        return new JsonResponse(
1✔
229
            $this->serializer->toArray($validation),
1✔
230
            Response::HTTP_OK
1✔
231
        );
1✔
232
    }
233

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

248
        $this->logger->info('Validation[{uid}] : removing all saved data...', [
1✔
249
            'uid' => $validation->getUid(),
1✔
250
            'datasetName' => $validation->getDatasetName(),
1✔
251
        ]);
1✔
252

253
        $em = $this->getDoctrine()->getManager();
1✔
254
        $em->remove($validation);
1✔
255
        $em->flush();
1✔
256

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

267
        return new JsonResponse(null, Response::HTTP_NO_CONTENT);
1✔
268
    }
269

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

284
        if (Validation::STATUS_ARCHIVED == $validation->getStatus()) {
1✔
285
            throw new ApiException('Validation has been archived', Response::HTTP_FORBIDDEN);
1✔
286
        }
287

288
        if (Validation::STATUS_ERROR == $validation->getStatus()) {
1✔
289
            throw new ApiException('Validation failed, no normalized data', Response::HTTP_FORBIDDEN);
×
290
        }
291

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

296
        $outputDirectory = $this->storage->getOutputDirectory($validation);
×
NEW
297
        $zipFilepath = $outputDirectory . $validation->getDatasetName() . '.zip';
×
298

NEW
299
        return $this->getDownloadResponse($zipFilepath, $validation->getDatasetName() . '-normalized.zip');
×
300
    }
301

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

316
        if (Validation::STATUS_ARCHIVED == $validation->getStatus()) {
1✔
317
            throw new ApiException('Validation has been archived', Response::HTTP_FORBIDDEN);
1✔
318
        }
319

320
        $uploadDirectory = $this->storage->getUploadDirectory($validation);
×
NEW
321
        $zipFilepath = $uploadDirectory . $validation->getDatasetName() . '.zip';
×
322

NEW
323
        return $this->getDownloadResponse($zipFilepath, $validation->getDatasetName() . '-source.zip');
×
324
    }
325

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

339
        $stream = $this->storage->getStorage()->readStream($filepath);
×
340

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

© 2026 Coveralls, Inc