Ver código fonte

Implemented overrides support for OpenAPI spec

shalvah 4 anos atrás
pai
commit
2f7ab2d0eb

+ 14 - 4
config/scribe.php

@@ -116,12 +116,9 @@ INTRO
      * Generate a Postman collection in addition to HTML docs.
      * For 'static' docs, the collection will be generated to public/docs/collection.json.
      * For 'laravel' docs, it will be generated to storage/app/scribe/collection.json.
-     * Setting `laravel.autoload` to true (above) will add routes for both the HTML and the Postman collection.
+     * Setting `laravel.add_routes` to true (above) will also add a route for the collection.
      */
     'postman' => [
-        /*
-         * Specify whether the Postman collection should be generated.
-         */
         'enabled' => true,
 
         /*
@@ -142,8 +139,21 @@ INTRO
         'auth' => null,
     ],
 
+    /*
+     * Generate an OpenAPI spec file in addition to docs webpage.
+     * For 'static' docs, the collection will be generated to public/docs/openapi.yaml.
+     * For 'laravel' docs, it will be generated to storage/app/scribe/openapi.yaml.
+     * Setting `laravel.add_routes` to true (above) will also add a route for the spec.
+     */
     'openapi' => [
         'enabled' => true,
+
+        /*
+         * Manually override some generated content in the spec. Dot notation is supported.
+         */
+        'overrides' => [
+            // 'info.version' => '2.0.0',
+        ],
     ],
 
     /*

+ 8 - 1
src/Writing/Writer.php

@@ -229,7 +229,14 @@ class Writer
             ['config' => $this->config]
         );
 
-        return Yaml::dump($writer->generateSpecContent($groupedEndpoints), 10, 4, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE | Yaml::DUMP_OBJECT_AS_MAP);
+        $spec = $writer->generateSpecContent($groupedEndpoints);
+        $overrides = $this->config->get('openapi.overrides');
+        if (count($overrides)) {
+            foreach ($overrides as $key => $value) {
+                data_set($spec, $key, $value);
+            }
+        }
+        return Yaml::dump($spec, 10, 4, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE | Yaml::DUMP_OBJECT_AS_MAP);
     }
 
     protected function performFinalTasksForLaravelType(): void

+ 229 - 0
tests/Fixtures/openapi-overridden.yaml

@@ -0,0 +1,229 @@
+openapi: 3.0.3
+info:
+    title: null
+    description: ''
+    version: 3.9.9
+servers:
+    -
+        url: 'http://okay.dev'
+paths:
+    /api/withDescription:
+        get:
+            summary: 'Example title.'
+            description: "This will be the long description.\nIt can also be multiple lines long."
+            parameters:
+                -
+                    in: header
+                    name: Content-Type
+                    description: ''
+                    example: application/json
+                    schema:
+                        type: string
+                -
+                    in: header
+                    name: Accept
+                    description: ''
+                    example: application/json
+                    schema:
+                        type: string
+            responses: {  }
+            tags:
+                - 'Group A'
+            security: []
+    /api/withResponseTag:
+        get:
+            summary: ''
+            description: ''
+            parameters:
+                -
+                    in: header
+                    name: Content-Type
+                    description: ''
+                    example: application/json
+                    schema:
+                        type: string
+                -
+                    in: header
+                    name: Accept
+                    description: ''
+                    example: application/json
+                    schema:
+                        type: string
+            responses:
+                200:
+                    description: '200'
+                    content:
+                        application/json:
+                            schema:
+                                type: object
+                                example:
+                                    id: 4
+                                    name: banana
+                                    color: red
+                                    weight: '1 kg'
+                                    delicious: true
+                                    responseTag: true
+                                properties:
+                                    id: { type: integer, example: 4 }
+                                    name: { type: string, example: banana }
+                                    color: { type: string, example: red }
+                                    weight: { type: string, example: '1 kg' }
+                                    delicious: { type: boolean, example: true }
+                                    responseTag: { type: boolean, example: true }
+            tags:
+                - 'Group A'
+            security: []
+    /api/withBodyParameters:
+        post:
+            summary: 'Endpoint with body parameters.'
+            description: ''
+            parameters:
+                -
+                    in: header
+                    name: Content-Type
+                    description: ''
+                    example: application/json
+                    schema:
+                        type: string
+                -
+                    in: header
+                    name: Accept
+                    description: ''
+                    example: application/json
+                    schema:
+                        type: string
+            responses: {  }
+            tags:
+                - 'Group A'
+            requestBody:
+                required: true
+                content:
+                    application/json:
+                        schema:
+                            type: object
+                            properties:
+                                user_id:
+                                    type: integer
+                                    description: 'The id of the user.'
+                                    example: 9
+                                room_id:
+                                    type: string
+                                    description: 'The id of the room.'
+                                    example: consequatur
+                                forever:
+                                    type: boolean
+                                    description: 'Whether to ban the user forever.'
+                                    example: false
+                                another_one:
+                                    type: number
+                                    description: 'Just need something here.'
+                                    example: 11613.31890586
+                                yet_another_param:
+                                    type: object
+                                    description: 'Some object params.'
+                                    example: {  }
+                                yet_another_param.name:
+                                    type: string
+                                    description: 'Subkey in the object param.'
+                                    example: consequatur
+                                even_more_param:
+                                    type: array
+                                    description: 'Some array params.'
+                                    example: []
+                                    items: { type: object }
+                                'even_more_param.*':
+                                    type: number
+                                    description: 'Subkey in the array param.'
+                                    example: 11613.31890586
+                                book.name:
+                                    type: string
+                                    description: ''
+                                    example: consequatur
+                                book.author_id:
+                                    type: integer
+                                    description: ''
+                                    example: 17
+                                'book[pages_count]':
+                                    type: integer
+                                    description: ''
+                                    example: 17
+                                'ids.*':
+                                    type: integer
+                                    description: ''
+                                    example: 17
+                                'users.*.first_name':
+                                    type: string
+                                    description: 'The first name of the user.'
+                                    example: John
+                                'users.*.last_name':
+                                    type: string
+                                    description: 'The last name of the user.'
+                                    example: Doe
+                            required:
+                                - user_id
+                                - yet_another_param
+                                - yet_another_param.name
+            security: []
+    /api/withQueryParameters:
+        get:
+            summary: ''
+            description: ''
+            parameters:
+                -
+                    in: query
+                    name: location_id
+                    description: 'The id of the location.'
+                    example: consequatur
+                    required: true
+                    schema:
+                        type: string
+                -
+                    in: query
+                    name: user_id
+                    description: 'The id of the user.'
+                    example: me
+                    required: true
+                    schema:
+                        type: string
+                -
+                    in: query
+                    name: page
+                    description: 'The page number.'
+                    example: '4'
+                    required: true
+                    schema:
+                        type: string
+                -
+                    in: query
+                    name: 'filters.*'
+                    description: 'The filters.'
+                    example: consequatur
+                    required: false
+                    schema:
+                        type: string
+                -
+                    in: query
+                    name: url_encoded
+                    description: 'Used for testing that URL parameters will be URL-encoded where needed.'
+                    example: '+ []&='
+                    required: false
+                    schema:
+                        type: string
+                -
+                    in: header
+                    name: Content-Type
+                    description: ''
+                    example: application/json
+                    schema:
+                        type: string
+                -
+                    in: header
+                    name: Accept
+                    description: ''
+                    example: application/json
+                    schema:
+                        type: string
+            responses: {  }
+            tags:
+                - 'Group A'
+            security: []

+ 23 - 0
tests/GenerateDocumentationTest.php

@@ -293,6 +293,29 @@ class GenerateDocumentationTest extends TestCase
         $this->assertEquals($fixtureCollection, $generatedCollection);
     }
 
+    /** @test */
+    public function can_override_fields_in_generated_openapi_spec_file()
+    {
+        RouteFacade::get('/api/withDescription', [TestController::class, 'withEndpointDescription']);
+        RouteFacade::get('/api/withResponseTag', TestController::class . '@withResponseTag');
+        RouteFacade::post('/api/withBodyParameters', TestController::class . '@withBodyParameters');
+        RouteFacade::get('/api/withQueryParameters', TestController::class . '@withQueryParameters');
+
+        config(['scribe.faker_seed' => 1234]);
+        config(['scribe.openapi.enabled' => true]);
+        config(['scribe.openapi.overrides' => [
+            'info.version' => '3.9.9',
+            'servers.0.url' => 'http://okay.dev',
+        ]]);
+        config(['scribe.routes.0.match.prefixes' => ['api/*']]);
+
+        $this->artisan('scribe:generate');
+
+        $generatedCollection = json_decode(file_get_contents(__DIR__ . '/../public/docs/openapi.yaml'), true);
+        $fixtureCollection = json_decode(file_get_contents(__DIR__ . '/Fixtures/openapi-overridden.yaml'), true);
+        $this->assertEquals($fixtureCollection, $generatedCollection);
+    }
+
     /** @test */
     public function generated_postman_collection_domain_is_correct()
     {