Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Proposal for Enhancing OpenAPI Code Generation #2786

Cafeine42 started this conversation in Ideas
Discussion options

Dear ApiPlatform team,

I hope this message finds you well. I am currently working on encouraging the use of OpenAPI specifications with code generation tools to reduce the workload for developers working with APIs. In this context, I’ve been exploring ways to improve the code generated from the OpenAPI specifications produced by ApiPlatform.

During this process, I encountered a few challenges that led me to implement a custom NormalizerInterface to address some issues. I believe these enhancements could be beneficial for the broader community, and I wanted to ask if it would be possible to integrate them directly into ApiPlatform.

One potential improvement could involve creating specialized classes for certain parameters to avoid the presence of properties like allowEmptyValue when it is not authorized. Additionally, it might be helpful to offer an option that prevents the exposure of unused schemas, ensuring a cleaner and more efficient API specification.

Could you please advise on the best approach to contribute these improvements? I’d appreciate your thoughts on whether these changes could be integrated into ApiPlatform directly, and what the ideal method for implementing them might be.

Thank you for your time and consideration. I look forward to hearing from you.

Best regards,

use ApiPlatform\OpenApi\OpenApi;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

class OpenApiNormalizer implements NormalizerInterface
{
    public function __construct(
        #[Autowire(service: 'api_platform.openapi.normalizer')]
        private readonly NormalizerInterface $normalizer,
    ) {
    }

    public function normalize(mixed $object, ?string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null
    {
        /**
         * @var array{paths: array<string, array<string, array{tags?: string[], parameters: array<string, array{in: string, allowEmptyValue: bool, allowReserved: bool}>}>>, components: array{schemas: array<string, array<string, string>>}} $result
         */
        $result = $this->normalizer->normalize($object, $format, $context);

        if ($object instanceof OpenApi && '3' == $context['spec_version']) {
            // Remove allowEmptyValue & allowReserved on parameters see https://spec.openapis.org/oas/v3.1.0#fixed-fields-9
            foreach ($result['paths'] as &$path) {
                foreach ($path as &$method) {
                    foreach ($method['parameters'] as &$parameters) {
                        // allowEmptyValue & allowReserved only applicable for query parameters
                        if ('query' !== $parameters['in']) {
                            unset($parameters['allowEmptyValue']);
                            unset($parameters['allowReserved']);
                        }
                    }
                }
            }

            // Remove unused components, could be optimized with list of depencies per schema and progressive elemination of schema without dependencies
            do {
                $refs = [];
                $removed = 0;
                foreach (new \RecursiveIteratorIterator(new \RecursiveArrayIterator($result), \RecursiveIteratorIterator::LEAVES_ONLY) as $key => $value) {
                    if ('$ref' === $key) {
                        $refs[] = $value;
                    }
                }
                foreach ($result['components']['schemas'] as $key => $schema) {
                    if (!\in_array('#/components/schemas/'.$key, $refs)) {
                        unset($result['components']['schemas'][$key]);
                        ++$removed;
                    }
                }
            } while ($removed > 0);

            // Add global tags
            $tags = [];
            foreach ($result['paths'] as &$path) {
                foreach ($path as &$method) {
                    $tags = array_merge($tags, $method['tags'] ?? []);
                }
            }
            $result['tags'] = array_map(fn (string $tag) => [
                'name' => $tag,
            ], array_values(array_unique($tags)));
        }

        return $result;
    }

    public function supportsNormalization(mixed $data, ?string $format = null): bool
    {
        return $data instanceof OpenApi && 'json' === $format;
    }

    /**
     * @return array<class-string, bool>
     */
    public function getSupportedTypes(?string $format): array
    {
        return [
            OpenApi::class => true,
        ];
    }
}
You must be logged in to vote

Replies: 0 comments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
💡
Ideas
Labels
None yet
1 participant
Morty Proxy This is a proxified and sanitized view of the page, visit original site.