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

IGNF / validator-api / 14596906002

22 Apr 2025 02:09PM UTC coverage: 33.386% (-12.1%) from 45.455%
14596906002

push

github

web-flow
Merge pull request #78 from IGNF/fix_timeout_error

Fix timeout error

0 of 61 new or added lines in 3 files covered. (0.0%)

72 existing lines in 5 files now uncovered.

210 of 629 relevant lines covered (33.39%)

2.29 hits per line

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

0.0
/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\Storage\ValidationsStorage;
8
use Doctrine\ORM\EntityManagerInterface;
9
use Psr\Log\LoggerInterface;
10
use RuntimeException;
11
use Symfony\Component\Filesystem\Filesystem;
12
use Symfony\Component\Process\Exception\ProcessFailedException;
13
use Symfony\Component\Process\Process;
14
use League\Flysystem\FilesystemOperator;
15

16
class ValidationManager
17
{
18

19
    /**
20
     * @var EntityManagerInterface
21
     */
22
    private $em;
23

24
    /**
25
     * @var ValidationsStorage
26
     */
27
    private $storage;
28

29
    /**
30
     * @var ValidatorCLI
31
     */
32
    private $validatorCli;
33

34
    /**
35
     * @var LoggerInterface
36
     */
37
    private $logger;
38

39
    /**
40
     * @var ZipArchiveValidator
41
     */
42
    private $zipArchiveValidator;
43

44
    /**
45
     * @var FilesystemOperator
46
     */
47
    private $dataStorage;
48

49
    /**
50
     * Current validation (in order to handle SIGTERM)
51
     * @var Validation
52
     */
53
    private $currentValidation = null;
54

55
    public function __construct(
56
        EntityManagerInterface $em,
57
        ValidationsStorage $storage,
58
        FilesystemOperator $dataStorage,
59
        ValidatorCLI $validatorCli,
60
        ZipArchiveValidator $zipArchiveValidator,
61
        LoggerInterface $logger
62
    ) {
UNCOV
63
        $this->em = $em;
×
UNCOV
64
        $this->storage = $storage;
×
UNCOV
65
        $this->dataStorage = $dataStorage;
×
UNCOV
66
        $this->validatorCli = $validatorCli;
×
UNCOV
67
        $this->zipArchiveValidator = $zipArchiveValidator;
×
UNCOV
68
        $this->logger = $logger;
×
69
    }
70

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

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

116
    /**
117
     * Process next pending validation.
118
     *
119
     * @return void
120
     */
121
    public function processOne()
122
    {
123
        $validation = $this->getValidationRepository()->popNextPending();
×
124
        if (is_null($validation)) {
×
125
            $this->logger->debug("processOne : no validation pending, quitting");
×
126
            return;
×
127
        }
128
        $this->currentValidation = $validation;
×
129
        $this->doProcess($validation);
×
130
        $this->currentValidation = null;
×
131
    }
132

133
    /**
134
     * Stop currently running validation (invoked when SIGTERM is received)
135
     *
136
     * @return void
137
     */
138
    public function cancelProcessing(){
139
        if ( is_null($this->currentValidation) ){
×
140
            $this->logger->debug("SIGTERM received, no validation in progress");
×
141
            return ;
×
142
        }
143
        $this->logger->warning("Validation[{uid}]: SIGTERM received, changing state to pending",[
×
144
            "uid" => $this->currentValidation->getUid()
×
145
        ]);
×
146
        $this->currentValidation->setStatus(Validation::STATUS_PENDING);
×
147
        $this->em->persist($this->currentValidation);
×
148
        $this->em->flush();
×
149
    }
150

151
    /**
152
     * Process pending validation
153
     *
154
     * @param Validation $validation
155
     * @return void
156
     */
157
    private function doProcess(Validation $validation)
158
    {
159
        $this->logger->info("Validation[{uid}]: process pending validation...", ['uid' => $validation->getUid()]);
×
160

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

174
        try {
175
            /*
176
             * get files from storage
177
             */
178
            $this->getZip($validation);
×
179

180
            /*
181
             * pre-validating the names of the files in the zip archive
182
             */
183
            $this->validateZip($validation);
×
184

185
            /*
186
             * unzip dataset
187
             */
188
            $this->unzip($validation);
×
189

190
            /*
191
             * run validator-cli.jar command
192
             */
193
            $this->validatorCli->process($validation);
×
194

195
            /*
196
             * zip normalized results
197
             */
198
            $this->zipNormData($validation);
×
199

200
            /*
201
             * Save validation data to storage
202
             */
203
            $this->saveToStorage($validation);
×
204

205
            /*
206
             * cleanup data
207
             */
208
            $this->cleanUp($validation);
×
209

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

223
        $validation->setDateFinish(new \DateTime('now'));
×
224
        $this->em->persist($validation);
×
225
        $this->em->flush();
×
226
    }
227

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

240
        $validationDirectory = $this->storage->getDirectory($validation);
×
NEW
241
        $uploadFile = $this->storage->getUploadDirectory($validation) . $validation->getDatasetName() . '.zip';
×
242

243
        if (!is_dir($validationDirectory)) {
×
244
            mkdir($validationDirectory);
×
245
        }
246

247
        $zipPath = $validationDirectory . '/' . $validation->getDatasetName() . '.zip';
×
248

249
        file_put_contents(
×
250
            $zipPath,
×
251
            $this->dataStorage->read($uploadFile)
×
252
        );
×
253
    }
254

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

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

292
        if ($zip->open($zipFilename) === true) {
×
293
            $zip->extractTo($validationDirectory . '/' . $validation->getDatasetName());
×
294
            $zip->close();
×
295
        } else {
296
            throw new \Exception("Zip decompression failed");
×
297
        }
298
    }
299

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

314
        $validationDirectory = $this->storage->getDirectory($validation);
×
315
        $normDataParentDir = $validationDirectory . '/validation/';
×
316
        $datasetName = $validation->getDatasetName();
×
317

318
        // checking if normalized data is present
319
        if (!$fs->exists($normDataParentDir . $datasetName)) {
×
320
            return;
×
321
        }
322

323
        $process = new Process(["zip","-r","$datasetName.zip",$datasetName],$normDataParentDir);
×
324
        $process->setTimeout(600);
×
325
        $process->setIdleTimeout(600);
×
326
        $process->run();
×
327

328
        if (!$process->isSuccessful()) {
×
329
            throw new ProcessFailedException($process);
×
330
        }
331
    }
332

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

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

NEW
365
        $stream = fopen($logPath, 'r+');
×
NEW
366
        $this->dataStorage->writeStream($outputPath, $stream);
×
UNCOV
367
        fclose($stream);
×
368
    }
369

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

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

393
    /**
394
     * @return ValidationRepository
395
     */
396
    protected function getValidationRepository()
397
    {
398
        return $this->em->getRepository(Validation::class);
×
399
    }
400

401
}
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