TgScraper::validateSchema()   A
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 5
rs 9.2222
cc 6
nc 6
nop 1
1
<?php
2
3
namespace TgScraper;
4
5
use Exception;
6
use GuzzleHttp\Exception\GuzzleException;
7
use InvalidArgumentException;
8
use JetBrains\PhpStorm\ArrayShape;
9
use JsonException;
10
use Psr\Log\LoggerInterface;
11
use Symfony\Component\Yaml\Yaml;
12
use TgScraper\Common\OpenApiGenerator;
13
use TgScraper\Common\SchemaExtractor;
14
use TgScraper\Common\StubCreator;
15
use TgScraper\Constants\Versions;
16
use Throwable;
17
18
/**
19
 * Class TgScraper
20
 * @package TgScraper
21
 */
22
class TgScraper
23
{
24
25
    /**
26
     * Path to templates directory.
27
     */
28
    public const TEMPLATES_DIRECTORY = __DIR__ . '/../templates';
29
30
    /**
31
     * @var string
32
     */
33
    private string $version;
34
    /**
35
     * @var array
36
     */
37
    private array $types;
38
    /**
39
     * @var array
40
     */
41
    private array $methods;
42
43
    /**
44
     * TgScraper constructor.
45
     * @param LoggerInterface $logger
46
     * @param array $schema
47
     */
48
    public function __construct(private LoggerInterface $logger, array $schema)
49
    {
50
        if (!self::validateSchema($schema)) {
51
            throw new InvalidArgumentException('Invalid schema provided');
52
        }
53
        $this->version = $schema['version'] ?? '1.0.0';
54
        $this->types = $schema['types'];
55
        $this->methods = $schema['methods'];
56
    }
57
58
    /**
59
     * @param LoggerInterface $logger
60
     * @param string $url
61
     * @return self
62
     * @throws Throwable
63
     */
64
    public static function fromUrl(LoggerInterface $logger, string $url): self
65
    {
66
        $extractor = SchemaExtractor::fromUrl($logger, $url);
67
        $schema = $extractor->extract();
68
        return new self($logger, $schema);
69
    }
70
71
    /**
72
     * @param LoggerInterface $logger
73
     * @param string $version
74
     * @return self
75
     * @throws Exception
76
     * @throws GuzzleException
77
     */
78
    public static function fromVersion(LoggerInterface $logger, string $version = Versions::LATEST): self
79
    {
80
        $extractor = SchemaExtractor::fromVersion($logger, $version);
81
        $schema = $extractor->extract();
82
        return new self($logger, $schema);
83
    }
84
85
    /**
86
     * @param array $schema
87
     * @return bool
88
     */
89
    public static function validateSchema(array $schema): bool
90
    {
91
        return array_key_exists('version', $schema) and is_string($schema['version']) and
92
            array_key_exists('types', $schema) and is_array($schema['types']) and
93
            array_key_exists('methods', $schema) and is_array($schema['methods']);
94
    }
95
96
    /**
97
     * @param LoggerInterface $logger
98
     * @param string $yaml
99
     * @return TgScraper
100
     */
101
    public static function fromYaml(LoggerInterface $logger, string $yaml): self
102
    {
103
        $data = Yaml::parse($yaml);
104
        return new self($logger, schema: $data);
105
    }
106
107
    /**
108
     * @param LoggerInterface $logger
109
     * @param string $json
110
     * @return TgScraper
111
     * @throws JsonException
112
     */
113
    public static function fromJson(LoggerInterface $logger, string $json): self
114
    {
115
        $data = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
116
        return new self($logger, schema: $data);
117
    }
118
119
    /**
120
     * @param string $directory
121
     * @param string $namespace
122
     * @return void
123
     * @throws Exception
124
     */
125
    public function toStubs(string $directory = '', string $namespace = 'TelegramApi'): void
126
    {
127
        try {
128
            $directory = self::getTargetDirectory($directory);
129
        } catch (Exception $e) {
130
            $this->logger->critical(
131
                'An exception occurred while trying to get the target directory: ' . $e->getMessage()
132
            );
133
            throw $e;
134
        }
135
        $typesDir = $directory . '/Types';
136
        if (!file_exists($typesDir)) {
137
            mkdir($typesDir, 0755);
138
        }
139
        try {
140
            $creator = new StubCreator($this->toArray(), $namespace);
141
        } catch (InvalidArgumentException $e) {
142
            $this->logger->critical(
143
                'An exception occurred while trying to parse the schema: ' . $e->getMessage()
144
            );
145
            throw $e;
146
        }
147
        $code = $creator->generateCode();
148
        foreach ($code['types'] as $className => $type) {
149
            $this->logger->info('Generating class for Type: ' . $className);
150
            $filename = sprintf('%s/Types/%s.php', $directory, $className);
151
            file_put_contents($filename, $type);
152
        }
153
        file_put_contents($directory . '/API.php', $code['api']);
154
    }
155
156
    /**
157
     * @param string $path
158
     * @return string
159
     * @throws Exception
160
     */
161
    public static function getTargetDirectory(string $path): string
162
    {
163
        $result = realpath($path);
164
        if (false === $result) {
165
            if (!mkdir($path, 0755, true)) {
166
                $path = getcwd() . '/gen';
167
                if (!file_exists($path)) {
168
                    mkdir($path, 0755, true);
169
                }
170
            }
171
        }
172
        $result = realpath($path);
173
        if (false === $result) {
174
            throw new Exception('Could not create target directory');
175
        }
176
        return $result;
177
    }
178
179
    /**
180
     * @return array
181
     */
182
    #[ArrayShape([
183
        'version' => "string",
184
        'types' => "array",
185
        'methods' => "array"
186
    ])] public function toArray(): array
187
    {
188
        return [
189
            'version' => $this->version,
190
            'types' => $this->types,
191
            'methods' => $this->methods
192
        ];
193
    }
194
195
    /**
196
     * @return array
197
     */
198
    public function toOpenApi(): array
199
    {
200
        $openapiTemplate = file_get_contents(self::TEMPLATES_DIRECTORY . '/openapi.json');
201
        $openapiData = json_decode($openapiTemplate, true);
202
        $responsesTemplate = file_get_contents(self::TEMPLATES_DIRECTORY . '/responses.json');
203
        $responses = json_decode($responsesTemplate, true);
204
        $openapi = new OpenApiGenerator($responses, $openapiData, $this->types, $this->methods);
205
        $openapi->setVersion($this->version);
206
        return $openapi->getData();
207
    }
208
209
210
    /**
211
     * Thanks to davtur19 (https://github.com/davtur19/TuriBotGen/blob/master/postman.php)
212
     * @return array
213
     */
214
    #[ArrayShape(['info' => "string[]", 'variable' => "string[]", 'item' => "array[]"])]
215
    public function toPostman(): array
216
    {
217
        $template = file_get_contents(self::TEMPLATES_DIRECTORY . '/postman.json');
218
        $result = json_decode($template, true);
219
        $result['info']['version'] = $this->version;
220
        foreach ($this->methods as $method) {
221
            $formData = [];
222
            if (!empty($method['fields'])) {
223
                foreach ($method['fields'] as $field) {
224
                    $data = [
225
                        'key' => $field['name'],
226
                        'disabled' => $field['optional'],
227
                        'description' => sprintf(
228
                            '%s. %s',
229
                            $field['optional'] ? 'Optional' : 'Required',
230
                            $field['description']
231
                        ),
232
                        'type' => 'text'
233
                    ];
234
                    $default = $field['default'] ?? null;
235
                    if (!empty($default)) {
236
                        $data['value'] = (string)$default;
237
                    }
238
                    $formData[] = $data;
239
                }
240
            }
241
            $result['item'][] = [
242
                'name' => $method['name'],
243
                'request' => [
244
                    'method' => 'POST',
245
                    'body' => [
246
                        'mode' => 'formdata',
247
                        'formdata' => $formData
248
                    ],
249
                    'url' => [
250
                        'raw' => 'https://api.telegram.org/bot{{token}}/' . $method['name'],
251
                        'protocol' => 'https',
252
                        'host' => [
253
                            'api',
254
                            'telegram',
255
                            'org'
256
                        ],
257
                        'path' => [
258
                            'bot{{token}}',
259
                            $method['name']
260
                        ]
261
                    ],
262
                    'description' => $method['description']
263
                ]
264
            ];
265
        }
266
        return $result;
267
    }
268
269
}
270