knuckleswtf / scribe

Generate API documentation for humans from your Laravel codebase.✍
https://scribe.knuckles.wtf/laravel/
MIT License
1.75k stars 314 forks source link

Required array marked as "optional" #563

Closed bjhijmans closed 1 year ago

bjhijmans commented 1 year ago

What happened? I have a route with the following rules:

[
    'data' => 'array|required',
    'data.title' => 'string|required',
]

and the following bodyParameters:

[
    'data' => [
          'description' => '...',
          'example' => ['title' => 'Example Title']
      ]
]

I've updated to 4.6.0 that fixed a bug where the first validation rule caused an exception. However, I was surprised to find that the output for the data field still showed it as optional, even though it is now explicitly required.

image

I think it shouldn't be marked as optional even with the following rules, because it has required children. But it definitely shouldn't be if it is explicitly required.

[
    'data.title' => 'string|required'
]

My environment:

My Scribe config (minus the comments):

NOTE: I disabled all custom strategies and got the same result.


    'theme' => 'default',
    'title' => config('app.name') . ' API Documentation',
    'description' => '',
    'base_url' => null,
    'routes' => [[
        'match' => [
            'domains' => ['*'],
            'prefixes' => ['api/*'],
            'versions' => ['v1'],
        ],
        'include' => [],
        'exclude' => [],
        'apply' => [
            'headers' => ['Content-Type' => 'application/json', 'Accept' => 'application/json'],
            'response_calls' => [
                'methods' => [],
                'config' => ['app.env' => 'documentation'],
                'queryParams' => [],
                'bodyParams' => [],
                'fileParams' => [],
                'cookies' => [],
            ],
        ],
    ]],
    'type' => 'laravel',
    'static' => [
        'output_path' => 'public/docs',
    ],
    'laravel' => [
        'add_routes' => false,
        'docs_url' => '/api-documentation',
        'middleware' => [],
        'assets_directory' => null,
    ],

    'try_it_out' => [
        'enabled' => false,
        'base_url' => null,
        'use_csrf' => false,
        'csrf_url' => '/sanctum/csrf-cookie',
    ],
    'auth' => [
        'enabled' => true,
        'default' => true,
        'in' => 'bearer',
        'name' => 'key',
        'use_value' => env('SCRIBE_AUTH_KEY'),
        'placeholder' => '{API_TOKEN}',
        'extra_info' => '',
    ],
    'intro_text' => <<<INTRO
 <snip>
INTRO

    ,
    'example_languages' => [
        'bash',
        'javascript',
        'python',
        'php',
    ],
    'postman' => [
        'enabled' => true,
        'overrides' => [
            // 'info.version' => '2.0.0',
        ],
    ],
    'openapi' => [
        'enabled' => true,
        'overrides' => [
            // 'info.version' => '2.0.0',
        ],
    ],
    'logo' => false,
    'strategies' => [
        'metadata' => [
            Strategies\Metadata\GetFromDocBlocks::class,
        ],
        'urlParameters' => [
//            Strategies\UrlParameters\GetFromLaravelAPI::class,
//            Strategies\UrlParameters\GetFromLumenAPI::class,
//            Strategies\UrlParameters\GetFromUrlParamTag::class,
            \App\Http\Api\Docs\Strategies\GetFromLaravelApiWithRandomUuid::class
        ],
        'queryParameters' => [
            Strategies\QueryParameters\GetFromFormRequest::class,
            Strategies\QueryParameters\GetFromInlineValidator::class,
            Strategies\QueryParameters\GetFromQueryParamTag::class,
            \App\Http\Api\Docs\Strategies\AddFilterParam::class,
            \App\Http\Api\Docs\Strategies\AddSortParam::class,
            \App\Http\Api\Docs\Strategies\AddIncludeParam::class,
        ],
        'headers' => [
            Strategies\Headers\GetFromRouteRules::class,
            Strategies\Headers\GetFromHeaderTag::class,
        ],
        'bodyParameters' => [
            Strategies\BodyParameters\GetFromFormRequest::class,
            Strategies\BodyParameters\GetFromInlineValidator::class,
            Strategies\BodyParameters\GetFromBodyParamTag::class,
            \App\Http\Api\Docs\Strategies\CheckFormRequestRules::class,
        ],
        'responses' => [
            Strategies\Responses\UseTransformerTags::class,
            Strategies\Responses\UseResponseTag::class,
            Strategies\Responses\UseResponseFileTag::class,
            Strategies\Responses\UseApiResourceTags::class,
            Strategies\Responses\ResponseCalls::class,
            \App\Http\Api\Docs\Strategies\AddExampleResponses::class,
            \App\Http\Api\Docs\Strategies\AddPaginatedExampleResponses::class,
        ],
        'responseFields' => [
            Strategies\ResponseFields\GetFromResponseFieldTag::class,
        ],
    ],

    'fractal' => [
        'serializer' => null,
    ],
    'routeMatcher' => \Knuckles\Scribe\Matching\RouteMatcher::class,
    'database_connections_to_transact' => [config('database.default'), config('database.default')],
    'groups' => [
        'default' => 'Endpoints',
        'order' => [],
    ],
    'last_updated' => 'Last updated: {date:F j, Y}',

    'examples' => [
        'faker_seed' => null,
        'models_source' => ['factoryCreate', 'factoryMake', 'databaseFirst'],
    ]

Additional info:

shalvah commented 1 year ago

Couldn't reproduce this. v4.6

image

image

(Btw, an object can be optional but have required fields. Means that if the object is supplied, it must have these fields.)