From b46d1a7b39f055d1c173b4c8a291739483a6fd42 Mon Sep 17 00:00:00 2001 From: Sean Molenaar Date: Tue, 28 Oct 2025 16:22:25 +0100 Subject: [PATCH 1/3] feat: switch to new CLI parser --- composer.json | 4 +- composer.lock | 547 +++++++++++++++++++++++----------------- phpdraft | 99 +------- src/PHPDraft/In/CLI.php | 110 ++++++++ 4 files changed, 434 insertions(+), 326 deletions(-) create mode 100644 src/PHPDraft/In/CLI.php diff --git a/composer.json b/composer.json index 393fb2a..3b72aea 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,6 @@ ], "require": { "php": "^8.1", - "vanilla/garden-cli": "~4.0", "michelf/php-markdown": "~2.0", "lukasoppermann/http-status": "~4.0", "ext-json": "*", @@ -27,7 +26,8 @@ "twig/twig": "^3.0", "twig/markdown-extra": "^3.0", "matthiasmullie/minify": "^1.3", - "rize/uri-template": "*" + "rize/uri-template": "*", + "splitbrain/php-cli": "^1.3" }, "require-dev": { "lunr/halo": "~0.11.0", diff --git a/composer.lock b/composer.lock index 491663e..0fd9ae0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e5e970b0aeb650a88b05e939faa17874", + "content-hash": "9e15bc4af7c7e4caf95a3c695199d35f", "packages": [ { "name": "lukasoppermann/http-status", @@ -243,56 +243,6 @@ }, "time": "2022-09-26T12:21:08+00:00" }, - { - "name": "psr/log", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/3.0.2" - }, - "time": "2024-09-11T13:17:53+00:00" - }, { "name": "rize/uri-template", "version": "0.4.0", @@ -357,6 +307,61 @@ ], "time": "2024-11-27T12:13:42+00:00" }, + { + "name": "splitbrain/php-cli", + "version": "1.3.4", + "source": { + "type": "git", + "url": "https://github.com/splitbrain/php-cli.git", + "reference": "4e669f38f660b0e9f76ed14dda7f12bff390f94f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/splitbrain/php-cli/zipball/4e669f38f660b0e9f76ed14dda7f12bff390f94f", + "reference": "4e669f38f660b0e9f76ed14dda7f12bff390f94f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^8" + }, + "suggest": { + "psr/log": "Allows you to make the CLI available as PSR-3 logger" + }, + "type": "library", + "autoload": { + "psr-4": { + "splitbrain\\phpcli\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Gohr", + "email": "andi@splitbrain.org" + } + ], + "description": "Easy command line scripts for PHP with opt parsing and color output. No dependencies", + "keywords": [ + "argparse", + "cli", + "command line", + "console", + "getopt", + "optparse", + "terminal" + ], + "support": { + "issues": "https://github.com/splitbrain/php-cli/issues", + "source": "https://github.com/splitbrain/php-cli/tree/1.3.4" + }, + "time": "2025-03-15T09:02:01+00:00" + }, { "name": "symfony/deprecation-contracts", "version": "v3.6.0", @@ -426,7 +431,7 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -485,7 +490,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -496,6 +501,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -505,7 +514,7 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", @@ -566,7 +575,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -577,6 +586,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -734,61 +747,6 @@ } ], "time": "2025-05-03T07:21:55+00:00" - }, - { - "name": "vanilla/garden-cli", - "version": "v4.0", - "source": { - "type": "git", - "url": "https://github.com/vanilla/garden-cli.git", - "reference": "e7e2f655b04661e9808ec7d51a381176e9bc981d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/vanilla/garden-cli/zipball/e7e2f655b04661e9808ec7d51a381176e9bc981d", - "reference": "e7e2f655b04661e9808ec7d51a381176e9bc981d", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": ">=8.1", - "psr/log": "^3.0" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.8", - "phpdocumentor/reflection-docblock": "^5.3", - "phpunit/phpunit": "^8", - "vanilla/garden-container": "^3.0", - "vanilla/standards": "^1.3", - "vimeo/psalm": "^4.0" - }, - "suggest": { - "ext-pdo": "Required for the DbUtils class.", - "phpdocumentor/reflection-docblock": "Required for the CliApplication functionality.", - "vanilla/garden-container": "Required for the CliApplication functionality." - }, - "type": "library", - "autoload": { - "psr-4": { - "Garden\\Cli\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Todd Burry", - "email": "todd@vanillaforums.com" - } - ], - "description": "A full-featured, yet ridiculously simple commandline parser for your next php cli script. Stop fighting with getopt().", - "support": { - "issues": "https://github.com/vanilla/garden-cli/issues", - "source": "https://github.com/vanilla/garden-cli/tree/v4.0" - }, - "time": "2023-01-24T20:33:46+00:00" } ], "packages-dev": [ @@ -840,16 +798,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.13.3", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "faed855a7b5f4d4637717c2b3863e277116beb36" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/faed855a7b5f4d4637717c2b3863e277116beb36", - "reference": "faed855a7b5f4d4637717c2b3863e277116beb36", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -888,7 +846,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.3" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -896,20 +854,20 @@ "type": "tidelift" } ], - "time": "2025-07-05T12:25:42+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nikic/php-parser", - "version": "v5.5.0", + "version": "v5.6.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" + "reference": "3a454ca033b9e06b63282ce19562e892747449bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", + "reference": "3a454ca033b9e06b63282ce19562e892747449bb", "shasum": "" }, "require": { @@ -928,7 +886,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -952,9 +910,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" }, - "time": "2025-05-31T08:24:38+00:00" + "time": "2025-10-21T19:32:17+00:00" }, { "name": "phar-io/manifest", @@ -1076,16 +1034,16 @@ }, { "name": "phing/phing", - "version": "3.0.1", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/phingofficial/phing.git", - "reference": "96d3b6f37b6b63a710ae7daf1c50b5c28151e695" + "reference": "816739d12a48e1b630a159d1885e04dd54bf80b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phingofficial/phing/zipball/96d3b6f37b6b63a710ae7daf1c50b5c28151e695", - "reference": "96d3b6f37b6b63a710ae7daf1c50b5c28151e695", + "url": "https://api.github.com/repos/phingofficial/phing/zipball/816739d12a48e1b630a159d1885e04dd54bf80b6", + "reference": "816739d12a48e1b630a159d1885e04dd54bf80b6", "shasum": "" }, "require": { @@ -1094,10 +1052,12 @@ "ext-libxml": "*", "ext-simplexml": "*", "ext-xml": "*", - "php": ">= 7.4", - "sebastian/version": "^3.0|^4.0|^5.0", - "symfony/console": "^5.3.10|^6.0|^7.0", - "symfony/yaml": "^5.0|^6.0|^7.0" + "php": ">= 8.1", + "sebastian/version": "^3.0|^4.0|^5.0|^6.0", + "symfony/console": "^6.4.22|^7.0", + "symfony/filesystem": "^5.4.45|^6.4", + "symfony/string": "^6.3.12", + "symfony/yaml": "^6.3.12" }, "replace": { "phing/task-analyzers": "self.version", @@ -1138,12 +1098,13 @@ "ext-xsl": "*", "ext-zip": "*", "friendsofphp/php-cs-fixer": "^3.0", - "guzzlehttp/guzzle": "^7.2", + "guzzlehttp/guzzle": "^7.9", + "guzzlehttp/promises": "^2.0.3", "jawira/plantuml-client": "^1.0", "jawira/plantuml-encoding": "^1.0", "mehr-als-nix/parallel": "^v1.0", "mikey179/vfsstream": "2.0.x-dev", - "monolog/monolog": "^2.2", + "monolog/monolog": "^3.9", "pdepend/pdepend": "^2.9", "pear/archive_tar": "^1.4", "pear/console_getopt": "^v1.4.3", @@ -1157,13 +1118,13 @@ "pear/versioncontrol_svn": "^0.7.0", "phing/phing-composer-configurator": "dev-master", "phpmd/phpmd": "^2.14", - "phpstan/phpstan": "^0.12.87 || ^1.0", - "phpunit/phpunit": "^9.5.10", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^9.6.21", "psr/http-message": "^2.0", "roave/security-advisories": "dev-master", - "scssphp/scssphp": "^1.13", + "scssphp/scssphp": "^2.0", "siad007/versioncontrol_hg": "^1.0", - "smarty/smarty": "^5.0", + "smarty/smarty": "^5.4.2", "squizlabs/php_codesniffer": "^3.5", "symfony/config": "^5.2|^6.0", "symfony/dependency-injection": "^5.2|^6.0", @@ -1354,7 +1315,7 @@ "type": "patreon" } ], - "time": "2024-12-04T19:56:50+00:00" + "time": "2025-07-25T18:55:19+00:00" }, { "name": "phpstan/extension-installer", @@ -1406,16 +1367,11 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.27", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "3a6e423c076ab39dfedc307e2ac627ef579db162" - }, + "version": "1.12.32", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/3a6e423c076ab39dfedc307e2ac627ef579db162", - "reference": "3a6e423c076ab39dfedc307e2ac627ef579db162", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/2770dcdf5078d0b0d53f94317e06affe88419aa8", + "reference": "2770dcdf5078d0b0d53f94317e06affe88419aa8", "shasum": "" }, "require": { @@ -1460,7 +1416,7 @@ "type": "github" } ], - "time": "2025-05-21T20:51:45+00:00" + "time": "2025-09-30T10:16:31+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -1612,16 +1568,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "11.0.10", + "version": "11.0.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "1a800a7446add2d79cc6b3c01c45381810367d76" + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1a800a7446add2d79cc6b3c01c45381810367d76", - "reference": "1a800a7446add2d79cc6b3c01c45381810367d76", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", "shasum": "" }, "require": { @@ -1678,7 +1634,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/show" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.11" }, "funding": [ { @@ -1698,7 +1654,7 @@ "type": "tidelift" } ], - "time": "2025-06-18T08:56:18+00:00" + "time": "2025-08-27T14:37:49+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1947,16 +1903,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.27", + "version": "11.5.42", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "446d43867314781df7e9adf79c3ec7464956fd8f" + "reference": "1c6cb5dfe412af3d0dfd414cfd110e3b9cfdbc3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/446d43867314781df7e9adf79c3ec7464956fd8f", - "reference": "446d43867314781df7e9adf79c3ec7464956fd8f", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1c6cb5dfe412af3d0dfd414cfd110e3b9cfdbc3c", + "reference": "1c6cb5dfe412af3d0dfd414cfd110e3b9cfdbc3c", "shasum": "" }, "require": { @@ -1966,24 +1922,24 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.3", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.10", + "phpunit/php-code-coverage": "^11.0.11", "phpunit/php-file-iterator": "^5.1.0", "phpunit/php-invoker": "^5.0.1", "phpunit/php-text-template": "^4.0.1", "phpunit/php-timer": "^7.0.1", "sebastian/cli-parser": "^3.0.2", "sebastian/code-unit": "^3.0.3", - "sebastian/comparator": "^6.3.1", + "sebastian/comparator": "^6.3.2", "sebastian/diff": "^6.0.2", "sebastian/environment": "^7.2.1", - "sebastian/exporter": "^6.3.0", + "sebastian/exporter": "^6.3.2", "sebastian/global-state": "^7.0.2", "sebastian/object-enumerator": "^6.0.1", - "sebastian/type": "^5.1.2", + "sebastian/type": "^5.1.3", "sebastian/version": "^5.0.2", "staabm/side-effects-detector": "^1.0.5" }, @@ -2028,7 +1984,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.27" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.42" }, "funding": [ { @@ -2052,7 +2008,7 @@ "type": "tidelift" } ], - "time": "2025-07-11T04:10:06+00:00" + "time": "2025-09-28T12:09:13+00:00" }, { "name": "psr/container", @@ -2279,16 +2235,16 @@ }, { "name": "sebastian/comparator", - "version": "6.3.1", + "version": "6.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "24b8fbc2c8e201bb1308e7b05148d6ab393b6959" + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/24b8fbc2c8e201bb1308e7b05148d6ab393b6959", - "reference": "24b8fbc2c8e201bb1308e7b05148d6ab393b6959", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8", "shasum": "" }, "require": { @@ -2347,15 +2303,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.1" + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2025-03-07T06:57:01+00:00" + "time": "2025-08-10T08:07:46+00:00" }, { "name": "sebastian/complexity", @@ -2560,16 +2528,16 @@ }, { "name": "sebastian/exporter", - "version": "6.3.0", + "version": "6.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3" + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/3473f61172093b2da7de1fb5782e1f24cc036dc3", - "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", "shasum": "" }, "require": { @@ -2583,7 +2551,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.1-dev" + "dev-main": "6.3-dev" } }, "autoload": { @@ -2626,15 +2594,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.0" + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-12-05T09:17:50+00:00" + "time": "2025-09-24T06:12:51+00:00" }, { "name": "sebastian/global-state", @@ -2872,23 +2852,23 @@ }, { "name": "sebastian/recursion-context", - "version": "6.0.2", + "version": "6.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "694d156164372abbd149a4b85ccda2e4670c0e16" + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/694d156164372abbd149a4b85ccda2e4670c0e16", - "reference": "694d156164372abbd149a4b85ccda2e4670c0e16", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { @@ -2924,28 +2904,40 @@ "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.2" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2024-07-03T05:10:34+00:00" + "time": "2025-08-13T04:42:22+00:00" }, { "name": "sebastian/type", - "version": "5.1.2", + "version": "5.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "a8a7e30534b0eb0c77cd9d07e82de1a114389f5e" + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/a8a7e30534b0eb0c77cd9d07e82de1a114389f5e", - "reference": "a8a7e30534b0eb0c77cd9d07e82de1a114389f5e", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", "shasum": "" }, "require": { @@ -2981,15 +2973,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/type/issues", "security": "https://github.com/sebastianbergmann/type/security/policy", - "source": "https://github.com/sebastianbergmann/type/tree/5.1.2" + "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" } ], - "time": "2025-03-18T13:35:50+00:00" + "time": "2025-08-09T06:55:48+00:00" }, { "name": "sebastian/version", @@ -3099,24 +3103,23 @@ }, { "name": "symfony/console", - "version": "v7.3.1", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101" + "reference": "93518c2ff7ce9c1818224c6effed3cf2429c63d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/9e27aecde8f506ba0fd1d9989620c04a87697101", - "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101", + "url": "https://api.github.com/repos/symfony/console/zipball/93518c2ff7ce9c1818224c6effed3cf2429c63d7", + "reference": "93518c2ff7ce9c1818224c6effed3cf2429c63d7", "shasum": "" }, "require": { "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2" + "symfony/string": "^6.4|^7.0" }, "conflict": { "symfony/dependency-injection": "<6.4", @@ -3173,7 +3176,77 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.1" + "source": "https://github.com/symfony/console/tree/v7.2.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-30T17:03:27+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v6.4.24", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "75ae2edb7cdcc0c53766c30b0a2512b8df574bd8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/75ae2edb7cdcc0c53766c30b0a2512b8df574bd8", + "reference": "75ae2edb7cdcc0c53766c30b0a2512b8df574bd8", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^5.4|^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.4.24" }, "funding": [ { @@ -3184,25 +3257,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-27T19:55:54+00:00" + "time": "2025-07-10T08:14:14+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { @@ -3251,7 +3328,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -3262,16 +3339,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -3332,7 +3413,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -3343,6 +3424,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -3435,20 +3520,20 @@ }, { "name": "symfony/string", - "version": "v7.3.0", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125" + "reference": "5621f039a71a11c87c106c1c598bdcd04a19aeea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125", + "url": "https://api.github.com/repos/symfony/string/zipball/5621f039a71a11c87c106c1c598bdcd04a19aeea", + "reference": "5621f039a71a11c87c106c1c598bdcd04a19aeea", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", @@ -3458,12 +3543,10 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/var-exporter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -3502,7 +3585,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.0" + "source": "https://github.com/symfony/string/tree/v6.4.26" }, "funding": [ { @@ -3513,37 +3596,41 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-20T20:19:01+00:00" + "time": "2025-09-11T14:32:46+00:00" }, { "name": "symfony/yaml", - "version": "v7.3.1", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "0c3555045a46ab3cd4cc5a69d161225195230edb" + "reference": "0fc8b966fd0dcaab544ae59bfc3a433f048c17b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/0c3555045a46ab3cd4cc5a69d161225195230edb", - "reference": "0c3555045a46ab3cd4cc5a69d161225195230edb", + "url": "https://api.github.com/repos/symfony/yaml/zipball/0fc8b966fd0dcaab544ae59bfc3a433f048c17b0", + "reference": "0fc8b966fd0dcaab544ae59bfc3a433f048c17b0", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<6.4" + "symfony/console": "<5.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0" + "symfony/console": "^5.4|^6.0|^7.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -3574,7 +3661,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.3.1" + "source": "https://github.com/symfony/yaml/tree/v6.4.26" }, "funding": [ { @@ -3585,12 +3672,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-03T06:57:57+00:00" + "time": "2025-09-26T15:07:38+00:00" }, { "name": "theseer/autoload", diff --git a/phpdraft b/phpdraft index a146899..6c9afd0 100755 --- a/phpdraft +++ b/phpdraft @@ -10,109 +10,16 @@ set_include_path(get_include_path() . ":" . __DIR__ . '/src/'); */ require_once 'vendor/autoload.php'; -use Garden\Cli\Cli; -use PHPDraft\In\ApibFileParser; -use PHPDraft\Out\Sorting; -use PHPDraft\Out\Version; -use PHPDraft\Parse\ExecutionException; -use PHPDraft\Parse\ParserFactory; -use PHPDraft\Parse\ResourceException; +use PHPDraft\In\CLI; define('VERSION', '0'); //define('ID_STATIC', 'SOME_ID'); try { - // Define the cli options. $cli = new Cli(); - $cli->description('Parse API Blueprint files.') - ->opt('help:h', 'This help text', false) - ->opt('version:v', 'Print the version for PHPDraft.', false) - ->opt('file:f', 'Specifies the file to parse.', false) - ->opt('openapi:a', 'Output location for an OpenAPI file.', false) - ->opt('yes:y', 'Always accept using the online mode.', false, 'bool') - ->opt('online:o', 'Always use the online mode.', false, 'bool') - ->opt('template:t', 'Specifies the template to use. (defaults to \'default\').', false) - ->opt('sort:s', 'Sort displayed values [All|None|Structures|Webservices] (defaults to the way the objects are in the file).', false) - ->opt('header_image:i', 'Specifies an image to display in the header.', false) - ->opt('css:c', 'Specifies a CSS file to include (value is put in a link element without checking).', false) - ->opt('javascript:j', 'Specifies a JS file to include (value is put in a script element without checking).', false) - ->opt('debug-json-file', 'Input a rendered JSON file for debugging.', false) - ->opt('debug-json', 'Input a rendered JSON text for debugging.', false); - - // Parse and return cli args. - $args = $cli->parse($argv, FALSE); - if (isset($args['help']) || empty($args->getOpts())) { - $cli->writeHelp(); - throw new ExecutionException('', 0); - } - if (isset($args['version'])) { - Version::version(); - throw new ExecutionException('', 0); - } - - stream_set_blocking(STDIN, false); - $stdin = stream_get_contents(STDIN); - $file = $args->getOpt('file'); - if (!empty($stdin) && $file !== NULL) { - throw new ExecutionException('ERROR: Passed data in both file and stdin', 2); - } elseif (!empty($stdin) && $file === NULL) { - $file = tempnam(sys_get_temp_dir(), 'phpdraft'); - file_put_contents($file, $stdin); - } - if ($file === NULL || $file === '') - { - throw new ExecutionException('ERROR: File does not exist', 200); - } - - if (!($file !== NULL || isset($args['debug-json-file']) || isset($args['debug-json']))) { - throw new ExecutionException('Missing required option: file', 1); - } - - define('THIRD_PARTY_ALLOWED', getenv('PHPDRAFT_THIRD_PARTY') !== '0'); - if ((isset($args['y']) || isset($args['o'])) && THIRD_PARTY_ALLOWED) { - define('DRAFTER_ONLINE_MODE', 1); - } - - if (!isset($args['debug-json-file']) && !isset($args['debug-json'])) { - $apib_parser = new ApibFileParser($file); - $apib = $apib_parser->parse(); - $offline = FALSE; - $online = FALSE; - - try { - $parser = ParserFactory::getDrafter(); - $parser = $parser->init($apib); - $data = $parser->parseToJson(); - } catch (ResourceException $exception) { - throw new ExecutionException('No drafter available', 255); - } - } else { - $json_string = $args['debug-json'] ?? file_get_contents($args['debug-json-file']); - $data = json_decode($json_string); - } - - if (isset($args['openapi'])) { - $openapi = ParserFactory::getOpenAPI()->init($data); - $openapi->write($args['openapi']); - } - - $html = ParserFactory::getJson()->init($data); - $name = 'PHPD_SORT_' . strtoupper($args->getOpt('sort', '')); - $html->sorting = Sorting::${$name} ?? Sorting::PHPD_SORT_NONE->value; - - $color1 = getenv('COLOR_PRIMARY') === FALSE ? NULL : getenv('COLOR_PRIMARY'); - $color2 = getenv('COLOR_SECONDARY') === FALSE ? NULL : getenv('COLOR_SECONDARY'); - $colors = (is_null($color1) || is_null($color2)) ? '' : '__' . $color1 . '__' . $color2; - $html->build_html( - $args->getOpt('template', 'default') . $colors, - $args['header_image'], - $args['css'], - $args['javascript'] - ); - - echo $html; + $cli->run(); } -catch (ExecutionException|Exception $exception) +catch (Exception $exception) { file_put_contents('php://stderr', $exception->getMessage() . PHP_EOL); exit($exception->getCode()); diff --git a/src/PHPDraft/In/CLI.php b/src/PHPDraft/In/CLI.php new file mode 100644 index 0000000..a672ee7 --- /dev/null +++ b/src/PHPDraft/In/CLI.php @@ -0,0 +1,110 @@ +setHelp('Usage: phpdraft [options]'); + $options->registerOption('version', 'Print the version for PHPDraft.', 'v', false); + $options->registerOption('file', 'Specifies the file to parse.', 'f', true); + $options->registerOption('openapi', 'Output location for an OpenAPI file.', 'a', true); + $options->registerOption('html', 'Output location for the rendered HTML.', 'r', true); + $options->registerOption('yes', 'Always accept using the online mode.', 'y', true); + $options->registerOption('online', 'Always use the online mode.', 'o', false); + $options->registerOption('template', 'Specifies the template to use. (defaults to \'default\').', 't', true); + $options->registerOption('sort', 'Sort displayed values [All|None|Structures|Webservices] (defaults to the way the objects are in the file).', 's', true); + $options->registerOption('header-image', 'Specifies an image to display in the header.', 'i', true); + $options->registerOption('css', 'Specifies a CSS file to include (value is put in a link element without checking).', 'c', true); + $options->registerOption('javascript', 'Specifies a JS file to include (value is put in a script element without checking).', 'j', true); + $options->registerOption('debug-json-file', 'Input a rendered JSON file for debugging.', '', true); + $options->registerOption('debug-json', 'Input a rendered JSON text for debugging.', '', true); + } + + /** + * @throws ExecutionException + */ + protected function main(Options $options): void + { + $args = $options->getOpt(); + if ($options->getOpt('version', NULL) !== NULL) { + Version::version(); + throw new ExecutionException('', 0); + } + + stream_set_blocking(STDIN, false); + $stdin = stream_get_contents(STDIN); + $file = $options->getOpt('file', NULL); + if (!empty($stdin) && $file !== NULL) { + throw new ExecutionException('ERROR: Passed data in both file and stdin', 2); + } elseif (!empty($stdin) && $file === NULL) { + $file = tempnam(sys_get_temp_dir(), 'phpdraft'); + file_put_contents($file, $stdin); + } + if ($file === NULL || $file === '') + { + throw new ExecutionException('ERROR: File does not exist', 200); + } + + if (!($file !== NULL || $options->getOpt('debug-json-file') === FALSE || $options->getOpt('debug-json') === FALSE)) { + throw new ExecutionException('Missing required option: file', 1); + } + + define('THIRD_PARTY_ALLOWED', getenv('PHPDRAFT_THIRD_PARTY') !== '0'); + if ((isset($args['yes']) || isset($args['online'])) && THIRD_PARTY_ALLOWED) { + define('DRAFTER_ONLINE_MODE', 1); + } + + if (!isset($args['debug-json-file']) && !isset($args['debug-json'])) { + $apib_parser = new ApibFileParser($file); + $apib = $apib_parser->parse(); + + try { + $parser = ParserFactory::getDrafter(); + $parser = $parser->init($apib); + $data = $parser->parseToJson(); + } catch (ResourceException $exception) { + throw new ExecutionException('No drafter available', 255, $exception); + } + } else { + $json_string = $args['debug-json'] ?? file_get_contents($args['debug-json-file']); + $data = json_decode($json_string); + } + + if (isset($args['openapi'])) { + $openapi = ParserFactory::getOpenAPI()->init($data); + $openapi->write($args['openapi']); + } + + $html = ParserFactory::getJson()->init($data); + $name = 'PHPD_SORT_' . strtoupper($options->getOpt('sort', '')); + $html->sorting = Sorting::${$name} ?? Sorting::PHPD_SORT_NONE->value; + + $color1 = getenv('COLOR_PRIMARY') === FALSE ? NULL : getenv('COLOR_PRIMARY'); + $color2 = getenv('COLOR_SECONDARY') === FALSE ? NULL : getenv('COLOR_SECONDARY'); + $colors = (is_null($color1) || is_null($color2)) ? '' : '__' . $color1 . '__' . $color2; + $html->build_html( + $options->getOpt('template', 'default') . $colors, + $args['header-image'] ?? NULL, + $args['css'] ?? NULL, + $args['javascript'] ?? NULL, + ); + + if (isset($args['html'])) { + file_put_contents($args['html'], $html); + return; + } + + echo $html; + } +} \ No newline at end of file From b6f3e63bf8aaf64be5d9dd23ac9d14333ff01b63 Mon Sep 17 00:00:00 2001 From: Sean Molenaar Date: Tue, 3 Mar 2026 17:08:20 +0100 Subject: [PATCH 2/3] fix: updated for vacuum --- .github/workflows/test.yml | 4 +- .vacuum/ruleset.yaml | 991 +++++ build.xml | 1 - composer.json | 2 +- composer.lock | 361 +- phpdraft | 3 +- report.html | 3457 +++++++++++++++++ src/PHPDraft/In/ApibFileParser.php | 3 + src/PHPDraft/Out/OpenAPI/OpenApiRenderer.php | 162 +- .../Out/OpenAPI/Tests/OpenApiRendererTest.php | 9 +- tests/statics/openapi/empty.json | 5 +- vacuum.conf.yaml | 1 + 12 files changed, 4769 insertions(+), 230 deletions(-) create mode 100644 .vacuum/ruleset.yaml create mode 100644 report.html create mode 100644 vacuum.conf.yaml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f0be4f1..752ada7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -65,7 +65,7 @@ jobs: run: composer validate - name: Install dependencies - run: composer install --prefer-dist --no-progress --no-suggest --ignore-platform-reqs + run: composer install --prefer-dist --no-progress --ignore-platform-reqs - name: Setup Problem Matchers for PHPUnit run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" @@ -223,7 +223,7 @@ jobs: run: composer validate - name: Install dependencies - run: composer install --prefer-dist --no-progress --no-suggest --ignore-platform-reqs + run: composer install --prefer-dist --no-progress --ignore-platform-reqs - name: Run test suite run: ./vendor/phpunit/phpunit/phpunit --configuration tests/phpunit.xml --exclude-group twig --coverage-clover=./var/coverage/clover.xml diff --git a/.vacuum/ruleset.yaml b/.vacuum/ruleset.yaml new file mode 100644 index 0000000..0321a8e --- /dev/null +++ b/.vacuum/ruleset.yaml @@ -0,0 +1,991 @@ +description: Recommended rules for a high quality specification. +documentationUrl: https://quobix.com/vacuum/rulesets/recommended +rules: + camel-case-properties: + category: + description: Schemas are how request bodies and response payloads are defined. They define the data going in and the data flowing out of an operation. These rules check for structural validity, checking types, checking required fields and validating correct use of structures. + id: schemas + name: Schemas + description: Schema property names should use camelCase instead of snake_case, PascalCase, kebab-case, or other formats. + formats: + - oas3 + - oas3_1 + - oas3_2 + given: $ + howToFix: Schema property names should use camelCase for consistency and better compatibility with code generation tools. Property names should start with a lowercase letter and use uppercase letters for word boundaries. + id: camel-case-properties + recommended: true + resolved: true + severity: warn + then: + function: oasCamelCaseProperties + type: style + component-description: + category: + description: Documentation is really important, in OpenAPI, just about everything can and should have a description. This set of rules checks for absent descriptions, poor quality descriptions (copy/paste), or short descriptions. + id: descriptions + name: Descriptions + description: Component description check + formats: + - oas3 + - oas3_1 + - oas3_2 + given: $ + howToFix: Components are the inputs and outputs of a specification. A user needs to be able to understand each component and what it does. Descriptions are critical to understanding components. Add a description! + id: component-description + recommended: true + resolved: true + severity: warn + then: + function: oasComponentDescriptions + type: validation + duplicate-paths: + category: + description: Operations are the core of the contract, they define paths and HTTP methods. These rules check operations have been well constructed, looks for operationId, parameter, schema and return types in depth. + id: operations + name: Operations + description: Paths cannot be duplicated; only the last definition will be kept. + formats: + - oas3 + - oas3_1 + - oas3_2 + given: $ + howToFix: Duplicate path definitions found in your OpenAPI specification. In YAML, duplicate keys are allowed, but only the last occurrence is used. This means earlier path definitions are silently ignored, which can lead to missing API endpoints in your specification. + id: duplicate-paths + recommended: true + severity: error + then: + function: duplicatePaths + type: validation + duplicated-entry-in-enum: + category: + description: Schemas are how request bodies and response payloads are defined. They define the data going in and the data flowing out of an operation. These rules check for structural validity, checking types, checking required fields and validating correct use of structures. + id: schemas + name: Schemas + description: Enum values must not have duplicate entry + formats: + - oas3 + - oas3_1 + - oas3_2 + - oas2 + given: $ + howToFix: Enums need to be unique, you can't duplicate them in the same definition. Please remove the duplicate value. + id: duplicated-entry-in-enum + recommended: true + severity: error + then: + function: duplicatedEnum + type: validation + info-description: + category: + description: The info object contains licencing, contact, authorship details and more. Checks to confirm required details have been completed. + id: information + name: Contract Information + description: Info section is missing a description + formats: + - oas3 + - oas3_1 + - oas3_2 + - oas2 + given: $ + howToFix: The 'info' section is missing a description, surely you want people to know what this spec is all about, right? + id: info-description + recommended: true + resolved: true + severity: error + then: + function: infoDescription + type: validation + info-license-spdx: + category: + description: The info object contains licencing, contact, authorship details and more. Checks to confirm required details have been completed. + id: information + name: Contract Information + description: License section cannot contain both an identifier and a URL, they are mutually exclusive. + formats: + - oas3 + - oas3_1 + - oas3_2 + - oas2 + given: $ + howToFix: A license can contain either a URL or an SPDX identifier, but not both, They are mutually exclusive and cannot both be present. Choose one or the other + id: info-license-spdx + recommended: true + resolved: true + severity: error + then: + function: infoLicenseURLSPDX + type: validation + migrate-zally-ignore: + category: + description: Validation rules make sure that certain characters or patterns have not been used that may cause issues when rendering in different types of applications. + id: validation + name: Validation + description: x-zally-ignore keys should be migrated to x-lint-ignore for compatibility with vacuum + formats: + - oas3 + - oas3_1 + - oas3_2 + - oas2 + given: $ + howToFix: Migrate x-zally-ignore directives to vacuum's x-lint-ignore. Rename the key to x-lint-ignore and update the ignored rule id to the vacuum equivalent rule. + id: migrate-zally-ignore + recommended: true + resolved: true + severity: warn + then: + function: migrateZallyIgnore + type: validation + no-$ref-siblings: + category: + description: Schemas are how request bodies and response payloads are defined. They define the data going in and the data flowing out of an operation. These rules check for structural validity, checking types, checking required fields and validating correct use of structures. + id: schemas + name: Schemas + description: $ref values cannot be placed next to other properties (like a description) + formats: + - oas2 + - oas3_0 + given: $ + howToFix: $ref values must not be placed next to sibling nodes, There should only be a single node when using $ref. A common mistake is adding 'description' next to a $ref. This is wrong. remove all siblings! + id: no-$ref-siblings + recommended: true + severity: error + then: + function: refSiblings + type: validation + no-ambiguous-paths: + category: + description: Operations are the core of the contract, they define paths and HTTP methods. These rules check operations have been well constructed, looks for operationId, parameter, schema and return types in depth. + id: operations + name: Operations + description: Paths need to resolve unambiguously from one another + formats: + - oas3 + - oas3_1 + - oas3_2 + - oas2 + given: $ + howToFix: Paths must all resolve unambiguously, they can't be confused with one another (/{id}/ambiguous and /ambiguous/{id} are the same thing. Make sure every path and the variables used are unique and do conflict with one another. Check the ordering of variables and the naming of path segments. + id: no-ambiguous-paths + recommended: true + resolved: true + severity: error + then: + function: noAmbiguousPaths + type: validation + no-eval-in-markdown: + category: + description: Validation rules make sure that certain characters or patterns have not been used that may cause issues when rendering in different types of applications. + id: validation + name: Validation + description: Markdown descriptions must not have `eval()` statements' + formats: + - oas3 + - oas3_1 + - oas3_2 + - oas2 + given: $ + howToFix: Remove all references to 'eval()' in the description. These can be used by malicious actors to embed code in contracts that is then executed when read by a browser. + id: no-eval-in-markdown + recommended: true + resolved: true + severity: error + then: + function: noEvalDescription + functionOptions: + pattern: eval\( + type: validation + no-http-verbs-in-path: + category: + description: Operations are the core of the contract, they define paths and HTTP methods. These rules check operations have been well constructed, looks for operationId, parameter, schema and return types in depth. + id: operations + name: Operations + description: Path segments must not contain an HTTP verb + formats: + - oas3 + - oas3_1 + - oas3_2 + - oas2 + given: $ + howToFix: When HTTP verbs (get/post/put etc) are used in path segments, it muddies the semantics of REST and creates a confusing and inconsistent experience. It's highly recommended that verbs are not used in path segments. Replace those HTTP verbs with more meaningful nouns. + id: no-http-verbs-in-path + recommended: true + severity: warn + then: + function: noVerbsInPath + type: style + no-request-body: + category: + description: Operations are the core of the contract, they define paths and HTTP methods. These rules check operations have been well constructed, looks for operationId, parameter, schema and return types in depth. + id: operations + name: Operations + description: HTTP GET and DELETE should not accept request bodies + formats: + - oas3 + - oas3_1 + - oas3_2 + given: $ + howToFix: Remove 'requestBody' from HTTP GET and DELETE methods + id: no-request-body + recommended: true + severity: warn + then: + function: noRequestBody + type: style + no-script-tags-in-markdown: + category: + description: Validation rules make sure that certain characters or patterns have not been used that may cause issues when rendering in different types of applications. + id: validation + name: Validation + description: Markdown descriptions must not have ` + + +
+
+ +
[unknown] Generated: 28 Oct 2025 17:16:22 CET
+
+ The mobile experience is still being developed. This may look/feel a bit odd for now. +
+
+
+
+
+ +
+ + + +
76                "responses": {
+77                    "200": {
+78                        "description": "",
+79                        "headers": {},
+80                        "content": {
+81                            "application/json": {
+
+
124                "responses": {
+125                    "200": {
+126                        "description": "",
+127                        "headers": {},
+128                        "content": {
+129                            "application/json": {
+
+
151                        "type": "string"
+152                    },
+153                    "description": "The id of the Vehicle."
+154                }
+155            ],
+156            "get": {
+
+
172                "responses": {
+173                    "200": {
+174                        "description": "",
+175                        "headers": {},
+176                        "content": {
+177                            "application/json": {
+
+
199                        "type": "string"
+200                    },
+201                    "description": "The id of the Vehicle."
+202                }
+203            ],
+204            "get": {
+
+
220                "responses": {
+221                    "200": {
+222                        "description": "",
+223                        "headers": {},
+224                        "content": {
+225                            "application/json": {
+
+
247                        "type": "string"
+248                    },
+249                    "description": "The id of the Vehicle."
+250                }
+251            ],
+252            "get": {
+
+
268                "responses": {
+269                    "200": {
+270                        "description": "",
+271                        "headers": {},
+272                        "content": {
+273                            "application/json": {
+
+
295                        "type": "string"
+296                    },
+297                    "description": "The id of the Vehicle."
+298                }
+299            ],
+300            "get": {
+
+
316                "responses": {
+317                    "200": {
+318                        "description": "",
+319                        "headers": {},
+320                        "content": {
+321                            "application/json": {
+
+
343                        "type": "string"
+344                    },
+345                    "description": "The id of the Vehicle."
+346                }
+347            ],
+348            "get": {
+
+
364                "responses": {
+365                    "200": {
+366                        "description": "",
+367                        "headers": {},
+368                        "content": {
+369                            "application/json": {
+
+
391                        "type": "string"
+392                    },
+393                    "description": "The id of the Vehicle."
+394                }
+395            ],
+396            "post": {
+
+
412                "responses": {
+413                    "200": {
+414                        "description": "",
+415                        "headers": {},
+416                        "content": {
+417                            "application/json": {
+
+
439                        "type": "string"
+440                    },
+441                    "description": "The id of the Vehicle."
+442                }
+443            ],
+444            "post": {
+
+
460                "responses": {
+461                    "200": {
+462                        "description": "",
+463                        "headers": {},
+464                        "content": {
+465                            "application/json": {
+
+
487                        "type": "string"
+488                    },
+489                    "description": "The id of the Vehicle."
+490                }
+491            ],
+492            "post": {
+
+
508                "responses": {
+509                    "200": {
+510                        "description": "",
+511                        "headers": {},
+512                        "content": {
+513                            "application/json": {
+
+
535                        "type": "string"
+536                    },
+537                    "description": "The id of the Vehicle."
+538                }
+539            ],
+540            "post": {
+
+
556                "responses": {
+557                    "200": {
+558                        "description": "",
+559                        "headers": {},
+560                        "content": {
+561                            "application/json": {
+
+
583                        "type": "string"
+584                    },
+585                    "description": "The id of the Vehicle."
+586                }
+587            ],
+588            "post": {
+
+
604                "responses": {
+605                    "200": {
+606                        "description": "",
+607                        "headers": {},
+608                        "content": {
+609                            "application/json": {
+
+
631                        "type": "string"
+632                    },
+633                    "description": "The id of the Vehicle."
+634                }
+635            ],
+636            "post": {
+
+
652                "responses": {
+653                    "200": {
+654                        "description": "",
+655                        "headers": {},
+656                        "content": {
+657                            "application/json": {
+
+
679                        "type": "string"
+680                    },
+681                    "description": "The id of the Vehicle."
+682                },
+683                {
+684                    "name": "limit_value",
+
+
709                "responses": {
+710                    "200": {
+711                        "description": "",
+712                        "headers": {},
+713                        "content": {
+714                            "application/json": {
+
+
736                        "type": "string"
+737                    },
+738                    "description": "The id of the Vehicle."
+739                }
+740            ],
+741            "post": {
+
+
757                "responses": {
+758                    "200": {
+759                        "description": "",
+760                        "headers": {},
+761                        "content": {
+762                            "application/json": {
+
+
784                        "type": "string"
+785                    },
+786                    "description": "The id of the Vehicle."
+787                }
+788            ],
+789            "post": {
+
+
805                "responses": {
+806                    "200": {
+807                        "description": "",
+808                        "headers": {},
+809                        "content": {
+810                            "application/json": {
+
+
832                        "type": "string"
+833                    },
+834                    "description": "The id of the Vehicle."
+835                }
+836            ],
+837            "post": {
+
+
853                "responses": {
+854                    "200": {
+855                        "description": "",
+856                        "headers": {},
+857                        "content": {
+858                            "application/json": {
+
+
880                        "type": "string"
+881                    },
+882                    "description": "The id of the Vehicle."
+883                }
+884            ],
+885            "post": {
+
+
901                "responses": {
+902                    "200": {
+903                        "description": "",
+904                        "headers": {},
+905                        "content": {
+906                            "application/json": {
+
+
928                        "type": "string"
+929                    },
+930                    "description": "The id of the Vehicle."
+931                }
+932            ],
+933            "post": {
+
+
949                "responses": {
+950                    "200": {
+951                        "description": "",
+952                        "headers": {},
+953                        "content": {
+954                            "application/json": {
+
+
976                        "type": "string"
+977                    },
+978                    "description": "The id of the Vehicle."
+979                }
+980            ],
+981            "post": {
+
+
 997                "responses": {
+ 998                    "200": {
+ 999                        "description": "",
+1000                        "headers": {},
+1001                        "content": {
+1002                            "application/json": {
+
+
1024                        "type": "string"
+1025                    },
+1026                    "description": "The id of the Vehicle."
+1027                },
+1028                {
+1029                    "name": "driver_degC",
+
+
1063                "responses": {
+1064                    "200": {
+1065                        "description": "",
+1066                        "headers": {},
+1067                        "content": {
+1068                            "application/json": {
+
+
1090                        "type": "string"
+1091                    },
+1092                    "description": "The id of the Vehicle."
+1093                }
+1094            ],
+1095            "post": {
+
+
1111                "responses": {
+1112                    "200": {
+1113                        "description": "",
+1114                        "headers": {},
+1115                        "content": {
+1116                            "application/json": {
+
+
1138                        "type": "string"
+1139                    },
+1140                    "description": "The id of the Vehicle."
+1141                }
+1142            ],
+1143            "post": {
+
+
1159                "responses": {
+1160                    "200": {
+1161                        "description": "",
+1162                        "headers": {},
+1163                        "content": {
+1164                            "application/json": {
+
+
1186                        "type": "string"
+1187                    },
+1188                    "description": "The id of the Vehicle."
+1189                },
+1190                {
+1191                    "name": "state",
+
+
1232                "responses": {
+1233                    "200": {
+1234                        "description": "",
+1235                        "headers": {},
+1236                        "content": {
+1237                            "application/json": {
+
+
1259                        "type": "string"
+1260                    },
+1261                    "description": "The id of the Vehicle."
+1262                },
+1263                {
+1264                    "name": "password",
+
+
1289                "responses": {
+1290                    "200": {
+1291                        "description": "",
+1292                        "headers": {},
+1293                        "content": {
+1294                            "application/json": {
+
+
1316                        "type": "string"
+1317                    },
+1318                    "description": "The id of the Vehicle."
+1319                }
+1320            ],
+1321            "post": {
+
+
1337                "responses": {
+1338                    "200": {
+1339                        "description": "",
+1340                        "headers": {},
+1341                        "content": {
+1342                            "application/json": {
+
+
66                "description": "Retrieve a list of your owned vehicles (includes vehicles not yet shipped!)",
+67                "parameters": [
+68                    {
+69                        "name": "Authorization",
+70                        "in": "header",
+71                        "schema": {
+
+
114                "description": "Determines if mobile access to the vehicle is enabled.",
+115                "parameters": [
+116                    {
+117                        "name": "Authorization",
+118                        "in": "header",
+119                        "schema": {
+
+
162                "description": "Returns the state of charge in the battery.",
+163                "parameters": [
+164                    {
+165                        "name": "Authorization",
+166                        "in": "header",
+167                        "schema": {
+
+
210                "description": "Returns the current temperature and climate control state.",
+211                "parameters": [
+212                    {
+213                        "name": "Authorization",
+214                        "in": "header",
+215                        "schema": {
+
+
258                "description": "Returns the driving and position state of the vehicle.",
+259                "parameters": [
+260                    {
+261                        "name": "Authorization",
+262                        "in": "header",
+263                        "schema": {
+
+
306                "description": "Returns various information about the GUI settings of the car, such as unit format and range display.",
+307                "parameters": [
+308                    {
+309                        "name": "Authorization",
+310                        "in": "header",
+311                        "schema": {
+
+
354                "description": "Returns the vehicle's physical state, such as which doors are open.",
+355                "parameters": [
+356                    {
+357                        "name": "Authorization",
+358                        "in": "header",
+359                        "schema": {
+
+
402                "description": "Wakes up the car from the sleep state. Necessary to get some data from the car.",
+403                "parameters": [
+404                    {
+405                        "name": "Authorization",
+406                        "in": "header",
+407                        "schema": {
+
+
450                "description": "Sets valet mode on or off with a PIN to disable it from within the car. Reuses last PIN from previous valet session.\nValet Mode limits the car's top speed to 70MPH and 80kW of acceleration power. It also disables Homelink, Bluetooth and\nWifi settings, and the ability to disable mobile access to the car. It also hides your favorites, home, and work\nlocations in navigation.",
+451                "parameters": [
+452                    {
+453                        "name": "Authorization",
+454                        "in": "header",
+455                        "schema": {
+
+
498                "description": "Resets the PIN set for valet mode, if set.",
+499                "parameters": [
+500                    {
+501                        "name": "Authorization",
+502                        "in": "header",
+503                        "schema": {
+
+
546                "description": "Opens the charge port. Does not close the charge port (for now...)",
+547                "parameters": [
+548                    {
+549                        "name": "Authorization",
+550                        "in": "header",
+551                        "schema": {
+
+
594                "description": "Set the charge mode to standard (90% under the new percentage system introduced in 4.5).",
+595                "parameters": [
+596                    {
+597                        "name": "Authorization",
+598                        "in": "header",
+599                        "schema": {
+
+
642                "description": "Set the charge mode to max range (100% under the new percentage system introduced in 4.5). Use sparingly!",
+643                "parameters": [
+644                    {
+645                        "name": "Authorization",
+646                        "in": "header",
+647                        "schema": {
+
+
699                "description": "Set the charge limit to a custom percentage.",
+700                "parameters": [
+701                    {
+702                        "name": "Authorization",
+703                        "in": "header",
+704                        "schema": {
+
+
747                "description": "Start charging. Must be plugged in, have power available, and not have reached your charge limit.",
+748                "parameters": [
+749                    {
+750                        "name": "Authorization",
+751                        "in": "header",
+752                        "schema": {
+
+
795                "description": "Stop charging. Must already be charging.",
+796                "parameters": [
+797                    {
+798                        "name": "Authorization",
+799                        "in": "header",
+800                        "schema": {
+
+
843                "description": "Flash the lights once.",
+844                "parameters": [
+845                    {
+846                        "name": "Authorization",
+847                        "in": "header",
+848                        "schema": {
+
+
891                "description": "Honk the horn once.",
+892                "parameters": [
+893                    {
+894                        "name": "Authorization",
+895                        "in": "header",
+896                        "schema": {
+
+
939                "description": "Unlock the car's doors.",
+940                "parameters": [
+941                    {
+942                        "name": "Authorization",
+943                        "in": "header",
+944                        "schema": {
+
+
987                "description": "Lock the car's doors.",
+988                "parameters": [
+989                    {
+990                        "name": "Authorization",
+991                        "in": "header",
+992                        "schema": {
+
+
1053                "description": "Set the temperature target for the HVAC system.",
+1054                "parameters": [
+1055                    {
+1056                        "name": "Authorization",
+1057                        "in": "header",
+1058                        "schema": {
+
+
1101                "description": "Start the climate control system. Will cool or heat automatically, depending on set temperature.",
+1102                "parameters": [
+1103                    {
+1104                        "name": "Authorization",
+1105                        "in": "header",
+1106                        "schema": {
+
+
1149                "description": "Stop the climate control system.",
+1150                "parameters": [
+1151                    {
+1152                        "name": "Authorization",
+1153                        "in": "header",
+1154                        "schema": {
+
+
1222                "description": "Controls the car's panoramic roof, if installed.",
+1223                "parameters": [
+1224                    {
+1225                        "name": "Authorization",
+1226                        "in": "header",
+1227                        "schema": {
+
+
1279                "description": "Start the car for keyless driving. Must start driving within 2 minutes of issuing this request.",
+1280                "parameters": [
+1281                    {
+1282                        "name": "Authorization",
+1283                        "in": "header",
+1284                        "schema": {
+
+
1327                "description": "Open the trunk or frunk. Currently inoperable.",
+1328                "parameters": [
+1329                    {
+1330                        "name": "Authorization",
+1331                        "in": "header",
+1332                        "schema": {
+
+
1203                    },
+1204                    "description": "\nThe desired state of the panoramic roof. The approximate percent open values for each state are `open` = 100%, `close` = 0%, `comfort` = 80%, and `vent` = ~15%\n"
+1205                },
+1206                {
+1207                    "name": "percent",
+1208                    "in": "path",
+
+
1357    },
+1358    "webhooks": {},
+1359    "components": {
+1360        "schemas": []
+1361    },
+1362    "security": [],
+
+
25                ],
+26                "description": "Performs the login. Takes in an plain text email and password, matching the owner's login information for [https://my.teslamotors.com/user/login](https://my.teslamotors.com/user/login).\n\nReturns a `access_token` which is passed along as a header with all future requests to authenticate the user.\n\nYou must provide the `Authorization: Bearer {access_token}` header in all other requests.\n\nThe current client ID and secret are [available here](http://pastebin.com/fX6ejAHd)",
+27                "requestBody": {
+28                    "content": {
+29                        "application/json": {
+30                            "examples": {
+
+
38                },
+39                "responses": {
+40                    "200": {
+41                        "description": "",
+42                        "headers": {},
+43                        "content": {
+
+
75                ],
+76                "responses": {
+77                    "200": {
+78                        "description": "",
+79                        "headers": {},
+80                        "content": {
+
+
123                ],
+124                "responses": {
+125                    "200": {
+126                        "description": "",
+127                        "headers": {},
+128                        "content": {
+
+
171                ],
+172                "responses": {
+173                    "200": {
+174                        "description": "",
+175                        "headers": {},
+176                        "content": {
+
+
219                ],
+220                "responses": {
+221                    "200": {
+222                        "description": "",
+223                        "headers": {},
+224                        "content": {
+
+
267                ],
+268                "responses": {
+269                    "200": {
+270                        "description": "",
+271                        "headers": {},
+272                        "content": {
+
+
315                ],
+316                "responses": {
+317                    "200": {
+318                        "description": "",
+319                        "headers": {},
+320                        "content": {
+
+
363                ],
+364                "responses": {
+365                    "200": {
+366                        "description": "",
+367                        "headers": {},
+368                        "content": {
+
+
411                ],
+412                "responses": {
+413                    "200": {
+414                        "description": "",
+415                        "headers": {},
+416                        "content": {
+
+
459                ],
+460                "responses": {
+461                    "200": {
+462                        "description": "",
+463                        "headers": {},
+464                        "content": {
+
+
507                ],
+508                "responses": {
+509                    "200": {
+510                        "description": "",
+511                        "headers": {},
+512                        "content": {
+
+
555                ],
+556                "responses": {
+557                    "200": {
+558                        "description": "",
+559                        "headers": {},
+560                        "content": {
+
+
603                ],
+604                "responses": {
+605                    "200": {
+606                        "description": "",
+607                        "headers": {},
+608                        "content": {
+
+
651                ],
+652                "responses": {
+653                    "200": {
+654                        "description": "",
+655                        "headers": {},
+656                        "content": {
+
+
708                ],
+709                "responses": {
+710                    "200": {
+711                        "description": "",
+712                        "headers": {},
+713                        "content": {
+
+
756                ],
+757                "responses": {
+758                    "200": {
+759                        "description": "",
+760                        "headers": {},
+761                        "content": {
+
+
804                ],
+805                "responses": {
+806                    "200": {
+807                        "description": "",
+808                        "headers": {},
+809                        "content": {
+
+
852                ],
+853                "responses": {
+854                    "200": {
+855                        "description": "",
+856                        "headers": {},
+857                        "content": {
+
+
900                ],
+901                "responses": {
+902                    "200": {
+903                        "description": "",
+904                        "headers": {},
+905                        "content": {
+
+
948                ],
+949                "responses": {
+950                    "200": {
+951                        "description": "",
+952                        "headers": {},
+953                        "content": {
+
+
 996                ],
+ 997                "responses": {
+ 998                    "200": {
+ 999                        "description": "",
+1000                        "headers": {},
+1001                        "content": {
+
+
1062                ],
+1063                "responses": {
+1064                    "200": {
+1065                        "description": "",
+1066                        "headers": {},
+1067                        "content": {
+
+
1110                ],
+1111                "responses": {
+1112                    "200": {
+1113                        "description": "",
+1114                        "headers": {},
+1115                        "content": {
+
+
1158                ],
+1159                "responses": {
+1160                    "200": {
+1161                        "description": "",
+1162                        "headers": {},
+1163                        "content": {
+
+
1231                ],
+1232                "responses": {
+1233                    "200": {
+1234                        "description": "",
+1235                        "headers": {},
+1236                        "content": {
+
+
1288                ],
+1289                "responses": {
+1290                    "200": {
+1291                        "description": "",
+1292                        "headers": {},
+1293                        "content": {
+
+
1336                ],
+1337                "responses": {
+1338                    "200": {
+1339                        "description": "",
+1340                        "headers": {},
+1341                        "content": {
+
+
670            }
+671        },
+672        "/api/1/vehicles/{vehicle_id}/command/set_charge_limit?percent={limit_value}": {
+673            "parameters": [
+674                {
+675                    "name": "vehicle_id",
+
+
1015            }
+1016        },
+1017        "/api/1/vehicles/{vehicle_id}/command/set_temps?driver_temp={driver_degC}&passenger_temp={pass_degC}": {
+1018            "parameters": [
+1019                {
+1020                    "name": "vehicle_id",
+
+
1177            }
+1178        },
+1179        "/api/1/vehicles/{vehicle_id}/command/sun_roof_control?state={state}&percent={percent}": {
+1180            "parameters": [
+1181                {
+1182                    "name": "vehicle_id",
+
+
1250            }
+1251        },
+1252        "/api/1/vehicles/{vehicle_id}/command/remote_start_drive?password={password}": {
+1253            "parameters": [
+1254                {
+1255                    "name": "vehicle_id",
+
+
1178        },
+1179        "/api/1/vehicles/{vehicle_id}/command/sun_roof_control?state={state}&percent={percent}": {
+1180            "parameters": [
+1181                {
+1182                    "name": "vehicle_id",
+1183                    "in": "path",
+
+
94            }
+95        },
+96        "/api/1/vehicles/{vehicle_id}/mobile_enabled": {
+97            "parameters": [
+98                {
+99                    "name": "vehicle_id",
+
+
142            }
+143        },
+144        "/api/1/vehicles/{vehicle_id}/data_request/charge_state": {
+145            "parameters": [
+146                {
+147                    "name": "vehicle_id",
+
+
190            }
+191        },
+192        "/api/1/vehicles/{vehicle_id}/data_request/climate_state": {
+193            "parameters": [
+194                {
+195                    "name": "vehicle_id",
+
+
238            }
+239        },
+240        "/api/1/vehicles/{vehicle_id}/data_request/drive_state": {
+241            "parameters": [
+242                {
+243                    "name": "vehicle_id",
+
+
286            }
+287        },
+288        "/api/1/vehicles/{vehicle_id}/data_request/gui_settings": {
+289            "parameters": [
+290                {
+291                    "name": "vehicle_id",
+
+
334            }
+335        },
+336        "/api/1/vehicles/{vehicle_id}/data_request/vehicle_state": {
+337            "parameters": [
+338                {
+339                    "name": "vehicle_id",
+
+
382            }
+383        },
+384        "/api/1/vehicles/{vehicle_id}/wake_up": {
+385            "parameters": [
+386                {
+387                    "name": "vehicle_id",
+
+
430            }
+431        },
+432        "/api/1/vehicles/{vehicle_id}/command/set_valet_mode": {
+433            "parameters": [
+434                {
+435                    "name": "vehicle_id",
+
+
478            }
+479        },
+480        "/api/1/vehicles/{vehicle_id}/command/reset_valet_pin": {
+481            "parameters": [
+482                {
+483                    "name": "vehicle_id",
+
+
526            }
+527        },
+528        "/api/1/vehicles/{vehicle_id}/command/charge_port_door_open": {
+529            "parameters": [
+530                {
+531                    "name": "vehicle_id",
+
+
574            }
+575        },
+576        "/api/1/vehicles/{vehicle_id}/command/charge_standard": {
+577            "parameters": [
+578                {
+579                    "name": "vehicle_id",
+
+
622            }
+623        },
+624        "/api/1/vehicles/{vehicle_id}/command/charge_max_range": {
+625            "parameters": [
+626                {
+627                    "name": "vehicle_id",
+
+
670            }
+671        },
+672        "/api/1/vehicles/{vehicle_id}/command/set_charge_limit?percent={limit_value}": {
+673            "parameters": [
+674                {
+675                    "name": "vehicle_id",
+
+
727            }
+728        },
+729        "/api/1/vehicles/{vehicle_id}/command/charge_start": {
+730            "parameters": [
+731                {
+732                    "name": "vehicle_id",
+
+
775            }
+776        },
+777        "/api/1/vehicles/{vehicle_id}/command/charge_stop": {
+778            "parameters": [
+779                {
+780                    "name": "vehicle_id",
+
+
823            }
+824        },
+825        "/api/1/vehicles/{vehicle_id}/command/flash_lights": {
+826            "parameters": [
+827                {
+828                    "name": "vehicle_id",
+
+
871            }
+872        },
+873        "/api/1/vehicles/{vehicle_id}/command/honk_horn": {
+874            "parameters": [
+875                {
+876                    "name": "vehicle_id",
+
+
919            }
+920        },
+921        "/api/1/vehicles/{vehicle_id}/command/door_unlock": {
+922            "parameters": [
+923                {
+924                    "name": "vehicle_id",
+
+
967            }
+968        },
+969        "/api/1/vehicles/{vehicle_id}/command/door_lock": {
+970            "parameters": [
+971                {
+972                    "name": "vehicle_id",
+
+
1015            }
+1016        },
+1017        "/api/1/vehicles/{vehicle_id}/command/set_temps?driver_temp={driver_degC}&passenger_temp={pass_degC}": {
+1018            "parameters": [
+1019                {
+1020                    "name": "vehicle_id",
+
+
1081            }
+1082        },
+1083        "/api/1/vehicles/{vehicle_id}/command/auto_conditioning_start": {
+1084            "parameters": [
+1085                {
+1086                    "name": "vehicle_id",
+
+
1129            }
+1130        },
+1131        "/api/1/vehicles/{vehicle_id}/command/auto_conditioning_stop": {
+1132            "parameters": [
+1133                {
+1134                    "name": "vehicle_id",
+
+
1177            }
+1178        },
+1179        "/api/1/vehicles/{vehicle_id}/command/sun_roof_control?state={state}&percent={percent}": {
+1180            "parameters": [
+1181                {
+1182                    "name": "vehicle_id",
+
+
1250            }
+1251        },
+1252        "/api/1/vehicles/{vehicle_id}/command/remote_start_drive?password={password}": {
+1253            "parameters": [
+1254                {
+1255                    "name": "vehicle_id",
+
+
1307            }
+1308        },
+1309        "/api/1/vehicles/{vehicle_id}/command/trunk_open": {
+1310            "parameters": [
+1311                {
+1312                    "name": "vehicle_id",
+
+
+ +
670            }
+671        },
+672        "/api/1/vehicles/{vehicle_id}/command/set_charge_limit?percent={limit_value}": {
+673            "parameters": [
+674                {
+675                    "name": "vehicle_id",
+
+
1015            }
+1016        },
+1017        "/api/1/vehicles/{vehicle_id}/command/set_temps?driver_temp={driver_degC}&passenger_temp={pass_degC}": {
+1018            "parameters": [
+1019                {
+1020                    "name": "vehicle_id",
+
+
1177            }
+1178        },
+1179        "/api/1/vehicles/{vehicle_id}/command/sun_roof_control?state={state}&percent={percent}": {
+1180            "parameters": [
+1181                {
+1182                    "name": "vehicle_id",
+
+
1250            }
+1251        },
+1252        "/api/1/vehicles/{vehicle_id}/command/remote_start_drive?password={password}": {
+1253            "parameters": [
+1254                {
+1255                    "name": "vehicle_id",
+
+
1178        },
+1179        "/api/1/vehicles/{vehicle_id}/command/sun_roof_control?state={state}&percent={percent}": {
+1180            "parameters": [
+1181                {
+1182                    "name": "vehicle_id",
+1183                    "in": "path",
+
+
94            }
+95        },
+96        "/api/1/vehicles/{vehicle_id}/mobile_enabled": {
+97            "parameters": [
+98                {
+99                    "name": "vehicle_id",
+
+
142            }
+143        },
+144        "/api/1/vehicles/{vehicle_id}/data_request/charge_state": {
+145            "parameters": [
+146                {
+147                    "name": "vehicle_id",
+
+
190            }
+191        },
+192        "/api/1/vehicles/{vehicle_id}/data_request/climate_state": {
+193            "parameters": [
+194                {
+195                    "name": "vehicle_id",
+
+
238            }
+239        },
+240        "/api/1/vehicles/{vehicle_id}/data_request/drive_state": {
+241            "parameters": [
+242                {
+243                    "name": "vehicle_id",
+
+
286            }
+287        },
+288        "/api/1/vehicles/{vehicle_id}/data_request/gui_settings": {
+289            "parameters": [
+290                {
+291                    "name": "vehicle_id",
+
+
334            }
+335        },
+336        "/api/1/vehicles/{vehicle_id}/data_request/vehicle_state": {
+337            "parameters": [
+338                {
+339                    "name": "vehicle_id",
+
+
382            }
+383        },
+384        "/api/1/vehicles/{vehicle_id}/wake_up": {
+385            "parameters": [
+386                {
+387                    "name": "vehicle_id",
+
+
430            }
+431        },
+432        "/api/1/vehicles/{vehicle_id}/command/set_valet_mode": {
+433            "parameters": [
+434                {
+435                    "name": "vehicle_id",
+
+
478            }
+479        },
+480        "/api/1/vehicles/{vehicle_id}/command/reset_valet_pin": {
+481            "parameters": [
+482                {
+483                    "name": "vehicle_id",
+
+
526            }
+527        },
+528        "/api/1/vehicles/{vehicle_id}/command/charge_port_door_open": {
+529            "parameters": [
+530                {
+531                    "name": "vehicle_id",
+
+
574            }
+575        },
+576        "/api/1/vehicles/{vehicle_id}/command/charge_standard": {
+577            "parameters": [
+578                {
+579                    "name": "vehicle_id",
+
+
622            }
+623        },
+624        "/api/1/vehicles/{vehicle_id}/command/charge_max_range": {
+625            "parameters": [
+626                {
+627                    "name": "vehicle_id",
+
+
670            }
+671        },
+672        "/api/1/vehicles/{vehicle_id}/command/set_charge_limit?percent={limit_value}": {
+673            "parameters": [
+674                {
+675                    "name": "vehicle_id",
+
+
727            }
+728        },
+729        "/api/1/vehicles/{vehicle_id}/command/charge_start": {
+730            "parameters": [
+731                {
+732                    "name": "vehicle_id",
+
+
775            }
+776        },
+777        "/api/1/vehicles/{vehicle_id}/command/charge_stop": {
+778            "parameters": [
+779                {
+780                    "name": "vehicle_id",
+
+
823            }
+824        },
+825        "/api/1/vehicles/{vehicle_id}/command/flash_lights": {
+826            "parameters": [
+827                {
+828                    "name": "vehicle_id",
+
+
871            }
+872        },
+873        "/api/1/vehicles/{vehicle_id}/command/honk_horn": {
+874            "parameters": [
+875                {
+876                    "name": "vehicle_id",
+
+
919            }
+920        },
+921        "/api/1/vehicles/{vehicle_id}/command/door_unlock": {
+922            "parameters": [
+923                {
+924                    "name": "vehicle_id",
+
+
967            }
+968        },
+969        "/api/1/vehicles/{vehicle_id}/command/door_lock": {
+970            "parameters": [
+971                {
+972                    "name": "vehicle_id",
+
+
1015            }
+1016        },
+1017        "/api/1/vehicles/{vehicle_id}/command/set_temps?driver_temp={driver_degC}&passenger_temp={pass_degC}": {
+1018            "parameters": [
+1019                {
+1020                    "name": "vehicle_id",
+
+
1081            }
+1082        },
+1083        "/api/1/vehicles/{vehicle_id}/command/auto_conditioning_start": {
+1084            "parameters": [
+1085                {
+1086                    "name": "vehicle_id",
+
+
1129            }
+1130        },
+1131        "/api/1/vehicles/{vehicle_id}/command/auto_conditioning_stop": {
+1132            "parameters": [
+1133                {
+1134                    "name": "vehicle_id",
+
+
1177            }
+1178        },
+1179        "/api/1/vehicles/{vehicle_id}/command/sun_roof_control?state={state}&percent={percent}": {
+1180            "parameters": [
+1181                {
+1182                    "name": "vehicle_id",
+
+
1250            }
+1251        },
+1252        "/api/1/vehicles/{vehicle_id}/command/remote_start_drive?password={password}": {
+1253            "parameters": [
+1254                {
+1255                    "name": "vehicle_id",
+
+
1307            }
+1308        },
+1309        "/api/1/vehicles/{vehicle_id}/command/trunk_open": {
+1310            "parameters": [
+1311                {
+1312                    "name": "vehicle_id",
+
+
+ +
1203                    },
+1204                    "description": "\nThe desired state of the panoramic roof. The approximate percent open values for each state are `open` = 100%, `close` = 0%, `comfort` = 80%, and `vent` = ~15%\n"
+1205                },
+1206                {
+1207                    "name": "percent",
+1208                    "in": "path",
+
+
1357    },
+1358    "webhooks": {},
+1359    "components": {
+1360        "schemas": []
+1361    },
+1362    "security": [],
+
+
+ +
76                "responses": {
+77                    "200": {
+78                        "description": "",
+79                        "headers": {},
+80                        "content": {
+81                            "application/json": {
+
+
124                "responses": {
+125                    "200": {
+126                        "description": "",
+127                        "headers": {},
+128                        "content": {
+129                            "application/json": {
+
+
151                        "type": "string"
+152                    },
+153                    "description": "The id of the Vehicle."
+154                }
+155            ],
+156            "get": {
+
+
172                "responses": {
+173                    "200": {
+174                        "description": "",
+175                        "headers": {},
+176                        "content": {
+177                            "application/json": {
+
+
199                        "type": "string"
+200                    },
+201                    "description": "The id of the Vehicle."
+202                }
+203            ],
+204            "get": {
+
+
220                "responses": {
+221                    "200": {
+222                        "description": "",
+223                        "headers": {},
+224                        "content": {
+225                            "application/json": {
+
+
247                        "type": "string"
+248                    },
+249                    "description": "The id of the Vehicle."
+250                }
+251            ],
+252            "get": {
+
+
268                "responses": {
+269                    "200": {
+270                        "description": "",
+271                        "headers": {},
+272                        "content": {
+273                            "application/json": {
+
+
295                        "type": "string"
+296                    },
+297                    "description": "The id of the Vehicle."
+298                }
+299            ],
+300            "get": {
+
+
316                "responses": {
+317                    "200": {
+318                        "description": "",
+319                        "headers": {},
+320                        "content": {
+321                            "application/json": {
+
+
343                        "type": "string"
+344                    },
+345                    "description": "The id of the Vehicle."
+346                }
+347            ],
+348            "get": {
+
+
364                "responses": {
+365                    "200": {
+366                        "description": "",
+367                        "headers": {},
+368                        "content": {
+369                            "application/json": {
+
+
391                        "type": "string"
+392                    },
+393                    "description": "The id of the Vehicle."
+394                }
+395            ],
+396            "post": {
+
+
412                "responses": {
+413                    "200": {
+414                        "description": "",
+415                        "headers": {},
+416                        "content": {
+417                            "application/json": {
+
+
439                        "type": "string"
+440                    },
+441                    "description": "The id of the Vehicle."
+442                }
+443            ],
+444            "post": {
+
+
460                "responses": {
+461                    "200": {
+462                        "description": "",
+463                        "headers": {},
+464                        "content": {
+465                            "application/json": {
+
+
487                        "type": "string"
+488                    },
+489                    "description": "The id of the Vehicle."
+490                }
+491            ],
+492            "post": {
+
+
508                "responses": {
+509                    "200": {
+510                        "description": "",
+511                        "headers": {},
+512                        "content": {
+513                            "application/json": {
+
+
535                        "type": "string"
+536                    },
+537                    "description": "The id of the Vehicle."
+538                }
+539            ],
+540            "post": {
+
+
556                "responses": {
+557                    "200": {
+558                        "description": "",
+559                        "headers": {},
+560                        "content": {
+561                            "application/json": {
+
+
583                        "type": "string"
+584                    },
+585                    "description": "The id of the Vehicle."
+586                }
+587            ],
+588            "post": {
+
+
604                "responses": {
+605                    "200": {
+606                        "description": "",
+607                        "headers": {},
+608                        "content": {
+609                            "application/json": {
+
+
631                        "type": "string"
+632                    },
+633                    "description": "The id of the Vehicle."
+634                }
+635            ],
+636            "post": {
+
+
652                "responses": {
+653                    "200": {
+654                        "description": "",
+655                        "headers": {},
+656                        "content": {
+657                            "application/json": {
+
+
679                        "type": "string"
+680                    },
+681                    "description": "The id of the Vehicle."
+682                },
+683                {
+684                    "name": "limit_value",
+
+
709                "responses": {
+710                    "200": {
+711                        "description": "",
+712                        "headers": {},
+713                        "content": {
+714                            "application/json": {
+
+
736                        "type": "string"
+737                    },
+738                    "description": "The id of the Vehicle."
+739                }
+740            ],
+741            "post": {
+
+
757                "responses": {
+758                    "200": {
+759                        "description": "",
+760                        "headers": {},
+761                        "content": {
+762                            "application/json": {
+
+
784                        "type": "string"
+785                    },
+786                    "description": "The id of the Vehicle."
+787                }
+788            ],
+789            "post": {
+
+
805                "responses": {
+806                    "200": {
+807                        "description": "",
+808                        "headers": {},
+809                        "content": {
+810                            "application/json": {
+
+
832                        "type": "string"
+833                    },
+834                    "description": "The id of the Vehicle."
+835                }
+836            ],
+837            "post": {
+
+
853                "responses": {
+854                    "200": {
+855                        "description": "",
+856                        "headers": {},
+857                        "content": {
+858                            "application/json": {
+
+
880                        "type": "string"
+881                    },
+882                    "description": "The id of the Vehicle."
+883                }
+884            ],
+885            "post": {
+
+
901                "responses": {
+902                    "200": {
+903                        "description": "",
+904                        "headers": {},
+905                        "content": {
+906                            "application/json": {
+
+
928                        "type": "string"
+929                    },
+930                    "description": "The id of the Vehicle."
+931                }
+932            ],
+933            "post": {
+
+
949                "responses": {
+950                    "200": {
+951                        "description": "",
+952                        "headers": {},
+953                        "content": {
+954                            "application/json": {
+
+
976                        "type": "string"
+977                    },
+978                    "description": "The id of the Vehicle."
+979                }
+980            ],
+981            "post": {
+
+
 997                "responses": {
+ 998                    "200": {
+ 999                        "description": "",
+1000                        "headers": {},
+1001                        "content": {
+1002                            "application/json": {
+
+
1024                        "type": "string"
+1025                    },
+1026                    "description": "The id of the Vehicle."
+1027                },
+1028                {
+1029                    "name": "driver_degC",
+
+
1063                "responses": {
+1064                    "200": {
+1065                        "description": "",
+1066                        "headers": {},
+1067                        "content": {
+1068                            "application/json": {
+
+
1090                        "type": "string"
+1091                    },
+1092                    "description": "The id of the Vehicle."
+1093                }
+1094            ],
+1095            "post": {
+
+
1111                "responses": {
+1112                    "200": {
+1113                        "description": "",
+1114                        "headers": {},
+1115                        "content": {
+1116                            "application/json": {
+
+
1138                        "type": "string"
+1139                    },
+1140                    "description": "The id of the Vehicle."
+1141                }
+1142            ],
+1143            "post": {
+
+
1159                "responses": {
+1160                    "200": {
+1161                        "description": "",
+1162                        "headers": {},
+1163                        "content": {
+1164                            "application/json": {
+
+
1186                        "type": "string"
+1187                    },
+1188                    "description": "The id of the Vehicle."
+1189                },
+1190                {
+1191                    "name": "state",
+
+
1232                "responses": {
+1233                    "200": {
+1234                        "description": "",
+1235                        "headers": {},
+1236                        "content": {
+1237                            "application/json": {
+
+
1259                        "type": "string"
+1260                    },
+1261                    "description": "The id of the Vehicle."
+1262                },
+1263                {
+1264                    "name": "password",
+
+
1289                "responses": {
+1290                    "200": {
+1291                        "description": "",
+1292                        "headers": {},
+1293                        "content": {
+1294                            "application/json": {
+
+
1316                        "type": "string"
+1317                    },
+1318                    "description": "The id of the Vehicle."
+1319                }
+1320            ],
+1321            "post": {
+
+
1337                "responses": {
+1338                    "200": {
+1339                        "description": "",
+1340                        "headers": {},
+1341                        "content": {
+1342                            "application/json": {
+
+
66                "description": "Retrieve a list of your owned vehicles (includes vehicles not yet shipped!)",
+67                "parameters": [
+68                    {
+69                        "name": "Authorization",
+70                        "in": "header",
+71                        "schema": {
+
+
114                "description": "Determines if mobile access to the vehicle is enabled.",
+115                "parameters": [
+116                    {
+117                        "name": "Authorization",
+118                        "in": "header",
+119                        "schema": {
+
+
162                "description": "Returns the state of charge in the battery.",
+163                "parameters": [
+164                    {
+165                        "name": "Authorization",
+166                        "in": "header",
+167                        "schema": {
+
+
210                "description": "Returns the current temperature and climate control state.",
+211                "parameters": [
+212                    {
+213                        "name": "Authorization",
+214                        "in": "header",
+215                        "schema": {
+
+
258                "description": "Returns the driving and position state of the vehicle.",
+259                "parameters": [
+260                    {
+261                        "name": "Authorization",
+262                        "in": "header",
+263                        "schema": {
+
+
306                "description": "Returns various information about the GUI settings of the car, such as unit format and range display.",
+307                "parameters": [
+308                    {
+309                        "name": "Authorization",
+310                        "in": "header",
+311                        "schema": {
+
+
354                "description": "Returns the vehicle's physical state, such as which doors are open.",
+355                "parameters": [
+356                    {
+357                        "name": "Authorization",
+358                        "in": "header",
+359                        "schema": {
+
+
402                "description": "Wakes up the car from the sleep state. Necessary to get some data from the car.",
+403                "parameters": [
+404                    {
+405                        "name": "Authorization",
+406                        "in": "header",
+407                        "schema": {
+
+
450                "description": "Sets valet mode on or off with a PIN to disable it from within the car. Reuses last PIN from previous valet session.\nValet Mode limits the car's top speed to 70MPH and 80kW of acceleration power. It also disables Homelink, Bluetooth and\nWifi settings, and the ability to disable mobile access to the car. It also hides your favorites, home, and work\nlocations in navigation.",
+451                "parameters": [
+452                    {
+453                        "name": "Authorization",
+454                        "in": "header",
+455                        "schema": {
+
+
498                "description": "Resets the PIN set for valet mode, if set.",
+499                "parameters": [
+500                    {
+501                        "name": "Authorization",
+502                        "in": "header",
+503                        "schema": {
+
+
546                "description": "Opens the charge port. Does not close the charge port (for now...)",
+547                "parameters": [
+548                    {
+549                        "name": "Authorization",
+550                        "in": "header",
+551                        "schema": {
+
+
594                "description": "Set the charge mode to standard (90% under the new percentage system introduced in 4.5).",
+595                "parameters": [
+596                    {
+597                        "name": "Authorization",
+598                        "in": "header",
+599                        "schema": {
+
+
642                "description": "Set the charge mode to max range (100% under the new percentage system introduced in 4.5). Use sparingly!",
+643                "parameters": [
+644                    {
+645                        "name": "Authorization",
+646                        "in": "header",
+647                        "schema": {
+
+
699                "description": "Set the charge limit to a custom percentage.",
+700                "parameters": [
+701                    {
+702                        "name": "Authorization",
+703                        "in": "header",
+704                        "schema": {
+
+
747                "description": "Start charging. Must be plugged in, have power available, and not have reached your charge limit.",
+748                "parameters": [
+749                    {
+750                        "name": "Authorization",
+751                        "in": "header",
+752                        "schema": {
+
+
795                "description": "Stop charging. Must already be charging.",
+796                "parameters": [
+797                    {
+798                        "name": "Authorization",
+799                        "in": "header",
+800                        "schema": {
+
+
843                "description": "Flash the lights once.",
+844                "parameters": [
+845                    {
+846                        "name": "Authorization",
+847                        "in": "header",
+848                        "schema": {
+
+
891                "description": "Honk the horn once.",
+892                "parameters": [
+893                    {
+894                        "name": "Authorization",
+895                        "in": "header",
+896                        "schema": {
+
+
939                "description": "Unlock the car's doors.",
+940                "parameters": [
+941                    {
+942                        "name": "Authorization",
+943                        "in": "header",
+944                        "schema": {
+
+
987                "description": "Lock the car's doors.",
+988                "parameters": [
+989                    {
+990                        "name": "Authorization",
+991                        "in": "header",
+992                        "schema": {
+
+
1053                "description": "Set the temperature target for the HVAC system.",
+1054                "parameters": [
+1055                    {
+1056                        "name": "Authorization",
+1057                        "in": "header",
+1058                        "schema": {
+
+
1101                "description": "Start the climate control system. Will cool or heat automatically, depending on set temperature.",
+1102                "parameters": [
+1103                    {
+1104                        "name": "Authorization",
+1105                        "in": "header",
+1106                        "schema": {
+
+
1149                "description": "Stop the climate control system.",
+1150                "parameters": [
+1151                    {
+1152                        "name": "Authorization",
+1153                        "in": "header",
+1154                        "schema": {
+
+
1222                "description": "Controls the car's panoramic roof, if installed.",
+1223                "parameters": [
+1224                    {
+1225                        "name": "Authorization",
+1226                        "in": "header",
+1227                        "schema": {
+
+
1279                "description": "Start the car for keyless driving. Must start driving within 2 minutes of issuing this request.",
+1280                "parameters": [
+1281                    {
+1282                        "name": "Authorization",
+1283                        "in": "header",
+1284                        "schema": {
+
+
1327                "description": "Open the trunk or frunk. Currently inoperable.",
+1328                "parameters": [
+1329                    {
+1330                        "name": "Authorization",
+1331                        "in": "header",
+1332                        "schema": {
+
+
25                ],
+26                "description": "Performs the login. Takes in an plain text email and password, matching the owner's login information for [https://my.teslamotors.com/user/login](https://my.teslamotors.com/user/login).\n\nReturns a `access_token` which is passed along as a header with all future requests to authenticate the user.\n\nYou must provide the `Authorization: Bearer {access_token}` header in all other requests.\n\nThe current client ID and secret are [available here](http://pastebin.com/fX6ejAHd)",
+27                "requestBody": {
+28                    "content": {
+29                        "application/json": {
+30                            "examples": {
+
+
38                },
+39                "responses": {
+40                    "200": {
+41                        "description": "",
+42                        "headers": {},
+43                        "content": {
+
+
75                ],
+76                "responses": {
+77                    "200": {
+78                        "description": "",
+79                        "headers": {},
+80                        "content": {
+
+
123                ],
+124                "responses": {
+125                    "200": {
+126                        "description": "",
+127                        "headers": {},
+128                        "content": {
+
+
171                ],
+172                "responses": {
+173                    "200": {
+174                        "description": "",
+175                        "headers": {},
+176                        "content": {
+
+
219                ],
+220                "responses": {
+221                    "200": {
+222                        "description": "",
+223                        "headers": {},
+224                        "content": {
+
+
267                ],
+268                "responses": {
+269                    "200": {
+270                        "description": "",
+271                        "headers": {},
+272                        "content": {
+
+
315                ],
+316                "responses": {
+317                    "200": {
+318                        "description": "",
+319                        "headers": {},
+320                        "content": {
+
+
363                ],
+364                "responses": {
+365                    "200": {
+366                        "description": "",
+367                        "headers": {},
+368                        "content": {
+
+
411                ],
+412                "responses": {
+413                    "200": {
+414                        "description": "",
+415                        "headers": {},
+416                        "content": {
+
+
459                ],
+460                "responses": {
+461                    "200": {
+462                        "description": "",
+463                        "headers": {},
+464                        "content": {
+
+
507                ],
+508                "responses": {
+509                    "200": {
+510                        "description": "",
+511                        "headers": {},
+512                        "content": {
+
+
555                ],
+556                "responses": {
+557                    "200": {
+558                        "description": "",
+559                        "headers": {},
+560                        "content": {
+
+
603                ],
+604                "responses": {
+605                    "200": {
+606                        "description": "",
+607                        "headers": {},
+608                        "content": {
+
+
651                ],
+652                "responses": {
+653                    "200": {
+654                        "description": "",
+655                        "headers": {},
+656                        "content": {
+
+
708                ],
+709                "responses": {
+710                    "200": {
+711                        "description": "",
+712                        "headers": {},
+713                        "content": {
+
+
756                ],
+757                "responses": {
+758                    "200": {
+759                        "description": "",
+760                        "headers": {},
+761                        "content": {
+
+
804                ],
+805                "responses": {
+806                    "200": {
+807                        "description": "",
+808                        "headers": {},
+809                        "content": {
+
+
852                ],
+853                "responses": {
+854                    "200": {
+855                        "description": "",
+856                        "headers": {},
+857                        "content": {
+
+
900                ],
+901                "responses": {
+902                    "200": {
+903                        "description": "",
+904                        "headers": {},
+905                        "content": {
+
+
948                ],
+949                "responses": {
+950                    "200": {
+951                        "description": "",
+952                        "headers": {},
+953                        "content": {
+
+
 996                ],
+ 997                "responses": {
+ 998                    "200": {
+ 999                        "description": "",
+1000                        "headers": {},
+1001                        "content": {
+
+
1062                ],
+1063                "responses": {
+1064                    "200": {
+1065                        "description": "",
+1066                        "headers": {},
+1067                        "content": {
+
+
1110                ],
+1111                "responses": {
+1112                    "200": {
+1113                        "description": "",
+1114                        "headers": {},
+1115                        "content": {
+
+
1158                ],
+1159                "responses": {
+1160                    "200": {
+1161                        "description": "",
+1162                        "headers": {},
+1163                        "content": {
+
+
1231                ],
+1232                "responses": {
+1233                    "200": {
+1234                        "description": "",
+1235                        "headers": {},
+1236                        "content": {
+
+
1288                ],
+1289                "responses": {
+1290                    "200": {
+1291                        "description": "",
+1292                        "headers": {},
+1293                        "content": {
+
+
1336                ],
+1337                "responses": {
+1338                    "200": {
+1339                        "description": "",
+1340                        "headers": {},
+1341                        "content": {
+
+
+ +
+
+
+
+
+
+ +
+
+ + \ No newline at end of file diff --git a/src/PHPDraft/In/ApibFileParser.php b/src/PHPDraft/In/ApibFileParser.php index e9ec4d1..8ff577e 100644 --- a/src/PHPDraft/In/ApibFileParser.php +++ b/src/PHPDraft/In/ApibFileParser.php @@ -160,6 +160,9 @@ private function get_schema(string $url): string $result = curl_exec($ch); curl_close($ch); + if ($result === false) { + throw new ExecutionException("Schema could not be fetched: $url", 1); + } return $result; } diff --git a/src/PHPDraft/Out/OpenAPI/OpenApiRenderer.php b/src/PHPDraft/Out/OpenAPI/OpenApiRenderer.php index b779e78..433fe3e 100644 --- a/src/PHPDraft/Out/OpenAPI/OpenApiRenderer.php +++ b/src/PHPDraft/Out/OpenAPI/OpenApiRenderer.php @@ -12,6 +12,8 @@ class OpenApiRenderer extends BaseTemplateRenderer { + const NO_DESCRIPTION_PROVIDED = 'No description provided'; + public function init(object $json): self { $this->object = $json; @@ -82,7 +84,11 @@ private function getServers(): array $return = []; $return[] = ['url' => $this->base_data['HOST'], 'description' => 'Main host']; - foreach (explode(',', $this->base_data['ALT_HOST'] ?? '') as $host) { + if (!isset($this->base_data['ALT_HOST'])) { + return $return; + } + + foreach (explode(',', $this->base_data['ALT_HOST']) as $host) { $return[] = ['url' => $host]; } @@ -123,7 +129,44 @@ private function getPaths(): object $request_return['responses'] = $this->toResponses($transition->responses); $transition_return[strtolower($request->method)] = (object) $request_return; } - $return[$transition->href] = (object) $transition_return; + $cleaned_href = preg_replace('/{\?.*}/', '', $transition->href); + $cleaned_href = rtrim($cleaned_href, "/"); + $optional_paths = array_filter($parameters, fn ($parameter) => $parameter['in'] === 'path' && $parameter['required'] === FALSE); + if ($optional_paths === []) { + $return[$cleaned_href] = (object) $transition_return; + continue; + } + + $cleaned_parameters = []; + $optional_href = NULL; + foreach ($parameters as $key => $parameter) { + if ($parameter['in'] === 'path' && $parameter['required'] === TRUE) { + $cleaned_parameters[] = $parameter; + continue; + } + + if ($parameter['in'] !== 'path') { continue; } + + $optional_href = str_replace("/{{$parameter["name"]}}", '', $cleaned_href); + $parameters[$key]['required'] = TRUE; + } + + //Full path + $transition_return['parameters'] = $parameters; + $return[$cleaned_href] = (object) $transition_return; + + //Path without optional item + $cleaned_return = $transition_return; + foreach ($cleaned_return as &$value) { + if (!is_object($value) || !property_exists($value, 'operationId')) { + continue; + } + + $value->operationId = $value->operationId . '-optional'; + } + $cleaned_return['parameters'] = $cleaned_parameters; + + $return[$optional_href] = (object) $cleaned_return; } } } @@ -146,8 +189,10 @@ private function toOperation(HTTPRequest $request, Transition $transition, array 'tags' => $tags, ]; $description = $request->description ?? $transition->description; - if ($description !== null) { + if ($description !== null && trim($description) !== '') { $operation['description'] = $description; + } else { + $operation['description'] = self::NO_DESCRIPTION_PROVIDED; } $parameters = []; @@ -175,7 +220,8 @@ private function toOperation(HTTPRequest $request, Transition $transition, array 'name' => $name, 'in' => 'header', 'schema' => ['type' => 'string'], - 'example' => $value, +// 'example' => $value, + 'description' => self::NO_DESCRIPTION_PROVIDED, ]; } @@ -184,11 +230,13 @@ private function toOperation(HTTPRequest $request, Transition $transition, array } $body = $this->toBody($request); - if ($body !== []) { - $body['required'] = true; - $operation['requestBody'] = $body; + if ($body === NULL) { + return $operation; } + $body['required'] = $body !== []; + $operation['requestBody'] = (object) $body; + return $operation; } @@ -216,17 +264,27 @@ private function toParameters(array $objects, string $href): array 'schema' => [], ]; if ($this->isRef($variable->type)) { - $return_tmp['schema']['$ref'] = '#/components/schemas/' . $variable->type; + var_dump($variable->type); + $return_tmp['schema']['$ref'] = '#/components/schemas/' . $this->refIdFromType($variable->type); + } elseif ($variable->type === 'enum') { + $return_tmp['schema']['type'] = $variable->value->type; + $return_tmp['schema']['enum'] = array_map(fn($item) => $item->value, $variable->value->value); } else { $return_tmp['schema']['type'] = $variable->type; } - if (isset($variable->value)) { - $return_tmp['example'] = $variable->value; + if (isset($variable->value) && $return_tmp['in'] === 'query') { + if (is_scalar($variable->value)) { + $return_tmp['example'] = $variable->value; + } else { + $return_tmp['example'] = $variable->value->value[0]?->value; + } } if (isset($variable->description)) { $return_tmp['description'] = $variable->description; + } else { + $return_tmp['description'] = self::NO_DESCRIPTION_PROVIDED; } $return[] = $return_tmp; } @@ -236,7 +294,7 @@ private function toParameters(array $objects, string $href): array private function isRef(string $type): bool { - return !in_array($type, ["array", "boolean", "integer", "null", "number", "object", "string"], true); + return !in_array($type, ["array", "boolean", "integer", "null", "number", "object", "string", "enum"], true); } /** @@ -286,7 +344,7 @@ private function toResponses(array $responses): array } $return[$response->statuscode] = [ - 'description' => $response->description ?? $response->title ?? '', + 'description' => $response->description ?? $response->title ?? self::NO_DESCRIPTION_PROVIDED, 'headers' => (object) $headers, 'content' => (object) $content, ]; @@ -300,37 +358,35 @@ private function toResponses(array $responses): array * * @param HTTPRequest $request Request to convert * - * @return array> OpenAPI style body + * @return array>|null OpenAPI style body */ - private function toBody(HTTPRequest $request): array + private function toBody(HTTPRequest $request): ?array { - $return = []; + if (in_array($request->method, ['GET', 'DELETE'], true)) { + return NULL; + } + $return = ['content' => []]; if (!is_array($request->struct) && $request->struct->description !== null) { $return['description'] = $request->struct->description; + } else { + $return['description'] = self::NO_DESCRIPTION_PROVIDED; } $content_type = $request->headers['Content-Type'] ?? 'text/plain'; - if (isset($request->struct) && $request->struct !== []) { + if (!isset($request->struct) && $request->struct !== []) { $content = $this->getComponent($request->struct); unset($content['required']); $return['content'] = [ $content_type => ['schema' => $content], ]; - } else { -// $return['content'] = [ -// $content_type => [ -// 'schema' => [ -// 'type' => 'string', -// ], -// ], -// ]; } if ($request->body !== null && $request->body !== []) { $return['content'][$content_type]['examples']['base']['value'] = $request->body[0]; } + $return['content'] = (object) $return['content']; return $return; } @@ -353,18 +409,19 @@ private function getComponents(): object foreach ($this->base_structures as $structure) { $object = $this->getComponent($structure); + $name = str_replace(' ', '_', $structure->type); if ($structure->ref !== null) { - $return[$structure->type] = [ - 'allOf' => [ - ['$ref' => "#/components/schemas/$structure->ref"], - $object, - ], + $return[$name] = [ + 'allOf' => [ + ['$ref' => "#/components/schemas/" . str_replace(' ', '_', $structure->ref)], + $object, + ], ]; } else { - $return[$structure->type] = $object; + $return[$name] = $object; } } - $return_object = ['schemas' => $return ]; + $return_object = ['schemas' => (object) $return ]; if (isset($this->base_data['API_KEY_HEADER'])) { $return_object['securitySchemes'] = [ 'api_key' => [ @@ -387,10 +444,16 @@ private function getComponents(): object */ private function getComponent(BasicStructureElement $structure): array { + $object = []; + if ($this->isRef($structure->element) && $structure->element !== 'enum') { + $object['$ref'] = '#/components/schemas/' . $this->refIdFromType($structure->element === 'member' ? $structure->type : $structure->element);; + return $object; + } + $required = []; $properties = []; if (is_array($structure->value)) { - /** @var BasicStructureElement $value */ + /** @var BasicStructureElement $value */ foreach ($structure->value as $value) { $propery_data = $this->getSchemaProperty($value); if ($propery_data === null) { @@ -404,13 +467,11 @@ private function getComponent(BasicStructureElement $structure): array } } - $object = [ - 'type' => $structure->element, - ]; switch ($structure->element) { case 'enum': +// $object['type'] = $structure->type;; case 'array': - $object['items'] = $properties; + $object['items'] = (object) $properties;; break; case 'object': $object['properties'] = $properties; @@ -425,6 +486,8 @@ private function getComponent(BasicStructureElement $structure): array if ($structure->description !== null) { $object['description'] = $structure->description; + } else { + $object['description'] = self::NO_DESCRIPTION_PROVIDED; } return $object; @@ -445,31 +508,37 @@ private function getSchemaProperty(BasicStructureElement|ElementStructureElement } $propery_data = []; - if ($value->description !== null) { - $propery_data['description'] = $value->description; - } - if ($this->isRef($value->type) && $value->type !== 'enum') { - $propery_data['$ref'] = '#/components/schemas/' . $value->type; + $propery_data['$ref'] = '#/components/schemas/' . $this->refIdFromType($value->type); return $propery_data; } + if ($value->description !== null) { + $propery_data['description'] = $value->description; + } + if ($value->type === 'enum') { - $propery_data['type'] = in_array('nullable', $value->status, true) ? [ $value->type, 'null' ] : $value->type; + $propery_data['type'] = in_array('nullable', $value->status, true) ? [ $value->value->type, 'null' ] : $value->value->type; $options = []; if (!is_iterable($value->value->value)) { return $propery_data; } foreach ($value->value->value as $option) { if ($option instanceof ElementStructureElement) { - $options[] = [ 'const' => $option->value, 'title' => $option->value ]; + $options[] = [ 'const' => $option->value, 'title' => "$option->value" ]; } } $propery_data['oneOf'] = $options; return $propery_data; } elseif ($value->type === 'array') { - $propery_data['type'] = array_unique(array_map(fn($item) => $item->type, $value->value->value)); + $type = array_unique(array_map(fn($item) => $item->type, $value->value->value))[0]; + if ($this->isRef($type)) { + $propery_data['$ref'] = '#/components/schemas/' . $this->refIdFromType($type); + return $propery_data; + } + + $propery_data['type'] = $type; $propery_data['example'] = array_merge(array_filter(array_map(fn($item) => $item->value, $value->value->value))); return $propery_data; @@ -520,4 +589,9 @@ private function getTags(): array return $return; } + + private function refIdFromType(string $type): string { + return str_replace(' ', '_', $type); + } + } diff --git a/src/PHPDraft/Out/OpenAPI/Tests/OpenApiRendererTest.php b/src/PHPDraft/Out/OpenAPI/Tests/OpenApiRendererTest.php index 8704be3..b48863f 100644 --- a/src/PHPDraft/Out/OpenAPI/Tests/OpenApiRendererTest.php +++ b/src/PHPDraft/Out/OpenAPI/Tests/OpenApiRendererTest.php @@ -25,6 +25,7 @@ public function tearDown(): void parent::tearDown(); } + public function testWrite(): void { $this->class->init((object)[]); @@ -55,7 +56,7 @@ public function testGetComponents(): void $method = $this->getReflectionMethod('getComponents'); $result = $method->invokeArgs($this->class, []); - $this->assertEquals((object)['schemas' => []], $result); + $this->assertEquals((object)['schemas' => (object)[]], $result); } public function testGetDocs(): void @@ -81,7 +82,7 @@ public function testGetServers(): void $method = $this->getReflectionMethod('getServers'); $result = $method->invokeArgs($this->class, []); - $this->assertEquals([['url' => null,'description' => 'Main host'], ['url' => '']], $result); + $this->assertEquals([['url' => null,'description' => 'Main host']], $result); } public function testGetApiInfo(): void @@ -110,11 +111,13 @@ public function testToBody(): void $mock = $this->getMockBuilder(HttpRequest::class) ->disableOriginalConstructor() ->getMock(); + $mock->method = 'PUT'; $method = $this->getReflectionMethod('toBody'); $result = $method->invokeArgs($this->class, [$mock]); - $this->assertEquals([], $result); + $this->assertEquals(['content' => (object)[], + 'description' => 'No description provided'], $result); } public function testToParameters(): void diff --git a/tests/statics/openapi/empty.json b/tests/statics/openapi/empty.json index 14497bf..04f83e2 100644 --- a/tests/statics/openapi/empty.json +++ b/tests/statics/openapi/empty.json @@ -10,15 +10,12 @@ { "url": null, "description": "Main host" - }, - { - "url": "" } ], "paths": {}, "webhooks": {}, "components": { - "schemas": [] + "schemas": {} }, "security": [], "tags": [] diff --git a/vacuum.conf.yaml b/vacuum.conf.yaml new file mode 100644 index 0000000..4a5287a --- /dev/null +++ b/vacuum.conf.yaml @@ -0,0 +1 @@ +ruleset: ./.vacuum/ruleset.yaml From 8cad55641884870d878265bb7cf356a8ec90f96b Mon Sep 17 00:00:00 2001 From: Sean Molenaar Date: Wed, 4 Mar 2026 11:40:37 +0100 Subject: [PATCH 3/3] fix: phpstan and require php 8.3 --- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 4 ++- build.xml | 2 +- composer.json | 2 +- composer.lock | 4 +-- phpstan.neon.dist | 2 +- src/PHPDraft/In/CLI.php | 31 ++++++++++++-------- src/PHPDraft/Out/OpenAPI/OpenApiRenderer.php | 2 +- 8 files changed, 28 insertions(+), 21 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d3c9420..585f9e0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # 2.36.0 with: - php-version: 8.2 + php-version: 8.3 ini-values: assert.exception=1, phar.readonly=0, zend.assertions=1 extensions: curl, json, phar, mbstring, gzip, bzip2, openssl tools: pecl, phing diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 752ada7..14867c5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,11 +16,13 @@ jobs: key: cache-v1 # can be any string, change to clear the extension cache. strategy: matrix: - php-versions: ['8.2', '8.3'] + php-versions: ['8.3'] experimental: [ false ] include: - php-versions: '8.4' experimental: true + - php-versions: '8.5' + experimental: true steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/build.xml b/build.xml index ef60439..fafc51f 100644 --- a/build.xml +++ b/build.xml @@ -1,7 +1,7 @@ - + diff --git a/composer.json b/composer.json index 04f49e0..8470338 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ } ], "require": { - "php": "^8.1", + "php": "^8.3", "michelf/php-markdown": "~2.0", "lukasoppermann/http-status": "~4.0", "ext-json": "*", diff --git a/composer.lock b/composer.lock index e59f678..2a1f542 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "00ae3c2461e27a91a90c1cd02a451988", + "content-hash": "3eedefe2b86599a32d7b9462e99f07be", "packages": [ { "name": "lukasoppermann/http-status", @@ -3995,7 +3995,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^8.1", + "php": "^8.3", "ext-json": "*", "ext-curl": "*" }, diff --git a/phpstan.neon.dist b/phpstan.neon.dist index a8e08e1..7669faa 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,5 +1,5 @@ parameters: - phpVersion: 80100 + phpVersion: 80300 bootstrapFiles: - tests/test.bootstrap.inc.php excludePaths: diff --git a/src/PHPDraft/In/CLI.php b/src/PHPDraft/In/CLI.php index a672ee7..f46c6e6 100644 --- a/src/PHPDraft/In/CLI.php +++ b/src/PHPDraft/In/CLI.php @@ -37,36 +37,41 @@ protected function setup(Options $options): void protected function main(Options $options): void { $args = $options->getOpt(); - if ($options->getOpt('version', NULL) !== NULL) { + if ($options->getOpt('version') !== FALSE) { Version::version(); throw new ExecutionException('', 0); } stream_set_blocking(STDIN, false); + /** @var false|string $stdin */ $stdin = stream_get_contents(STDIN); - $file = $options->getOpt('file', NULL); - if (!empty($stdin) && $file !== NULL) { - throw new ExecutionException('ERROR: Passed data in both file and stdin', 2); - } elseif (!empty($stdin) && $file === NULL) { - $file = tempnam(sys_get_temp_dir(), 'phpdraft'); - file_put_contents($file, $stdin); + $tmp_file_name = NULL; + if ($stdin !== FALSE && $stdin !== '') { + $tmp_file_name = tempnam(sys_get_temp_dir(), 'phpdraft'); + file_put_contents($tmp_file_name, $stdin); } - if ($file === NULL || $file === '') - { - throw new ExecutionException('ERROR: File does not exist', 200); + /** @var false|string $file_name */ + $file_name = $options->getOpt('file'); + if ($tmp_file_name !== NULL && $file_name !== FALSE) { + throw new ExecutionException('ERROR: Passed data in both file and stdin', 2); } - if (!($file !== NULL || $options->getOpt('debug-json-file') === FALSE || $options->getOpt('debug-json') === FALSE)) { + if (!($options->getOpt('debug-json-file') === FALSE || $options->getOpt('debug-json') === FALSE || $file_name !== FALSE)) { throw new ExecutionException('Missing required option: file', 1); } + if ($file_name === FALSE || $file_name === '') + { + throw new ExecutionException('ERROR: File does not exist', 200); + } + define('THIRD_PARTY_ALLOWED', getenv('PHPDRAFT_THIRD_PARTY') !== '0'); if ((isset($args['yes']) || isset($args['online'])) && THIRD_PARTY_ALLOWED) { define('DRAFTER_ONLINE_MODE', 1); } if (!isset($args['debug-json-file']) && !isset($args['debug-json'])) { - $apib_parser = new ApibFileParser($file); + $apib_parser = new ApibFileParser($file_name); $apib = $apib_parser->parse(); try { @@ -88,7 +93,7 @@ protected function main(Options $options): void $html = ParserFactory::getJson()->init($data); $name = 'PHPD_SORT_' . strtoupper($options->getOpt('sort', '')); - $html->sorting = Sorting::${$name} ?? Sorting::PHPD_SORT_NONE->value; + $html->sorting = defined("Sorting::$name") ? Sorting::{$name} : Sorting::PHPD_SORT_NONE->value; $color1 = getenv('COLOR_PRIMARY') === FALSE ? NULL : getenv('COLOR_PRIMARY'); $color2 = getenv('COLOR_SECONDARY') === FALSE ? NULL : getenv('COLOR_SECONDARY'); diff --git a/src/PHPDraft/Out/OpenAPI/OpenApiRenderer.php b/src/PHPDraft/Out/OpenAPI/OpenApiRenderer.php index 433fe3e..1797d1a 100644 --- a/src/PHPDraft/Out/OpenAPI/OpenApiRenderer.php +++ b/src/PHPDraft/Out/OpenAPI/OpenApiRenderer.php @@ -358,7 +358,7 @@ private function toResponses(array $responses): array * * @param HTTPRequest $request Request to convert * - * @return array>|null OpenAPI style body + * @return null|array OpenAPI style body */ private function toBody(HTTPRequest $request): ?array {