Serialize nested properties as form-data with swagger-php - php

This is how Encoding Object Example is done in OpenApi
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md
requestBody:
content:
multipart/mixed:
schema:
type: object
properties:
id:
# default is text/plain
type: string
format: uuid
address:
# default is application/json
type: object
properties: {}
historyMetadata:
# need to declare XML format!
description: metadata in XML format
type: object
properties: {}
profileImage:
# default is application/octet-stream, need to declare an image type only!
type: string
format: binary
encoding:
historyMetadata:
# require XML Content-Type in utf-8 encoding
contentType: application/xml; charset=utf-8
profileImage:
# only accept png/jpeg
contentType: image/png, image/jpeg
headers:
X-Rate-Limit-Limit:
description: The number of allowed requests in the current period
schema:
type: integer
I'm trying to achive the same thing but with swagger-php.
What I don't know is how to pass encodings in #OA\MediaType to encode the test property as multipart/form-data because by default is encoded as application/json
EX:
* #OA\Post(
* path="/admin/test",
* summary="Create new Test",
* description="Will attempt to create a new Test",
* tags={"Admin Test"},
* #OA\RequestBody(
* #OA\MediaType(
* mediaType="multipart/form-data",
* encoding={}
* #OA\Schema(
* type="object",
* #OA\Property(
* property="test",
* type="object",
* description="test"
* ref="#/components/schemas/MyTestSchema"
* )
* )
* )
They have some examples here:
https://github.com/zircote/swagger-php/tree/master/Examples
but I didn't found any example regarding encoding
Inside here the field is defined
https://github.com/zircote/swagger-php/blob/master/src/Annotations/MediaType.php
/**
* A map between a property name and its encoding information.
* The key, being the property name, must exist in the schema as a property.
* The encoding object shall only apply to requestBody objects when the media type is multipart or application/x-www-form-urlencoded.
*/
public $encoding = UNDEFINED;
I've tried encoding={"recommended"={"contentType"="multipart/form-data"}} but it's useless.

I think the only solution is putting all the fields:
* #OA\Post(
* path="/admin/test",
* summary="Create new Test",
* description="Will attempt to create a new Test",
* tags={"Admin Test"},
* #OA\RequestBody(
* #OA\MediaType(
* mediaType="multipart/form-data",
* #OA\Schema(
* #OA\Property(
* property="test[name]",
* type="string",
* description="name"
* ),
* #OA\Property(
* property="test[desc]",
* type="string",
* description="description"
* )
* )
* )
* )

Related

How to set #SWG\Info annotation for only area in Symfony using NelmioApiDocBundle

/**
* #SWG\Swagger(
* #SWG\Info(
* title="My first swagger documented API",
* version="1.0.0"
* )
* )
*/
I tried to write this block in:
my base controller - src/MyProject/ApiBundle/Controller/SiteApi/SiteApiBaseController.php
my bundle file - src/MyProject/ApiBundle/MyProjectApiBundle.php
Generated JSON contains empty "info": {"title": ""}
Symfony 3.4, NelmioApiDocBundle 3.9.1(including zircote/swagger-php 2.0.9)
• For controller try:
/**
use FOS\RestBundle\Controller\Annotations as Rest;
use Swagger\Annotations as SWG;
...
* Add Tva.
* #Rest\Post("/tva/add")
* #SWG\Tag(name="TVA Management")
...
...
* #SWG\Response(response=201, description="Tva created successfully.")
...
...
*/
=> You will get something like this:
• For api/doc page Title, Description & Version, try to edit this file:
~Your_Project_Path\config\packages\nelmio_api_doc.yaml

Internal error due to serialization with paramconverter

I have an API which takes multiple input values. One of them is a date.
When the date is sent, all is fine.
But, when the user isn't sending a date, I have an error 500 with this error message:
Invalid datetime "Some invalid data", expected format Y-m-d\TH:i:sP.
So I wanted to check if the data sent had the format required.
But I don't understand how things are working, hope you can help.
This is what I have
/**
*
* #Rest\Post(
* path = "/signup",
* name = "api_users_add"
* )
* #Rest\View(StatusCode=201, serializerGroups={"user_detail"})
* #ParamConverter(
* "user",
* converter="fos_rest.request_body",
* options={"deserializationContent"={"groups"={"Deserialize"}}},
* )
* #ParamConverter(
* "profile",
* converter="fos_rest.request_body",
* options={"deserializationContent"={"groups"={"Deserialize"}}},
* )
*/
public function postUserAction(Request $request, User $user, Profile $profile)
{
if (!preg_match("^[0-9]{4}-[0-1][0-9]-[0-3][0-9]$",$profile->getBirth())){
return new JsonResponse([
'success' => false,
'message' => "Date d'anniversaire au mauvais format"
]);
}
}
But in fact, I never go into this condition, the error 500 is triggered before.
I guess this has something to do with the #ParamConverter from Profile who can't "deserialize" when birth is not a DateTime.
Thing is, I would like to check what is sent (as I did into my condition) in order to avoid this internal error. But I can't find where this is handled on my code.
Thanks for the help.
Considering this : symfony doc for Param converter fos rest bundle
What I am looking for is to find where the validationErrors are specified.

Symfony validation on multiple file upload

I have a form containing a FileType field. I've set the multiple option to true so the user can upload multiple files at the same time.
$builder->add('myFile', FileType::class, [
'label' => 'upload file',
'multiple' => true,
])
Here is the corresponding property in the Entity connected to this form:
/**
* #Assert\NotBlank()
* #Assert\File(mimeTypes = {"application/pdf", "application/x-pdf", "image/jpeg", "image/png"})
* #ORM\Column(type="array")
*/
private $myFile;
When I submit the form I get the error:
UnexpectedTypeException in FileValidator.php line 168:
Expected argument of type "string", "array" given
I added curly braces in front of File assert so it looks like this:
* #Assert\File{}(mimeTypes = {"application/pdf", "application/x-pdf", "image/jpeg", "image/png"})
Now it doesn't complain when submitting the form. but the file type validation is also not checked.
Any idea how to make the file type working for multiple selected files?
Since you're validating array of File, you need to apply All validator, which will apply inner validators on each element of the array.
Try with something like:
/**
* #Assert\All({
* #Assert\NotBlank(),
* #Assert\File(mimeTypes = {"application/pdf", "application/x-pdf", "image/jpeg", "image/png"})
* })
* #ORM\Column(type="array")
*/
private $myFile;

how to Multiple HttpHeader(with same name) in ExpressJS

My Php Code can be send two headers with same name, in php second parameter is for replace or override. but in ExpressJS res.header is not like php code
PHP Code:
header("Link: <http://$prefetch_next_img>; rel=prefetch",false);
header("Link: <http://$prefetch_next_img2>; rel=prefetch",false);
http://us2.php.net/manual/en/function.header.php
ExpressJS(NodeJS) :
res.header('Link','Fake Value');
res.header('Link','Only Send it'); // previous header replaced
Ultimately, you'll need to pass an array to set multiple values for the same header
res.header('Set-Cookie', ['foo', 'bar']);
Looking at response library for express we can see the following examples:
/**
* Set header `field` to `val`, or pass
* an object of header fields.
*
* Examples:
*
* res.set('Foo', ['bar', 'baz']);
* res.set('Accept', 'application/json');
* res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
*
* Aliased as `res.header()`.
*
* #param {String|Object} field
* #param {String|Array} val
* #return {ServerResponse} for chaining
* #public
*/
A deeper dive into the code shows us that this is really just a proxy for node's response.setHeader(), which follows the same format
Sets a single header value for implicit headers. If this header
already exists in the to-be-sent headers, its value will be replaced.
Use an array of strings here if you need to send multiple headers with
the same name.
response.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']);

Symfony routing - locale as subdomain with fallback to default

I'm trying to set up routing system with subdomain representing current locale. Routing is set via #Routing annotation and looks like this:
/**
* #Route(
* "/",
* name="homepage",
* host="{locale}.{domain}",
* defaults={"locale" = "en", "domain" = "%domain%"},
* requirements={"locale" = "en|de|fr", "domain" = "%domain%"}
* )
*/
Works well for URL's like en.somedomain.com or de.somedomain.com, but fails to find correct route for somedomain.com, without locale.
I understand that because of the host parameter, that is set to represent exact locale.domain pattern, but I can't find way to tell Symfony routing system that there could be additional, default host.
Searched all around for this, but found nothing particular. Would appreciate any help!
UPDATE
There is actually a way to do it, by adding another #Route in annotation, without host parameter:
/**
* #Route(
* "/",
* name="homepage_default",
* defaults={"locale" = "en"}
* )
*/
but thats looks a bit dirty, and I'm not using %domain% parameter there, which is important for me - say, if I would need another subdomain for mobile version.
Well, looks like triple annotation routing to handle locale + subdomain is the only choice for now.
Studying documentation (for example, this article) shows that Symfony developers encourage us to do it that way, which, to me, is not that nice. However, here's the solution...
/**
* #Method({"GET"})
* #Route(
* "/",
* name="homepage",
* host="{mobile}.{_locale}.{domain}",
* defaults={"mobile" = "moblie", "locale" = "%locale%", "domain" = "%domain%"},
* requirements={"mobile" = "mobile|m", "_locale" = "%locale%|de|fr", "domain" = "%domain%"}
* )
* #Route(
* "/",
* name="homepage",
* host="{_locale}.{domain}",
* defaults={"_locale" = "%locale%", "domain" = "%domain%"},
* requirements={"_locale" = "%locale%|de|fr", "domain" = "%domain%"}
* )
* #Route(
* "/",
* name="homepage_default",
* defaults={"_locale" = "%locale%"}
* )
*/
Notice that order is important, starting from subdomains, going down to default. As this looks ugly with #Route annotation, I've decided to re-write this in YAML as well:
homepage_locale_mobile:
path: /
host: "{mobile}.{_locale}.{domain}"
defaults: { mobile: "mobile", _locale: "%locale%", domain: "%domain%" }
requirements:
mobile: "mobile|m"
_locale: "%locale%|de|fr",
domain: "%domain%"
homepage_locale:
path: /
host: "{_locale}.{domain}"
defaults: { _locale: "%locale%", domain: "%domain%" }
requirements:
_locale: "%locale%|de|fr",
domain: "%domain%"
homepage:
path: /
defaults: { _locale: "%locale%" }
Ordered as well. Maybe someone will come across and use it.
I've just had a similar problem with defaults, though it was a route parameter and not locale.
The solution was there to replace the = signs with : as it should be that and the compiler somehow doesn't complains about it.
So try this one out:
/**
* #Route(
* "/",
* name="homepage",
* host="{locale}.{domain}",
* defaults={"locale" : "en", "domain" : "%domain%"},
* requirements={"locale" : "en|de|fr", "domain" : "%domain%"}
* )
*/

Categories