diff --git a/Dockerfile b/Dockerfile index dcd99c0..d8a8e4c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # # Base install # -FROM amd64/php:8.0-apache as base +FROM amd64/php:8.1-apache as base LABEL vendor="SwaggerLume" @@ -60,7 +60,7 @@ WORKDIR /app/swaggerLume-app RUN /usr/local/bin/php -dxdebug.mode=off /usr/local/bin/composer config repositories.swagger-lume path '../' -RUN /usr/local/bin/php -dxdebug.mode=off /usr/local/bin/composer require 'DarkaOnLine/swagger-lume:dev-master' +RUN /usr/local/bin/php -dxdebug.mode=off /usr/local/bin/composer require 'darkaonline/swagger-lume:dev-master' RUN ln -s /app/tests/storage/annotations/ app/annotations diff --git a/README.md b/README.md index d5de8b4..dfa5ff1 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Installation 7.0 | 3 | 2.0, 3.0 | ``` composer require "darkaonline/swagger-lume:7.*" ``` 8.0 | 3 | 2.0, 3.0 | ``` composer require "darkaonline/swagger-lume:8.*" ``` 9.0 | 3 | 2.0, 3.0 | ``` composer require "darkaonline/swagger-lume:9.*" ``` + 10.0 | 3 | 2.0, 3.0 | ``` composer require "darkaonline/swagger-lume:10.*" ``` - Open your `bootstrap/app.php` file and: diff --git a/composer.json b/composer.json index 8b17321..229fc97 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,8 @@ "php": ">=7.2 || ^8.0", "laravel/lumen-framework": "~6.0 || ~7.0 || ^8.0 || ^9.0 || ^10.0", "zircote/swagger-php": "~2.0 || 3.*", - "swagger-api/swagger-ui": "^3.0 || ^4.0" + "swagger-api/swagger-ui": "^3.0 || ^4.0", + "symfony/yaml": "^4.0 || ^5.0 || ^6.2" }, "require-dev": { "phpunit/phpunit": "^10.0 || ^9.5", diff --git a/config/swagger-lume.php b/config/swagger-lume.php index 07967b5..4805cbe 100644 --- a/config/swagger-lume.php +++ b/config/swagger-lume.php @@ -67,6 +67,19 @@ */ 'docs_json' => 'api-docs.json', + /* + |-------------------------------------------------------------------------- + | File name of the generated YAML documentation file + |-------------------------------------------------------------------------- + */ + + 'docs_yaml' => 'api-docs.yaml', + + /* + * Set this to `json` or `yaml` to determine which documentation file to use in UI + */ + 'format_to_use_for_docs' => env('SWAGGER_LUME__FORMAT_TO_USE_FOR_DOCS', 'json'), + /* |-------------------------------------------------------------------------- | Absolute path to directory containing the swagger annotations are stored. @@ -151,6 +164,14 @@ */ 'generate_always' => env('SWAGGER_GENERATE_ALWAYS', false), + /* + |-------------------------------------------------------------------------- + | Turn this on to generate a copy of documentation in yaml format + |-------------------------------------------------------------------------- + */ + + 'generate_yaml_copy' => env('SWAGGER_LUME_GENERATE_YAML_COPY', false), + /* |-------------------------------------------------------------------------- | Edit to set the swagger version number diff --git a/src/Generator.php b/src/Generator.php index 180995f..4cea163 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -4,6 +4,10 @@ use Illuminate\Support\Facades\File; use OpenApi\Annotations\Server; +use OpenApi\Generator as OpenApiGenerator; +use OpenApi\Util; +use Symfony\Component\Yaml\Dumper as YamlDumper; +use Symfony\Component\Yaml\Yaml; class Generator { @@ -23,7 +27,9 @@ public static function generateDocs() $excludeDirs = config('swagger-lume.paths.excludes'); if (version_compare(config('swagger-lume.swagger_version'), '3.0', '>=')) { - $swagger = \OpenApi\scan($appDir, ['exclude' => $excludeDirs]); + $generator = new OpenApiGenerator(); + $finder = Util::finder($appDir, $excludeDirs); + $swagger = $generator->generate($finder); } else { $swagger = \Swagger\scan($appDir, ['exclude' => $excludeDirs]); } @@ -38,11 +44,13 @@ public static function generateDocs() } } - $filename = $docDir.'/'.config('swagger-lume.paths.docs_json'); + $filename = sprintf('%s/%s', $docDir, config('swagger-lume.paths.docs_json')); $swagger->saveAs($filename); $security = new SecurityDefinitions(); $security->generate($filename); + + self::makeYamlCopy($filename); } } @@ -54,4 +62,19 @@ protected static function defineConstants(array $constants) } } } + + protected static function makeYamlCopy($filename) + { + if (config('swagger-lume.generate_yaml_copy')) { + $path = sprintf('%s/%s', config('swagger-lume.paths.docs'), config('swagger-lume.paths.docs_yaml')); + $yamlContent = (new YamlDumper(2))->dump( + json_decode(file_get_contents($filename), true), + 20, + 0, + Yaml::DUMP_OBJECT_AS_MAP ^ Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE + ); + + file_put_contents($path, $yamlContent); + } + } } diff --git a/src/Http/Controllers/SwaggerLumeController.php b/src/Http/Controllers/SwaggerLumeController.php index afb7da2..a73ad58 100644 --- a/src/Http/Controllers/SwaggerLumeController.php +++ b/src/Http/Controllers/SwaggerLumeController.php @@ -4,7 +4,8 @@ use Illuminate\Http\Response; use Illuminate\Support\Facades\File; -use Illuminate\Support\Facades\Request; +use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Request as RequestFacade; use Laravel\Lumen\Routing\Controller as BaseController; use SwaggerLume\Generator; @@ -13,13 +14,24 @@ class SwaggerLumeController extends BaseController /** * Dump api-docs.json content endpoint. * - * @param null $jsonFile + * @param null $docsFile * @return \Illuminate\Http\Response */ - public function docs($jsonFile = null) + public function docs() { - $filePath = config('swagger-lume.paths.docs').'/'. - (! is_null($jsonFile) ? $jsonFile : config('swagger-lume.paths.docs_json')); + $filePath = sprintf( + '%s/%s', + config('swagger-lume.paths.docs'), + config('swagger-lume.paths.format_to_use_for_docs') === 'json' ? config('swagger-lume.paths.docs_json') : config('swagger-lume.paths.docs_yaml') + ); + + $yaml = false; + $parts = explode('.', $filePath); + + if (! empty($parts)) { + $extension = array_pop($parts); + $yaml = strtolower($extension) === 'yaml'; + } if (config('swagger-lume.generate_always') && ! File::exists($filePath)) { try { @@ -44,6 +56,14 @@ public function docs($jsonFile = null) $content = File::get($filePath); + if ($yaml) { + return Response($content, 200, [ + 'Content-Type' => 'application/yaml', + 'Content-Disposition' => 'inline', + 'filename' => config('swagger-lume.paths.docs_yaml'), + ]); + } + return new Response($content, 200, ['Content-Type' => 'application/json']); } @@ -59,9 +79,9 @@ public function api() } //need the / at the end to avoid CORS errors on Homestead systems. - $response = new Response( + return new Response( view('swagger-lume::index', [ - 'secure' => Request::secure(), + 'secure' => RequestFacade::secure(), 'urlToDocs' => route('swagger-lume.docs'), 'operationsSorter' => config('swagger-lume.operations_sort'), 'configUrl' => config('swagger-lume.additional_config_url'), @@ -70,8 +90,6 @@ public function api() 200, ['Content-Type' => 'text/html'] ); - - return $response; } /** diff --git a/tests/GeneratorTest.php b/tests/GeneratorTest.php index ee3b805..c9290f7 100644 --- a/tests/GeneratorTest.php +++ b/tests/GeneratorTest.php @@ -7,13 +7,14 @@ class GeneratorTest extends LumenTestCase { /** @test */ - public function canGenerateApiJsonFile() + public function canGenerateApiJsonAndYamlFile() { $this->setPaths(); Generator::generateDocs(); $this->assertTrue(file_exists($this->jsonDocsFile())); + $this->assertTrue(file_exists($this->yamlDocsFile())); $response = $this->get(config('swagger-lume.routes.docs')); @@ -24,7 +25,7 @@ public function canGenerateApiJsonFile() } /** @test */ - public function canGenerateApiJsonFileWithChangedBasePath() + public function canGenerateApiJsonAndYamlFileWithChangedBasePath() { if ($this->isOpenApi() == true) { $this->markTestSkipped('only for openApi 2.0'); @@ -39,6 +40,7 @@ public function canGenerateApiJsonFileWithChangedBasePath() Generator::generateDocs(); $this->assertTrue(file_exists($this->jsonDocsFile())); + $this->assertTrue(file_exists($this->yamlDocsFile())); $response = $this->get(config('swagger-lume.routes.docs')); diff --git a/tests/LumenTestCase.php b/tests/LumenTestCase.php index 102523e..a5523fa 100644 --- a/tests/LumenTestCase.php +++ b/tests/LumenTestCase.php @@ -23,6 +23,10 @@ public function tearDown(): void if (file_exists($this->jsonDocsFile())) { unlink($this->jsonDocsFile()); } + + if (file_exists($this->yamlDocsFile())) { + unlink($this->yamlDocsFile()); + } parent::tearDown(); } @@ -106,6 +110,8 @@ protected function setPaths() //For test we want to regenerate always $cfg['generate_always'] = true; + $cfg['generate_yaml_copy'] = true; + //Adding constants which will be replaced in generated json file $cfg['constants']['SWAGGER_LUME_CONST_HOST'] = 'http://my-default-host.com'; @@ -122,14 +128,32 @@ protected function setPaths() return $this; } - protected function crateJsonDocumentationFile() + protected function createJsonDocumentationFile() { file_put_contents($this->jsonDocsFile(), ''); } + protected function createYamlDocumentationFile() + { + file_put_contents($this->yamlDocsFile(), ''); + } + protected function jsonDocsFile() { - return config('swagger-lume.paths.docs').'/api-docs.json'; + return sprintf( + '%s/%s', + config('swagger-lume.paths.docs'), + config('swagger-lume.paths.docs_json') + ); + } + + protected function yamlDocsFile() + { + return sprintf( + '%s/%s', + config('swagger-lume.paths.docs'), + config('swagger-lume.paths.docs_yaml') + ); } protected function copyAssets() diff --git a/tests/RoutesTest.php b/tests/RoutesTest.php index 5ebd34b..48a30a7 100644 --- a/tests/RoutesTest.php +++ b/tests/RoutesTest.php @@ -19,7 +19,7 @@ public function canAccessJsonFile() { $jsonUrl = config('swagger-lume.routes.docs'); - $this->setPaths()->crateJsonDocumentationFile(); + $this->setPaths()->createJsonDocumentationFile(); $this->get($jsonUrl);