GenerateRdf   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 209
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 19
eloc 75
dl 0
loc 209
rs 10
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A getStoragePath() 0 3 1
A saveN3() 0 3 1
A saveRdfJson() 0 3 1
A makeReleaseArray() 0 3 1
A updateRelease() 0 3 1
A saveRdfa() 0 3 1
A saveTtl() 0 3 1
A saveNt() 0 3 1
A saveMicrodata() 0 3 1
A runRapper() 0 14 2
A saveJsonLd() 0 14 3
A handle() 0 19 1
A runCurl() 0 15 2
A saveXml() 0 6 1
A __construct() 0 12 1
1
<?php
2
3
namespace App\Jobs;
4
5
use App\Models\Elementset;
6
use App\Models\Release;
7
use App\Models\VocabsModel;
8
use App\Models\Vocabulary;
9
use App\Services\Publish\Git;
10
use apps\frontend\lib\services\jsonldElementsetService;
11
use apps\frontend\lib\services\jsonldVocabularyService;
12
use GuzzleHttp\Client;
13
use Illuminate\Bus\Queueable;
14
use Illuminate\Contracts\Queue\ShouldQueue;
15
use Illuminate\Foundation\Bus\Dispatchable;
16
use Illuminate\Queue\InteractsWithQueue;
17
use Illuminate\Queue\SerializesModels;
18
use Illuminate\Support\Facades\Storage;
19
use Symfony\Component\Process\Exception\ProcessFailedException;
20
use Symfony\Component\Process\Process;
21
22
class GenerateRdf implements ShouldQueue
23
{
24
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
25
    public const  VOCABULARY   = Vocabulary::class;
26
    public const  ELEMENTSET   = Elementset::class;
27
    public const  REPO_ROOT    = 'repos';
28
    public const  PROJECT_ROOT = 'projects';
29
    private const URLARRAY     = [self::VOCABULARY => 'vocabularies/', self::ELEMENTSET => 'elementsets/'];
30
31
    public $timeout            = 180;
32
33
    /** @var int $projectId */
34
    private $projectId;
35
    /** @var string $class */
36
    private $class;
37
    /** @var int $id */
38
    private $id;
39
    /** @var string $filePath */
40
    private $filePath;
41
    /** @var string $fileName */
42
    private $fileName;
43
    private $disk;
44
    /**
45
     * @var VocabsModel
46
     */
47
    private $model;
48
    /**
49
     * @var Release
50
     */
51
    private $release;
52
53
    /**
54
     * Create a new job instance.
55
     *
56
     * @param VocabsModel $model
57
     * @param Release     $release
58
     * @param string      $disk
59
     *
60
     * @throws \GitWrapper\GitException
61
     */
62
    public function __construct(VocabsModel $model, Release $release, $disk = self::REPO_ROOT)
63
    {
64
        $this->id        = $model->id;
65
        $this->model     = $model;
66
        $this->class     = \get_class($model);
67
        $this->projectId = $model->project_id;
68
        $basePath        = parse_url($model->base_domain)['path'];
0 ignored issues
show
Bug introduced by
The property base_domain does not seem to exist on App\Models\VocabsModel. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
69
        $this->filePath  = rtrim(parse_url($model->uri)['path'], '/');
0 ignored issues
show
Bug introduced by
The property uri does not seem to exist on App\Models\VocabsModel. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
70
        $this->fileName  = str_replace_first($basePath, '', parse_url($model->uri)['path']);
71
        $this->disk      = $disk;
72
        Git::initDir($model->project, $this->disk,$release->user);
0 ignored issues
show
Bug introduced by
The property project does not seem to exist on App\Models\VocabsModel. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
Bug introduced by
It seems like $release->user can also be of type null; however, parameter $user of App\Services\Publish\Git::initDir() does only seem to accept App\Models\Access\User\User, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

72
        Git::initDir($model->project, $this->disk,/** @scrutinizer ignore-type */ $release->user);
Loading history...
73
        $this->release = $release;
74
    }
75
76
    /**
77
     * Execute the job.
78
     *
79
     * @return void
80
     * @throws \InvalidArgumentException
81
     */
82
    public function handle(): void
83
    {
84
        //make sure we have a repo and create one if we don't
85
        //get the rdf/xml and store it in the repo
86
        $this->saveXml();
87
        //get the jsonld and store it in the repo
88
        $this->saveJsonLd();
89
        //TODO: make the translators that are run user-selectable as part of the project attributes
90
        //run the translators to generate the other flavours and store in the repo
91
        $this->saveTtl();
92
        $this->saveNt();
93
        //these are the wonky remote translators
94
        $this->saveN3();
95
        $this->saveRdfJson();
96
        $this->saveMicrodata();
97
        $this->saveRdfa();
98
99
        //update the entry in the releaseables table
100
        $this->updateRelease($this->model);
101
    }
102
103
    /**
104
     * @param $mimeType
105
     *
106
     * @return string
107
     */
108
    public function getStoragePath($mimeType): string
109
    {
110
        return Git::getProjectPath($this->projectId) . "{$mimeType}{$this->filePath}.$mimeType";
111
    }
112
113
    public function saveXml()
114
    {
115
        $storagePath = $this->getStoragePath('xml');
116
        $client      = new Client();
117
        $res         = $client->get(url(self::URLARRAY[$this->class] . $this->id . '.rdf'));
118
        Storage::disk($this->disk)->put($storagePath, $res->getBody());
119
    }
120
121
    public function saveJsonLd()
122
    {
123
        $storagePath = $this->getStoragePath('jsonld');
124
        $release     = $this->makeReleaseArray();
125
        initSymfonyDb();
126
        if ($this->class === self::VOCABULARY) {
127
            $vocabulary    = \VocabularyPeer::retrieveByPK($this->id);
128
            $jsonLdService = new jsonldVocabularyService($vocabulary, $release);
129
        }
130
        if ($this->class === self::ELEMENTSET) {
131
            $elementset    = \SchemaPeer::retrieveByPK($this->id);
132
            $jsonLdService = new jsonldElementsetService($elementset, $release);
133
        }
134
        Storage::disk($this->disk)->put($storagePath, $jsonLdService->getJsonLd());
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $jsonLdService does not seem to be defined for all execution paths leading up to this point.
Loading history...
135
    }
136
137
    public function saveTtl()
138
    {
139
        $this->runRapper('ttl', 'turtle');
140
    }
141
142
    public function saveNt()
143
    {
144
        $this->runRapper('nt', 'ntriples');
145
    }
146
147
    public function saveN3()
148
    {
149
        $this->runCurl('n3');
150
    }
151
152
    public function saveRdfJson()
153
    {
154
        $this->runCurl('rdf-json');
155
    }
156
157
    public function saveMicrodata()
158
    {
159
        $this->runCurl('microdata');
160
    }
161
162
    public function saveRdfa()
163
    {
164
        $this->runCurl('rdfa');
165
    }
166
167
    /**
168
     * @return array
169
     */
170
    private function makeReleaseArray(): array
171
    {
172
        return ['tag_name' => $this->release->tag_name, 'published_at' => $this->release->published_at->format('F j, Y')];
173
    }
174
175
    /**
176
     * @throws \Symfony\Component\Process\Exception\RuntimeException
177
     * @throws \Symfony\Component\Process\Exception\ProcessFailedException
178
     * @throws \Symfony\Component\Process\Exception\LogicException
179
     */
180
    private function runRapper($mimeType, $rapperType)
181
    {
182
        $outputPath = $this->getStoragePath($mimeType);
183
        //just to make sure the path exists
184
        Storage::disk($this->disk)->put($outputPath, ' ');
185
        //make sure rapper has the full paths
186
        $sourcePath = Storage::disk($this->disk)->path($this->getStoragePath('xml'));
187
        $outputPath = Storage::disk($this->disk)->path($this->getStoragePath($mimeType));
188
        $process    = new Process("rapper -o {$rapperType} {$sourcePath} > {$outputPath}");
189
        $process->run();
190
191
        // executes after the command finishes
192
        if (! $process->isSuccessful()) {
193
            throw new ProcessFailedException($process);
194
        }
195
    }
196
197
    /**
198
     * @param $mimeType
199
     *
200
     * @throws \Symfony\Component\Process\Exception\InvalidArgumentException
201
     * @throws \Symfony\Component\Process\Exception\ProcessFailedException
202
     * @throws \Symfony\Component\Process\Exception\RuntimeException
203
     * @throws \Symfony\Component\Process\Exception\LogicException
204
     */
205
    public function runCurl($mimeType)
206
    {
207
        $outputPath = $this->getStoragePath($mimeType);
208
        //just to make sure the path exists
209
        Storage::disk($this->disk)->put($outputPath, ' ');
210
        //make sure we have the full paths
211
        $sourcePath = Storage::disk($this->disk)->path($this->getStoragePath('xml'));
212
        $outputPath = Storage::disk($this->disk)->path($this->getStoragePath($mimeType));
213
        $process    = new Process("curl --data-urlencode content@{$sourcePath} http://rdf-translator.appspot.com/convert/xml/{$mimeType}/content > {$outputPath}");
214
        $process->setTimeout(180);
215
        $process->run();
216
217
        // executes after the command finishes
218
        if (! $process->isSuccessful()) {
219
            throw new ProcessFailedException($process);
220
        }
221
    }
222
223
    /**
224
     * @param $vocab
225
     *
226
     * @throws \InvalidArgumentException
227
     */
228
    private function updateRelease(VocabsModel $vocab): void
229
    {
230
        $vocab->releases()->updateExistingPivot($this->release->id, ['published_at' => $this->release->published_at]);
231
    }
232
}
233