• 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

22.09
/src/Validation/ValidationManager.php
1
<?php
2

3
namespace App\Validation;
4

5
use App\Entity\Validation;
6
use App\Exception\ZipArchiveValidationException;
7
use App\Repository\ValidationRepository;
8
use App\Storage\ValidationsStorage;
9
use Doctrine\ORM\EntityManagerInterface;
10
use Psr\Log\LoggerInterface;
11
use Symfony\Component\Filesystem\Filesystem;
12
use Symfony\Component\Process\Exception\ProcessFailedException;
13
use Symfony\Component\Process\Process;
14

15
class ValidationManager
16
{
17
    /**
18
     * @var EntityManagerInterface
19
     */
20
    private $em;
21

22
    /**
23
     * @var ValidationsStorage
24
     */
25
    private $storage;
26

27
    /**
28
     * @var ValidatorCLI
29
     */
30
    private $validatorCli;
31

32
    /**
33
     * @var LoggerInterface
34
     */
35
    private $logger;
36

37
    /**
38
     * @var ZipArchiveValidator
39
     */
40
    private $zipArchiveValidator;
41

42
    /**
43
     * @var ValidationRepository
44
     */
45
    private $validationRepository;
46

47
    /**
48
     * Current validation (in order to handle SIGTERM).
49
     *
50
     * @var Validation
51
     */
52
    private $currentValidation;
53

54
    public function __construct(
55
        EntityManagerInterface $em,
56
        ValidationsStorage $storage,
57
        ValidatorCLI $validatorCli,
58
        ZipArchiveValidator $zipArchiveValidator,
59
        LoggerInterface $logger,
60
        ValidationRepository $validationRepository,
61
    ) {
62
        $this->em = $em;
1✔
63
        $this->storage = $storage;
1✔
64
        $this->validatorCli = $validatorCli;
1✔
65
        $this->zipArchiveValidator = $zipArchiveValidator;
1✔
66
        $this->logger = $logger;
1✔
67
        $this->validationRepository = $validationRepository;
1✔
68
    }
69

70
    /**
71
     * Archive a given validation removing all local files.
72
     *
73
     * @return void
74
     */
75
    public function archive(Validation $validation)
76
    {
77
        $this->logger->info('Validation[{uid}] : archive removing all files...', [
1✔
78
            'uid' => $validation->getUid(),
1✔
79
        ]);
1✔
80
        $validationDirectory = $this->storage->getDirectory($validation);
1✔
81
        $fs = new Filesystem();
1✔
82
        if ($fs->exists($validationDirectory)) {
1✔
83
            $this->logger->debug('Validation[{uid}] : remove validation directory ...', [
1✔
84
                'uid' => $validation->getUid(),
1✔
85
                'validationDirectory' => $validationDirectory,
1✔
86
            ]);
1✔
87
            $fs->remove($validationDirectory);
1✔
88
        }
89

90
        // Delete from storage
91
        $this->logger->info('Validation[{uid}] : remove upload files', [
1✔
92
            'uid' => $validation->getUid(),
1✔
93
        ]);
1✔
94
        $uploadDirectory = $this->storage->getUploadDirectory($validation);
1✔
95
        if ($this->storage->getStorage()->directoryExists($uploadDirectory)) {
1✔
96
            $this->storage->getStorage()->deleteDirectory($uploadDirectory);
×
97
        }
98
        $this->logger->info('Validation[{uid}] : remove output files', [
1✔
99
            'uid' => $validation->getUid(),
1✔
100
        ]);
1✔
101
        $outputDirectory = $this->storage->getOutputDirectory($validation);
1✔
102
        if ($this->storage->getStorage()->directoryExists($outputDirectory)) {
1✔
103
            $this->storage->getStorage()->deleteDirectory($outputDirectory);
×
104
        }
105
        $this->logger->info('Validation[{uid}] : drop validation schema', [
1✔
106
            'uid' => $validation->getUid(),
1✔
107
        ]);
1✔
108
        $this->validationRepository->dropSchema($validation);
1✔
109
        $this->logger->info('Validation[{uid}] : archive removing all files : completed', [
1✔
110
            'uid' => $validation->getUid(),
1✔
111
            'status' => Validation::STATUS_ARCHIVED,
1✔
112
        ]);
1✔
113
        $validation->setStatus(Validation::STATUS_ARCHIVED);
1✔
114
        $this->em->persist($validation);
1✔
115
        $this->em->flush();
1✔
116
    }
117

118
    /**
119
     * Process next pending validation.
120
     *
121
     * @return void
122
     */
123
    public function processOne()
124
    {
125
        $validation = $this->getValidationRepository()->popNextPending();
×
126
        if (is_null($validation)) {
×
NEW
127
            $this->logger->debug('processOne : no validation pending, quitting');
×
128

UNCOV
129
            return;
×
130
        }
131
        $this->currentValidation = $validation;
×
132
        $this->doProcess($validation);
×
133
        $this->currentValidation = null;
×
134
    }
135

136
    /**
137
     * Stop currently running validation (invoked when SIGTERM is received).
138
     *
139
     * @return void
140
     */
141
    public function cancelProcessing()
142
    {
NEW
143
        if (is_null($this->currentValidation)) {
×
NEW
144
            $this->logger->debug('SIGTERM received, no validation in progress');
×
145

NEW
146
            return;
×
147
        }
NEW
148
        $this->logger->warning('Validation[{uid}]: SIGTERM received, changing state to pending', [
×
NEW
149
            'uid' => $this->currentValidation->getUid(),
×
150
        ]);
×
151
        $this->currentValidation->setStatus(Validation::STATUS_PENDING);
×
152
        $this->em->persist($this->currentValidation);
×
153
        $this->em->flush();
×
154
    }
155

156
    /**
157
     * Process pending validation.
158
     *
159
     * @return void
160
     */
161
    private function doProcess(Validation $validation)
162
    {
NEW
163
        $this->logger->info('Validation[{uid}]: process pending validation...', ['uid' => $validation->getUid()]);
×
164

165
        /*
166
         * force usage of popNextPending to avoid concurrency problems.
167
         */
168
        if (Validation::STATUS_PROCESSING !== $validation->getStatus()) {
×
169
            $message = sprintf(
×
170
                'doProcess must be invoked on validation with status %s (current status is %s)',
×
171
                Validation::STATUS_PROCESSING,
×
172
                $validation->getStatus()
×
173
            );
×
174
            $this->logger->error($message, ['uid' => $validation->getUid()]);
×
NEW
175
            throw new \RuntimeException($message);
×
176
        }
177

178
        try {
179
            /*
180
             * get files from storage
181
             */
182
            $this->getZip($validation);
×
183

184
            /*
185
             * pre-validating the names of the files in the zip archive
186
             */
187
            $this->validateZip($validation);
×
188

189
            /*
190
             * unzip dataset
191
             */
192
            $this->unzip($validation);
×
193

194
            /*
195
             * run validator-cli.jar command
196
             */
197
            $this->validatorCli->process($validation);
×
198

199
            /*
200
             * zip normalized results
201
             */
202
            $this->zipNormData($validation);
×
203

204
            /*
205
             * Save validation data to storage
206
             */
207
            $this->saveToStorage($validation);
×
208

209
            /*
210
             * cleanup data
211
             */
212
            $this->cleanUp($validation);
×
213

214
            $validation->setStatus(Validation::STATUS_FINISHED);
×
NEW
215
            $this->logger->info('Validation[{uid}]: validation carried out successfully', ['uid' => $validation->getUid()]);
×
216
        } catch (ZipArchiveValidationException $ex) {
×
217
            $validation->setStatus(Validation::STATUS_ERROR);
×
218
            $validation->setMessage($ex->getMessage());
×
219
            $validation->setResults($ex->getErrors());
×
NEW
220
            $this->logger->error('Validation[{uid}]: {message}: {errors}', ['uid' => $validation->getUid(), 'message' => $ex->getMessage(), 'errors' => $ex->getErrors()]);
×
221
        } catch (\Throwable $th) {
×
222
            $validation->setStatus(Validation::STATUS_ERROR);
×
223
            $validation->setMessage($th->getMessage());
×
NEW
224
            $this->logger->error('Validation[{uid}]: {message}', ['uid' => $validation->getUid(), 'message' => $th->getMessage()]);
×
225
        }
226

227
        $validation->setDateFinish(new \DateTime('now'));
×
228
        $this->em->persist($validation);
×
229
        $this->em->flush();
×
230
    }
231

232
    /**
233
     * Get Zip file from storage to validate.
234
     *
235
     * @return void
236
     */
237
    private function getZip(Validation $validation)
238
    {
239
        $this->logger->info('Validation[{uid}] : get from storage...', [
×
240
            'uid' => $validation->getUid(),
×
241
            'datasetName' => $validation->getDatasetName(),
×
242
        ]);
×
243

244
        $validationDirectory = $this->storage->getDirectory($validation);
×
NEW
245
        $uploadFile = $this->storage->getUploadDirectory($validation).$validation->getDatasetName().'.zip';
×
246

247
        if (!is_dir($validationDirectory)) {
×
248
            mkdir($validationDirectory);
×
249
        }
250

NEW
251
        $zipPath = $validationDirectory.'/'.$validation->getDatasetName().'.zip';
×
252

253
        file_put_contents(
×
254
            $zipPath,
×
255
            $this->storage->getStorage()->read($uploadFile)
×
256
        );
×
257
    }
258

259
    /**
260
     * Pre-validates the names of files in the zip.
261
     *
262
     * @param Validation $validation
263
     *
264
     * @return void
265
     *
266
     * @throws ZipArchiveValidationException
267
     */
268
    private function validateZip($validation)
269
    {
270
        $this->logger->info('Validation[{uid}] : validate zip archive...', [
×
271
            'uid' => $validation->getUid(),
×
272
            'datasetName' => $validation->getDatasetName(),
×
273
        ]);
×
274
        $validationDirectory = $this->storage->getDirectory($validation);
×
NEW
275
        $zipPath = $validationDirectory.'/'.$validation->getDatasetName().'.zip';
×
276
        $errors = $this->zipArchiveValidator->validate($zipPath);
×
277
        if (count($errors) > 0) {
×
278
            throw new ZipArchiveValidationException($errors);
×
279
        }
280
    }
281

282
    /**
283
     * Unzips the compressed dataset.
284
     *
285
     * @return void
286
     */
287
    private function unzip(Validation $validation)
288
    {
289
        $this->logger->info('Validation[{uid}] : extract source archive...', [
×
290
            'uid' => $validation->getUid(),
×
291
            'datasetName' => $validation->getDatasetName(),
×
292
        ]);
×
293
        $validationDirectory = $this->storage->getDirectory($validation);
×
NEW
294
        $zipFilename = $validationDirectory.'/'.$validation->getDatasetName().'.zip';
×
295
        $zip = new \ZipArchive();
×
296

NEW
297
        if (true === $zip->open($zipFilename)) {
×
NEW
298
            $zip->extractTo($validationDirectory.'/'.$validation->getDatasetName());
×
UNCOV
299
            $zip->close();
×
300
        } else {
NEW
301
            throw new \Exception('Zip decompression failed');
×
302
        }
303
    }
304

305
    /**
306
     * Zips the generated normalized data.
307
     *
308
     * @return void
309
     */
310
    private function zipNormData(Validation $validation)
311
    {
312
        $this->logger->info('Validation[{uid}] : compress normalized data...', [
×
313
            'uid' => $validation->getUid(),
×
314
            'datasetName' => $validation->getDatasetName(),
×
315
        ]);
×
316
        $fs = new Filesystem();
×
317

318
        $validationDirectory = $this->storage->getDirectory($validation);
×
NEW
319
        $normDataParentDir = $validationDirectory.'/validation/';
×
320
        $datasetName = $validation->getDatasetName();
×
321

322
        // checking if normalized data is present
NEW
323
        if (!$fs->exists($normDataParentDir.$datasetName)) {
×
324
            return;
×
325
        }
326

NEW
327
        $process = new Process(['zip', '-r', "$datasetName.zip", $datasetName], $normDataParentDir);
×
328
        $process->setTimeout(600);
×
329
        $process->setIdleTimeout(600);
×
330
        $process->run();
×
331

332
        if (!$process->isSuccessful()) {
×
333
            throw new ProcessFailedException($process);
×
334
        }
335
    }
336

337
    /**
338
     * Saves output to storage.
339
     */
340
    private function saveToStorage(Validation $validation)
341
    {
342
        // Saves normalized data to storage
343
        $this->logger->info('Validation[{uid}] : saving normalized data...', [
×
344
            'uid' => $validation->getUid(),
×
345
            'datasetName' => $validation->getDatasetName(),
×
346
        ]);
×
347
        $validationDirectory = $this->storage->getDirectory($validation);
×
NEW
348
        $normDataPath = $validationDirectory.'/validation/'.$validation->getDatasetName().'.zip';
×
349
        $outputDirectory = $this->storage->getOutputDirectory($validation);
×
NEW
350
        if (!$this->storage->getStorage()->directoryExists($outputDirectory)) {
×
351
            $this->storage->getStorage()->createDirectory($outputDirectory);
×
352
        }
NEW
353
        $outputPath = $outputDirectory.$validation->getDatasetName().'.zip';
×
NEW
354
        if ($this->storage->getStorage()->fileExists($outputPath)) {
×
UNCOV
355
            $this->storage->getStorage()->delete($outputPath);
×
356
        }
357
        $stream = fopen($normDataPath, 'r+');
×
358
        $this->storage->getStorage()->writeStream($outputPath, $stream);
×
359
        fclose($stream);
×
360

361
        // Saves validator logs to storage
362
        $this->logger->info('Validation[{uid}] : saving logs...', [
×
363
            'uid' => $validation->getUid(),
×
364
            'datasetName' => $validation->getDatasetName(),
×
365
        ]);
×
NEW
366
        $logPath = $validationDirectory.'/validator-debug.log';
×
NEW
367
        $outputPath = $outputDirectory.'/validator-debug.log';
×
368

369
        $stream = fopen($logPath, 'r+');
×
370
        $this->storage->getStorage()->writeStream($outputPath, $stream);
×
371
        fclose($stream);
×
372
    }
373

374
    /**
375
     * Cleans up temporary files.
376
     *
377
     * @return void
378
     */
379
    private function cleanUp(Validation $validation)
380
    {
381
        $this->logger->info('Validation[{uid}] : cleanup...', [
×
382
            'uid' => $validation->getUid(),
×
383
            'datasetName' => $validation->getDatasetName(),
×
384
        ]);
×
385
        $validationDirectory = $this->storage->getDirectory($validation);
×
386

NEW
387
        $fs = new Filesystem();
×
388
        if ($fs->exists($validationDirectory)) {
×
389
            $this->logger->debug('Validation[{uid}] : rm -rf {uid}/{datasetName}/...', [
×
390
                'uid' => $validation->getUid(),
×
391
                'datasetName' => $validation->getDatasetName(),
×
392
            ]);
×
393
            $fs->remove($validationDirectory);
×
394
        }
395
    }
396

397
    /**
398
     * @return ValidationRepository
399
     */
400
    protected function getValidationRepository()
401
    {
402
        return $this->em->getRepository(Validation::class);
×
403
    }
404
}
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