How to ask for additional GET parameters in an endpoint of api-platform using swagger docs?
Asked Answered
B

9

17

I have a symfony project, where I use api-platform.

I have an entity, and I have data providers for it. I am in trouble with definition of additional parameters for a collection endpoint.

An entity is called suggestion. It has to return collection of documents from elastic search.

An endpoint is:

/suggestion

This endpoint listens to additional GET parameters:

page, level

These two params are read each time, when the endpoint is requested.

In my SuggestionsCollectionDataProvider.php class I have:

/**
     * Retrieves a collection.
     *
     * @param string $resourceClass
     * @param string|null $operationName
     * @return \Generator
     */
    public function getCollection(string $resourceClass, string $operationName = null): \Generator
    {
        $query = $this->requestStack->getCurrentRequest()->query;
        // I am reading these two parameters from RequestStack
        
        // this one is built-in
        $page = max($query->get('page', 1), 1); 

        // this is a custom one
        $level = $query->get('level', 0); 
        ...

In my SuggestionRepository.php class:

/**
     * @return \Generator
     */
    public function find(int $page, int $level): \Generator
    {
        // here I can process with $level without problems

Page parameter is default parameter, that is generating in swagger for collections.

A screenshot from API platform generated Swagger doc:

enter image description here

But the page parameter is now the only parameter, that can be edited in web version.

I need to add more parameters (level in this case) to swagger and describe them, so the user/tester knows which parameter actually goes to this endpoint.

Main question:

How to tell api-platform, that I want a user/tester of the API (from client side) to enter some other parameters, i.e. level for example?

Boarhound answered 16/5, 2018 at 11:40 Comment(0)
B
15

Finally figured it out.

I haven't found a documentation for it yet, but I found a way.

In an entity class Suggestion.php I've added some lines of annotations:

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Class Suggestion. Represents an entity for an item from the suggestion result set.
 * @package App\Entity
 * @ApiResource(
 *     collectionOperations={
 *          "get"={
 *              "method"="GET",
 *              "swagger_context" = {
 *                  "parameters" = {
 *                      {
 *                          "name" = "level",
 *                          "in" = "query",
 *                          "description" = "Levels available in result",
 *                          "required" = "true",
 *                          "type" : "array",
 *                          "items" : {
 *                              "type" : "integer"
 *                          }
 *                      }
 *                  }
 *               }
 *          }
 *     },
 *     itemOperations={"get"}
 * )
 */

The result view in API platform swagger DOCs:

enter image description here

Boarhound answered 16/5, 2018 at 12:50 Comment(3)
Change "swagger_context" to "openapi_context" if you are in the last versionsBalbo
Attention, "required" = "true" works because the string "true" evaluates to true. "required" = "false" doesn't work, it must be "required" = falseStimulate
And documentation is here: swagger.io/docs/specification/describing-parameters The Api Platform docs don't cover Swagger/OpenAPI specification muchStimulate
C
4

Adding "swagger_context" to the get annotation didn't work for me in Api Platform core version 2.4.6. Instead, I used the instructions provided at API Platform - Overriding the OpenAPI Specification

In my case, I had to deviate from the instructions a little bit. In the overridden normalize method, I had to remove the existing parameter first, then added the customized definition to the $doc parameters array. As pedrouan did, I was able to add the required=true property and it worked in the same manner.

In services.yaml I added:

    App\Swagger\SwaggerEventRequireDecorator:
         decorates: 'api_platform.swagger.normalizer.api_gateway'
         arguments: [ '@App\Swagger\SwaggerEventRequireDecorator.inner' ]
         autoconfigure: false

In App\Swagger folder, I added the following class:

<?php

namespace App\Swagger;

use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

final class SwaggerEventRequireDecorator implements NormalizerInterface
{
    private $decorated;

    public function __construct(NormalizerInterface $decorated)
    {
        $this->decorated = $decorated;
    }

    public function normalize($object, $format = null, array $context = [])
    {
        $docs = $this->decorated->normalize($object, $format, $context);

        $customDefinition = [
            'name' => 'event',
            'description' => 'ID of the event the activities belong to.',
            'in' => 'query',
            'required' => 'true',
            'type' => 'integer'
        ];

//         e.g. remove an existing event parameter
        $docs['paths']['/scheduleamap-api/activities']['get']['parameters'] = array_values(array_filter($docs['paths']['/scheduleamap-api/activities']['get']['parameters'], function ($param) {
            return $param['name'] !== 'event';
        }));

    // e.g. add the new definition for event
    $docs['paths']['/scheduleamap-api/activities']['get']['parameters'][] = $customDefinition;

        // Remove other restricted parameters that will generate errors.
        $docs['paths']['/scheduleamap-api/activities']['get']['parameters'] = array_values(array_filter($docs['paths']['/scheduleamap-api/activities']['get']['parameters'], function ($param) {
            return $param['name'] !== 'event[]';
        }));

        return $docs;
    }

    public function supportsNormalization($data, $format = null)
    {
        return $this->decorated->supportsNormalization($data, $format);
    }
}

Note:

  1. I also have autowire and autoconfig set to true in services.yaml.

  2. I added a custom Data Provider to require the event property filter be set in all api requests to the activity entity resource. The above customization does not require it to be set when doing direct fetch or url get requests.

Capo answered 18/10, 2019 at 18:13 Comment(0)
L
3

Voilà la version avec les attributs PHP8 :

        new GetCollection(
        uriTemplate: '/products',
        controller: GetProductsCollection::class,
        openapiContext: [
            'parameters' => [
                [
                    'name'        => 'page',
                    'in'          => 'query',
                    'description' => 'Collection page number',
                    'required'    => false,
                    'type'        => 'integer',
                    'default'     => 1,
                ],
                [
                    'name'        => 'rows',
                    'in'          => 'query',
                    'description' => 'Max rows',
                    'required'    => false,
                    'type'        => 'integer',
                    'default'     => 50,
                ],
            ],
        ],
        read: false
    ),
Liturgical answered 12/7, 2023 at 6:36 Comment(0)
B
2

Use openapi_context and reference OpenAPI documentation:

 *              "openapi_context" = {
 *                  "parameters" = {
 *                      {
 *                          "name" = "nameOfQueryParameter",
 *                          "in" = "query",
 *                          "description" = "Description goes here",
 *                          "schema" = {
 *                              "type" = "string"
 *                          }
 *                      }
 *                  }
 *               }
Balder answered 22/3, 2021 at 20:33 Comment(0)
C
1

If someone end up here by expecting an additionnal GET parameter in the URL, for example if your routing {id} parameter is not parsed by the swagger test tool, you should just change this in @pedrouan solution:

"in" = "path",
Covenanter answered 22/3, 2021 at 14:3 Comment(0)
T
1

You could use Filter like you could see in ApiPlatform documentation (https://api-platform.com/docs/core/filters/).

For example for my address model i just use doctrine bridge SearchFilter from ApiPlatform core

/**
 * Class Address
 *
 * @ORM\Entity
 */
#[ApiResource]
#[ApiFilter(SearchFilter::class, properties: ['street' => 'partial'])]
class Address
{
    /**
     * @var string|null $street
     *
     * @ORM\Column(type="string", nullable=true)
     */
    private ?string $street;

   // Do some fancy things here
}

It will result in

Result

Hope it could help someone !

Tensile answered 7/12, 2021 at 8:3 Comment(0)
R
0

If someone needs to do something similar, but using XML configuration:

<collectionOperation name="find_duplicated_items">
    <attribute name="method">GET</attribute>
    <attribute name="path">/items/find_duplicates</attribute>
    <attribute name="controller">App\Infrastructure\Http\Items\FindDuplicates</attribute>

     <attribute name="openapi_context">
        <attribute name="parameters">

            <attribute>
                <attribute name="name">someProperty</attribute>
                <attribute name="in">query</attribute>
                <attribute name="required">true</attribute>
                <attribute name="description">List foos and bars</attribute>
                <attribute name="schema">
                     <attribute name="type">array</attribute>
                     <attribute name="items">
                        <attribute name="type">integer</attribute>
                     </attribute>
                </attribute>
            </attribute>
        
            <attribute>
                <attribute name="name">ageDays</attribute>
                <attribute name="in">query</attribute>
                <attribute name="required">false</attribute>
                <attribute name="description">Max age in days</attribute>
                <attribute name="default">5</attribute>
                <attribute name="schema">
                    <attribute name="type">integer</attribute>
                </attribute>
            </attribute>
        </attribute>

    </attribute>

</collectionOperation>

Which gets you this:

resulting swagger UI

Release answered 26/10, 2020 at 11:38 Comment(3)
You should write where this content should be added.Impermeable
@Impermeable You mean the location of the configuration files? It's a bit outside the scope of the question, I think. That question would be something like "how can I can configure Api-Platform resources without using annotations?"Release
@Impermeable just respect the schema and add it wherever you want, in this case open api context expects 2d array of parameters just check the docs on the equivalent for resource to add this configHorsetail
S
0

You'd better create a custom filter to describe an additional parameter.

This will give you the desired openApi doc entry with the advantage that api-platform will also automatically apply the validation constraints you described in your filter class. Plus you can enrich the context with your sanitized (or not) value.

<?php

namespace App\Filter;

use ApiPlatform\Core\Serializer\Filter\FilterInterface;
use Symfony\Component\HttpFoundation\Request;

class MyParamFilter implements FilterInterface
{
    public const MYPARAM_FILTER_CONTEXT = 'myparam';

    public function getDescription(string $resourceClass): array
    {
        $doc = [
            'allowEmptyValue' => false,
            'example' => 'blabla',
        ];
        $schema = [
            'type' => 'string',
            'minLength' => 2,
            'maxLength' => 32,
        ];

        return [
            'myparam' => [
                'description' => 'Parameter description',
                'property' => null,
                'type' => 'string',
                'required' => true,
                'swagger' => array_merge(
                    $doc,
                    $schema
                ),
                'openapi' => array_merge(
                    $doc,
                    [
                        'schema' => $schema,
                    ]
                ),
            ],
        ];
    }

    public function apply(Request $request, bool $normalization, array $attributes, array &$context): void
    {
        $context[self::MYPARAM_FILTER_CONTEXT] = $request->query->get('myparam');
    }
}

This is not described in api-platform documentation but, nevertheless, here is the current documentation link:

https://api-platform.com/docs/core/filters/#creating-custom-filters.

Take care to implement the right interface ;)

Semitone answered 28/9, 2021 at 16:46 Comment(0)
H
0

Things slightly changed since ApiPlatform 3.3. Instead of openapi_context you should use parameters and QueryParameter:

        new GetCollection(
            uriTemplate: '/example/custom',
            controller: ExampleCustomAction::class,
            name: 'custom',
            parameters: [
                'param_name' => new QueryParameter(
                    schema: ['type' => 'string'],
                    description: 'Param description',
                    required: true,  
                ),
            ],
        )

Please note how type should be declared:

schema: ['type' => 'string']

thus it will be correctly passed to openapi.yaml|openapi.json would you need one.

BTW, with openapi_context you should use schema too, instead of directly using type.

Hong answered 29/7 at 0:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.