Passed
Push — master ( 137960...4d87d9 )
by Sys
11:11
created

TgScraper::validateSchema()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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