Added 'version' field (might not work on older versions), improved Postman schema, initial OpenAPI implementation

This commit is contained in:
Sys 2021-07-31 17:01:46 +02:00
parent 3eef215858
commit 137960dea2
No known key found for this signature in database
GPG Key ID: 3CD2C29F8AB39BFD
4 changed files with 226 additions and 23 deletions

View File

@ -33,13 +33,33 @@ class SchemaExtractor
'answerPreCheckoutQuery' 'answerPreCheckoutQuery'
]; ];
private Dom $dom;
private string $version;
/** /**
* SchemaExtractor constructor. * SchemaExtractor constructor.
* @param LoggerInterface $logger * @param LoggerInterface $logger
* @param string $url * @param string $url
* @throws ChildNotFoundException
* @throws CircularException
* @throws ClientExceptionInterface
* @throws ContentLengthException
* @throws LogicalException
* @throws StrictException
* @throws Throwable
*/ */
public function __construct(private LoggerInterface $logger, private string $url = Versions::LATEST) public function __construct(private LoggerInterface $logger, private string $url = Versions::LATEST)
{ {
$this->dom = new Dom();
try {
$this->dom->loadFromURL($this->url);
} catch (Throwable $e) {
$this->logger->critical(sprintf('Unable to load data from URL "%s": %s', $this->url, $e->getMessage()));
throw $e;
}
$this->version = $this->parseVersion();
$this->logger->info('Bot API version: ' . $this->version);
} }
/** /**
@ -76,6 +96,28 @@ class SchemaExtractor
return ['description' => $description, 'table' => $table, 'extended_by' => $extendedBy]; return ['description' => $description, 'table' => $table, 'extended_by' => $extendedBy];
} }
private function parseVersion(): string
{
/** @var Dom\Node\AbstractNode $element */
$element = $this->dom->find('h3')[0];
$tag = '';
while ($tag != 'p') {
try {
$element = $element->nextSibling();
} catch (ChildNotFoundException | ParentNotFoundException) {
continue;
}
$tag = $element->tag->name();
}
$versionNumbers = explode('.', str_replace('Bot API ', '', $element->innerText));
return sprintf(
'%s.%s.%s',
$versionNumbers[0] ?? '1',
$versionNumbers[1] ?? '0',
$versionNumbers[2] ?? '0'
);
}
/** /**
* @return array * @return array
* @throws ChildNotFoundException * @throws ChildNotFoundException
@ -88,28 +130,24 @@ class SchemaExtractor
* @throws ClientExceptionInterface * @throws ClientExceptionInterface
* @throws Throwable * @throws Throwable
*/ */
#[ArrayShape(['version' => "string", 'methods' => "array", 'types' => "array"])]
public function extract(): array public function extract(): array
{ {
$dom = new Dom;
try { try {
$dom->loadFromURL($this->url); $elements = $this->dom->find('h4');
} catch (Throwable $e) { } catch (Throwable $e) {
$this->logger->critical(sprintf('Unable to load data from URL "%s": %s', $this->url, $e->getMessage())); $this->logger->critical(sprintf('Unable to load data from URL "%s": %s', $this->url, $e->getMessage()));
throw $e; throw $e;
} }
try { $data = ['version' => $this->version];
$elements = $dom->find('h4');
} catch (Throwable $e) {
$this->logger->critical(sprintf('Unable to load data from URL "%s": %s', $this->url, $e->getMessage()));
throw $e;
}
$data = [];
/* @var Dom\Node\AbstractNode $element */ /* @var Dom\Node\AbstractNode $element */
foreach ($elements as $element) { foreach ($elements as $element) {
if (!str_contains($name = $element->text, ' ')) { if (!str_contains($name = $element->text, ' ')) {
$isMethod = lcfirst($name) == $name; $isMethod = lcfirst($name) == $name;
$path = $isMethod ? 'methods' : 'types'; $path = $isMethod ? 'methods' : 'types';
['description' => $description, 'table' => $table, 'extended_by' => $extendedBy] = self::parseNode($element); ['description' => $description, 'table' => $table, 'extended_by' => $extendedBy] = self::parseNode(
$element
);
$data[$path][] = self::generateElement( $data[$path][] = self::generateElement(
$name, $name,
trim($description), trim($description),

View File

@ -2,6 +2,7 @@
namespace TgScraper; namespace TgScraper;
use BadMethodCallException;
use Exception; use Exception;
use InvalidArgumentException; use InvalidArgumentException;
use JetBrains\PhpStorm\ArrayShape; use JetBrains\PhpStorm\ArrayShape;
@ -27,6 +28,11 @@ use Throwable;
class Generator class Generator
{ {
/**
* Path to templates directory.
*/
public const TEMPLATES_DIRECTORY = __DIR__ . '/../templates';
/** /**
* @var array * @var array
*/ */
@ -53,8 +59,8 @@ class Generator
?array $schema = null ?array $schema = null
) { ) {
if (empty($schema)) { if (empty($schema)) {
$extractor = new SchemaExtractor($this->logger, $this->url);
try { try {
$extractor = new SchemaExtractor($this->logger, $this->url);
$this->logger->info('Schema not provided, extracting from URL.'); $this->logger->info('Schema not provided, extracting from URL.');
$schema = $extractor->extract(); $schema = $extractor->extract();
} catch (Throwable $e) { } catch (Throwable $e) {
@ -179,6 +185,14 @@ class Generator
return Yaml::dump($this->schema, $inline, $indent, $flags); return Yaml::dump($this->schema, $inline, $indent, $flags);
} }
/**
* @return string
*/
public function toOpenApi(): string
{
throw new BadMethodCallException('Not implemented');
}
/** /**
* Thanks to davtur19 (https://github.com/davtur19/TuriBotGen/blob/master/postman.php) * Thanks to davtur19 (https://github.com/davtur19/TuriBotGen/blob/master/postman.php)
@ -189,24 +203,16 @@ class Generator
public function toPostman( public function toPostman(
int $options = 0 int $options = 0
): string { ): string {
$result = [ $template = file_get_contents(self::TEMPLATES_DIRECTORY . '/postman.json');
'info' => [ $result = json_decode($template, true);
'name' => 'Telegram Bot API', $result['info']['version'] = $this->schema['version'];
'schema' => 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
],
'variable' => [
'key' => 'token',
'value' => '1234:AAbbcc',
'type' => 'string'
]
];
foreach ($this->schema['methods'] as $method) { foreach ($this->schema['methods'] as $method) {
$formData = []; $formData = [];
if (!empty($method['fields'])) { if (!empty($method['fields'])) {
foreach ($method['fields'] as $field) { foreach ($method['fields'] as $field) {
$formData[] = [ $formData[] = [
'key' => $field['name'], 'key' => $field['name'],
'value' => '', 'disabled' => !$field['required'],
'description' => sprintf( 'description' => sprintf(
'%s. %s', '%s. %s',
$field['required'] ? 'Required' : 'Optional', $field['required'] ? 'Required' : 'Optional',

144
templates/openapi.json Normal file
View File

@ -0,0 +1,144 @@
{
"openapi": "3.0.0",
"info": {
"title": "Telegram Bot API",
"description": "Auto-generated OpenAPI schema by TGScraper.",
"version": "1.0.0"
},
"servers": [
{
"url": "https://api.telegram.org/bot{token}",
"variables": {
"token": {
"default": "1234:AAbbcc",
"description": "Bot's unique authentication token, given by @BotFather."
}
}
}
],
"externalDocs": {
"description": "Official Telegram Bot API documentation",
"url": "https://core.telegram.org/bots/api"
},
"components": {
"responses": {
"BadRequest": {
"description": "Bad request, you have provided malformed data.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"Unauthorized": {
"description": "The authorization token is invalid or it has been revoked.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"Forbidden": {
"description": "This action is forbidden.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"NotFound": {
"description": "The specified resource was not found.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"Conflict": {
"description": "There is a conflict with another instance using webhook or polling.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"TooManyRequests": {
"description": "You're doing too many requests, retry after a while.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
},
"schemas": {
"Response": {
"type": "object",
"required": [
"ok"
],
"properties": {
"ok": {
"type": "boolean"
}
}
},
"Success": {
"allOf": [
{
"$ref": "#/components/schemas/Response"
},
{
"type": "object",
"required": [
"result"
],
"properties": {
"result": {
"type": "object"
}
}
}
]
},
"Error": {
"allOf": [
{
"$ref": "#/components/schemas/Response"
},
{
"type": "object",
"required": [
"error_code",
"description"
],
"properties": {
"error_code": {
"type": "integer"
},
"description": {
"type": "string"
},
"parameters": {
"$ref": "#/components/schemas/ResponseParameters"
}
}
}
]
}
}
},
"paths": {}
}

15
templates/postman.json Normal file
View File

@ -0,0 +1,15 @@
{
"info": {
"name": "Telegram Bot API",
"description": "Auto-generated Postman collection by TGScraper.",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"version": "1.0.0"
},
"variable": {
"key": "token",
"description": "Bot's unique authentication token, given by @BotFather.",
"type": "string",
"value": "1234:AAbbcc"
},
"item": []
}