Nelmioapidocbundle: Body Parameter as Array instead of Object

Created on 26 Feb 2018  路  17Comments  路  Source: nelmio/NelmioApiDocBundle

I'm using nelmio/api-doc-bundle ^3.0
Giving the following SWG\Parameter / SWG\Schema construct I expected that I will have an Array as example value, instead of that I get the following:

[
  {
    "name": "string",
    "mac": "string"
  }
]

What I expected:

{
  "name": "string",
  "mac": "string"
}

What I used to build the Documentation:

     *     @SWG\Parameter(
     *         name="body",
     *         in="body",
     *         @SWG\Schema(
     *             type="array",
     *             @Model(type=MySymfonyBundle\JsonInputs\PcComputerAdd::class)
     *         )
     *     ),

Swapping the type="array" to type="object" just gives an empty array as Documentation Output.
This happens on POST, PUT, PATCH operation (actually everything where you are transmitting body).

Stripped down contents of MySymfonyBundle/JsonInputs/PcComputerAdd.php:

namespace MySymfonyBundle\JsonInputs;
use JMS\Serializer\Annotation\Type;
use JMS\Serializer\Annotation\SerializedName;
class PcComputerAdd {
    /**
    * Name of the Machine
    * @Type("string")
    * @SerializedName("name")
    */
    private $name;
    /**
    * MacAdress of the Machine
    * @Type("string")
    * @SerializedName("mac")
    */
    private $mac;
}

What did I do wrong or did I hit a bug?
Actually the docs are a bit messy and hard to find :-(

Most helpful comment

@dbu if I remember well the prefix can be removed at runtime so we can't trully whether it's here.

https://github.com/nelmio/NelmioApiDocBundle/pull/1261 will allow:
```php
/**
* @SWG\Parameter(
* name="user",
* in="body",
* @SWG\Schema(type="object",
* @SWG\Property(property="user", ref=@Model(type=UserType::class))
* )
* )
*/

All 17 comments

You have to remove the @Schema annotation around @Model, see https://symfony.com/doc/current/bundles/NelmioApiDocBundle/faq.html.

sorry but nope.

User Notice: Field "schema" is required when @SWG\Parameter(name="body",in="body") is in "body" in \MyController->computerAdd() in /var/www/myProject/src/MySymfonyBundle/Controller/MyController.php

if I add an empty field called schema:

Type error: Argument 1 passed to EXSyst\Component\Swagger\Schema::mergeDescription() must be of the type array, string given, called in /var/www/myProject/vendor/exsyst/swagger/src/Schema.php on line 86

Well then it seems to be a bug, I guess the @Model annotation is somehow not passed to @Parameter. I don't see where this could come from... Could you submit a PR with a failing test case please?

Interesting stuff. Was about to write the test... forked... cloned... tried (forgot to add type="array", )... works... WTF.

Upgraded to 3.1, removed type="array", and it worked inside the real world project too.
I'm not sure if there's a testcase needed anymore?
Adding type="array", breaks it. Not sure if intended but it does.
Oh and surprise the exception is the same within the constructed testcase. So maybe a better exception message would be great in this case.
Field "schema" is required when @SWG\Parameter(name="body",in="body") is in "body" in \JMSController->simpleAction() in /root/sources/NelmioApiDocBundle/Tests/Functional/Controller/JMSController.php

The diff for the defect simpleAction():

diff --git a/Tests/Functional/Controller/JMSController.php b/Tests/Functional/Controller/JMSController.php
index 468ba76..4295d54 100644
--- a/Tests/Functional/Controller/JMSController.php
+++ b/Tests/Functional/Controller/JMSController.php
@@ -12,6 +12,7 @@
 namespace Nelmio\ApiDocBundle\Tests\Functional\Controller;

 use Nelmio\ApiDocBundle\Annotation\Model;
+use Nelmio\ApiDocBundle\Annotation\Operation;
 use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSComplex;
 use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSUser;
 use Nelmio\ApiDocBundle\Tests\Functional\Entity\VirtualProperty;
@@ -55,4 +56,23 @@ class JMSController
     public function complexAction()
     {
     }
+
+    /**
+     * @Operation(
+     *     @SWG\Parameter(
+     *         name="body",
+     *         in="body",
+     *         type="array",
+     *         @Model(type=JMSComplex::class)
+     *     ),
+     *     @SWG\Response(
+     *         response=200,
+     *         description="Success"
+     *     )
+     * )
+     * @Route("/api/jms_complex_post", methods={"POST"})
+     */
+    public function simpleAction()
+    {
+    }
 }

First since you're documenting an object you shouldn't use type="array".

Adding type="array", breaks it. Not sure if intended but it does.

That's intended, body parameter only accepts an object in input.
The exception is not very clear because it isn't designed for this bundle (it is thrown by zircote/swagger-php).
To make it clearer, we could add a check here:

if ('body' === $annotation->in) {
    throw new \LogicException(sprintf('Array type is not accepted  when using a body @Parameter in %s. Use an object type instead.', $annotation->_context));
}

Hi, I have another issue related to Parameter Object. I can't find a way to wrap parameter inside an object key (form type block prefix). It renders only parameter in form type object. As a result, it can't work with form type without adjust the parameter to do the request from apidoc.

For Ex,
Controller

    /**
     * Create a new user.
     *
     * @SWG\Tag(name="/api/v1/user")
     * @SWG\Response(
     *     response=200,
     *     description="Create a new user",
     *     @Model(type=User::class)
     * )
     * @SWG\Parameter(
     *     name="user",
     *     in="body",
     *     @Model(type=UserType::class)
     * )
     *
     * @View(
     *     serializerGroups={"userDetail"}
     * )
     *
     * @Post("/user")
     *
     * @param Request $request
     *
     * @return User|FormInterface
     */

APIDOC

{
  "email": "string",
  "password": "string",
  "status": "string",
  "phone": "string"
}

What I expected is

{
  "user":  {
    "email": "string",
    "password": "string",
    "status": "string",
    "phone": "string"
  }
}

why would you expect a user key, where would that key come from? if you return one user, it should be one object with the fields of the user. if you want a user key, you would need some sort of UserResponse object with a field $user. but unless you have other things to return than the user itself, the API design i usually see would just return the object directly, as in your first result

Hi dbu, the 'user' key I mentioned is api doc parameter which is generated by swagger annotation. In the nelmioapidoc version 2.0, it was took from FormType::getBlockPrefix(). By using an Symfony FormType original (no modification), I need that key in request parameters to conveniently do request from api doc.

i think the body parameter "name" is purely decorative, all of body is the one body parameter, at least in version 3. i don't know version 2 enough to tell if that is a BC break. maybe @GuilhemN can help us here.

I believe in 2.0 it was added but I think it's more usual to remove it when using forms in an API (and it's harder to remove it than to readd it).

Do you mean it is better to avoid parameter object key when using form in API? At the moment, I'm addressing the problem by returning FormType::getBlockPrefix() to null. However, it will be a deal when I sometime need to POST multiple object in one api endpoint or combine form type objects.

if this is a feature of the symfony form component, ideally it would work as expected with the swagger doc generated by this bundle. @tuancode i think the relevant class is https://github.com/nelmio/NelmioApiDocBundle/blob/master/ModelDescriber/FormModelDescriber.php - can you try to see if you find a place where the prefix could be determined and send a pull request to fix the describer?

@dbu if I remember well the prefix can be removed at runtime so we can't trully whether it's here.

https://github.com/nelmio/NelmioApiDocBundle/pull/1261 will allow:
```php
/**
* @SWG\Parameter(
* name="user",
* in="body",
* @SWG\Schema(type="object",
* @SWG\Property(property="user", ref=@Model(type=UserType::class))
* )
* )
*/

now that #1261 is merged, should we close this?

Indeed the original issue is fixed and for forms, #1261 always to wrap them manually in an object if wanted.

the answer to your question is:

    *     @SWG\Parameter(
     *         name="body",
     *         in="body",
     *         @SWG\Schema(ref=@Model(type=MySymfonyBundle\JsonInputs\PcComputerAdd::class))
     *     ),
Was this page helpful?
0 / 5 - 0 ratings

Related issues

DavidGarciaCat picture DavidGarciaCat  路  4Comments

NicolasGuilloux picture NicolasGuilloux  路  4Comments

abidichrak picture abidichrak  路  5Comments

alxfv picture alxfv  路  5Comments

kojidev picture kojidev  路  6Comments