Loader   A
last analyzed

Complexity

Total Complexity 3

Size/Duplication

Total Lines 36
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 3
eloc 13
dl 0
loc 36
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A loadVersion() 0 11 2
A process() 0 11 1
1
<?php
2
3
use Illuminate\Support\Collection;
4
5
require __DIR__ . '/../vendor/autoload.php';
6
7
class Swagger
8
{
9
    /**
10
     * @var Collection
11
     */
12
    public $map;
13
14
    /**
15
     * @var Collection
16
     */
17
    public $models;
18
19
    /**
20
     * @var Collection
21
     */
22
    public $specs;
23
24
    /**
25
     * @var string
26
     */
27
    public $version;
28
29
    public function __construct($file, $version)
30
    {
31
        $this->map = collect([]);
32
        $this->models = collect([]);
33
        $this->specs = collect(json_decode(file_get_contents($file), true));
34
        $this->version = $version;
35
    }
36
37
    public function convertDefinitionToModel($class, $attributes)
38
    {
39
        $template = <<<EOF
40
<?php
41
42
namespace {{ Namespace }};
43
44
use Carbon\Carbon;
45
use Spinen\ConnectWise\Support\Model;
46
47
/**
48
 * Class {{ Class }} Version {{ Version }}
49
 *
50
 * {{ Description }}
51
 *
52
{{ Properties }}
53
 */
54
class {{ Class }} extends Model
55
{
56
    /**
57
     * Properties that need to be casts to a specific object or type
58
     *
59
     * @var array
60
     */
61
    protected \$casts = [
62
{{ Casts }}
63
    ];
64
}
65
66
EOF;
67
68
        // Make array of types keyed by property
69
        $property_type = collect($attributes['properties'])
70
            ->map(function ($attributes) {
71
                return $this->parseType($attributes);
72
            });
73
74
        $casts = $property_type->map(function ($type, $property) {
75
            $primitives = [
76
                'array',
77
                'boolean',
78
                'float',
79
                'integer',
80
                'object',
81
                'string',
82
            ];
83
84
            return "        '${property}' => " . ((in_array($type, $primitives)) ? "'${type}'" : "${type}::class");
85
        })
86
                               ->values()
87
                               ->sort()
88
                               ->implode(",\n");
89
90
        $properties = $property_type->map(function ($type, $property) {
91
            return " * @property ${type} $${property}";
92
        })
93
                                    ->values()
94
                                    ->sort()
95
                                    ->implode("\n");
96
97
        $model = preg_replace('|{{ Namespace }}|u', $this->getNamespace(), $template);
98
        $model = preg_replace('|{{ Version }}|u', $this->version, $model);
99
        $model = preg_replace('|{{ Class }}|u', $class, $model);
100
        $model = preg_replace('|{{ Description }}|u', $attributes['description'] ?? 'Model for ' . $class, $model);
101
        $model = preg_replace('|{{ Casts }}|u', $casts, $model);
102
        $model = preg_replace('|{{ Properties }}|u', $properties, $model);
103
104
        return $model;
105
    }
106
107
    public function getNamespace($class = null)
108
    {
109
        return implode(
110
            '\\',
111
            array_filter(
112
                [
113
                    'Spinen',
114
                    'ConnectWise',
115
                    'Models',
116
                    $this->version,
117
                    $this->getTitle(),
118
                    $class,
119
                ]
120
            )
121
        );
122
    }
123
124
    public function getTitle()
125
    {
126
        return preg_replace('/\\s+API$/u', '', $this->specs['info']['title']);
127
    }
128
129
    public function parseDefinitions()
130
    {
131
        foreach ($this->specs['definitions'] as $class => $attributes) {
132
            $this->models[$class] = $this->convertDefinitionToModel($class, $attributes);
133
        };
134
135
        return $this;
136
    }
137
138
    public function parseType(array $attributes)
139
    {
140
        // Cast to another model
141
        if (array_key_exists('$ref', $attributes)) {
142
            return collect(explode('/', $attributes['$ref']))->last();
143
        }
144
145
        $format = $attributes['format'] ?? null;
146
        $type = $attributes['type'];
147
148
        // NOTE: We should consider supporting bigint, but for now, just int
149
        if (in_array($format, ['int32', 'int64']) || 'integer' === $type) {
150
            return 'integer';
151
        }
152
153
        if (in_array($format, ['double', 'float']) || 'number' === $type) {
154
            return 'float';
155
        }
156
157
        if (in_array($format, ['date', 'date-time'])) {
158
            return 'Carbon';
159
        }
160
161
        // NOTE: Would be nice to parse the 'items' key to get the types of the properties
162
        if ('array' === $type) {
163
            return 'array';
164
        }
165
166
        if ('boolean' === $type) {
167
            return 'boolean';
168
        }
169
170
        if ('object' === $type) {
171
            return 'object';
172
        }
173
174
        // default to string
175
        return 'string';
176
    }
177
178
    public function parsePaths()
179
    {
180
        foreach ($this->specs['paths'] as $path => $actions) {
181
            if (array_key_exists('get', $actions) && array_key_exists('schema', $actions['get']['responses'][200])) {
182
                $schema = $actions['get']['responses'][200]['schema'];
183
184
                // Single item or collection of items
185
                $ref = $schema['$ref'] ?? $schema['items']['$ref'];
186
187
                $this->map[$path] = $this->getNamespace(collect(explode('/', $ref))->last());
188
            }
189
        };
190
191
        return $this;
192
    }
193
}
194
195
class Loader
196
{
197
    /**
198
     * @var Collection
199
     */
200
    public $swaggers;
201
202
    /**
203
     * @var string
204
     */
205
    public $version;
206
207
    public function loadVersion($version)
208
    {
209
        $this->version = $version;
210
211
        $this->swaggers = collect([]);
212
213
        foreach (glob(__DIR__ . "/swagger/" . $this->version . "/*.json") as $swaggerfile) {
214
            $this->swaggers->push(new Swagger($swaggerfile, $this->version));
215
        }
216
217
        return $this;
218
    }
219
220
    public function process()
221
    {
222
        $this->swaggers->each(
223
            function (Swagger $swagger) {
224
                $swagger->parseDefinitions();
225
226
                $swagger->parsePaths();
227
            }
228
        );
229
230
        return $this;
231
    }
232
}
233
234
/** @var Loader $loader */
235
$loader = new Loader();
236
237
foreach (glob(__DIR__ . "/swagger/*") as $version) {
238
    $version = basename($version);
239
240
    echo "Working on version ${version}\n";
241
242
    $loader->loadVersion($version)
243
           ->process()->swaggers->each(function (Swagger $swagger) use ($version) {
244
                $directory = __DIR__ . '/Generated/' . $version;
245
246
                echo "Writing " . $swagger->getTitle() . "\n";
247
248
                mkdir($directory . '/' . $swagger->getTitle(), 0755, true);
249
250
                $swagger->models->each(function ($contents, $model) use ($directory, $swagger) {
251
                    file_put_contents($directory . '/' . $swagger->getTitle() . '/' . $model . '.php', $contents);
252
                });
253
254
                echo "Appending map\n";
255
256
                $map = collect(json_decode(@ file_get_contents($directory . '/map.json'), true))
0 ignored issues
show
Bug introduced by
It seems like @file_get_contents($directory . '/map.json') can also be of type false; however, parameter $json of json_decode() does only seem to accept string, 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

256
                $map = collect(json_decode(/** @scrutinizer ignore-type */ @ file_get_contents($directory . '/map.json'), true))
Loading history...
257
                    ->merge($swagger->map)
258
                    ->sort();
259
260
                file_put_contents($directory . '/map.json', $map->toJson());
261
           });
262
}
263
264
265