documentation for api

This commit is contained in:
Julien Fastré 2021-05-18 19:58:29 +02:00
parent 2f9c7c38b5
commit f3802e36b3

View File

@ -13,6 +13,9 @@ API
Chill provides a basic framework to build REST api.
Basic configuration
*******************
Configure a route
=================
@ -34,7 +37,7 @@ You can also:
* `How to create your custom normalizer <https://symfony.com/doc/current/serializer/custom_normalizer.html>`_
Auto-loading the routes
***********************
=======================
Ensure that those lines are present in your file `app/config/routing.yml`:
@ -47,14 +50,9 @@ Ensure that those lines are present in your file `app/config/routing.yml`:
Create your model
*****************
=================
Create your model on the usual way.
If you do not use a `custom normalizer <https://symfony.com/doc/current/serializer/custom_normalizer.html>`_, you must add `annotations to create serialization groups <https://symfony.com/doc/current/components/serializer.html#attributes-groups>`_. Those groups are, by defaults:
* :code:`read` for GET requests;
* :code:`write` for POST, PUT and PATCH requests
Create your model on the usual way:
.. code-block:: php
@ -62,7 +60,6 @@ If you do not use a `custom normalizer <https://symfony.com/doc/current/serializ
use Chill\PersonBundle\Entity\AccompanyingPeriod\OriginRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* @ORM\Entity(repositoryClass=OriginRepository::class)
@ -74,19 +71,16 @@ If you do not use a `custom normalizer <https://symfony.com/doc/current/serializ
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Groups({"read"})
*/
private $id;
/**
* @ORM\Column(type="json")
* @Groups({"read", "write"})
*/
private $label;
/**
* @ORM\Column(type="date_immutable", nullable=true)
* @Groups({"read", "write"})
*/
private $noActiveAfter;
@ -96,7 +90,7 @@ If you do not use a `custom normalizer <https://symfony.com/doc/current/serializ
Configure api
*************
=============
Configure the api using Yaml (see the full configuration: :ref:`api_full_configuration`):
@ -180,7 +174,7 @@ Configure the api using Yaml (see the full configuration: :ref:`api_full_configu
}
The :code:`_index` and :code:`_entity` action
=============================================
*********************************************
The :code:`_index` and :code:`_entity` action are default actions:
@ -198,7 +192,7 @@ Entity:
Path: :code:`/api/1.0/person/accompanying-period/origin/{id}.{_format}`
Role
====
****
By default, the key `base_role` is used to check ACL. Take care of creating the :code:`Voter` required to take that into account.
@ -224,50 +218,8 @@ You can also define a role for each method. In this case, this role is used for
GET: MY_ROLE_SEE
HEAD: MY ROLE_SEE
Edit an entity: PUT and PATCH requests
=======================================
By default, PUT and PATCH requests are not enabled. You may enable it into the configuration:
.. code-block:: yaml
# config/packages/chill_main.yaml
chill_main:
apis:
accompanying_period_origin:
base_path: '/api/1.0/person/bla/bla'
class: 'Chill\PersonBundle\Entity\Blah'
name: bla
actions:
_entity:
methods:
GET: true
HEAD: true
PUT: true
PATCH: true
roles:
GET: MY_ROLE_SEE
HEAD: MY ROLE_SEE
HEAD: MY ROLE_UPDATE
HEAD: MY ROLE_UPDATE
.. note::
PUT and PATCH requests are handled on a similar way. You may use both without difference, although updating a whole object should use a PUT request, and updating part of the object should use a PATCH request.
Those two requests allow natively to update all attributes which are present in :code:`write` group, and relation which are connected in ManyToOne relation (the entity under :code:`class` config must be associated with one entity).
The api will be able to load the associated entity with a simple given :code:`id` and :code:`type`, if
* if the class is an entity, managed by Doctrine;
* if a discriminator map is present;
* if the given data has two keys: the "discriminator map" (:code:`type`) and the id. Example: :code:`{ "type": "person", "id": 2205 }`;
* if the :code:`setter` is type-hinted.
Customize the controller
========================
************************
You can customize the controller by hooking into the default actions. Take care of extending :code:`Chill\MainBundle\CRUD\Controller\ApiController`.
@ -315,7 +267,7 @@ And set your controller in configuration:
HEAD: true
Create your own actions
=======================
***********************
You can add your own actions:
@ -412,8 +364,182 @@ Then, create the corresponding action into your controller:
}
}
Managing association
********************
ManyToOne association
=====================
In ManyToOne association, you can add associated entities using the :code:`PATCH` request. By default, the serializer deserialize entities only with their id and discriminator type, if any.
Example:
.. code-block:: bash
curl -X 'PATCH' \
'http://localhost:8001/api/1.0/person/accompanying-course/2668.json' \
-H 'accept: */*' \
-H 'Content-Type: application/json' \
# see the data sent to the server: \
-d '{
"type": "accompanying_period",
"id": 2668,
"origin": { "id": 11 }
}'
ManyToMany associations
=======================
In OneToMany association, you can easily create route for adding and removing entities, using :code:`POST` and :code:`DELETE` requests.
Prepare your entity, creating the methods :code:`addYourEntity` and :code:`removeYourEntity`:
.. code-block:: php
namespace Chill\PersonBundle\Entity;
use Chill\MainBundle\Entity\Scope;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
/**
* AccompanyingPeriod Class
*
* @ORM\Entity
* @ORM\Table(name="chill_person_accompanying_period")
* @DiscriminatorMap(typeProperty="type", mapping={
* "accompanying_period"=AccompanyingPeriod::class
* })
*/
class AccompanyingPeriod
{
/**
* @var Collection
* @ORM\ManyToMany(
* targetEntity=Scope::class,
* cascade={}
* )
* @Groups({"read"})
*/
private $scopes;
public function addScope(Scope $scope): self
{
$this->scopes[] = $scope;
return $this;
}
public function removeScope(Scope $scope): void
{
$this->scopes->removeElement($scope);
}
Create your route into the configuration:
.. code-block:: yaml
chill_main:
apis:
-
class: Chill\PersonBundle\Entity\AccompanyingPeriod
name: accompanying_course
base_path: /api/1.0/person/accompanying-course
controller: Chill\PersonBundle\Controller\AccompanyingCourseApiController
actions:
scope:
methods:
POST: true
DELETE: true
GET: false
HEAD: false
PUT: false
PATCH: false
roles:
POST: CHILL_PERSON_ACCOMPANYING_PERIOD_SEE
DELETE: CHILL_PERSON_ACCOMPANYING_PERIOD_SEE
GET: null
HEAD: null
PUT: null
PATCH: null
controller_action: null
path: null
single-collection: single
This will create a new route, which will accept two methods: DELETE and POST:
.. code-block:: raw
+--------------+---------------------------------------------------------------------------------------+
| Property | Value |
+--------------+---------------------------------------------------------------------------------------+
| Route Name | chill_api_single_accompanying_course_scope |
| Path | /api/1.0/person/accompanying-course/{id}/scope.{_format} |
| Path Regex | {^/api/1\.0/person/accompanying\-course/(?P<id>[^/]++)/scope\.(?P<_format>[^/]++)$}sD |
| Host | ANY |
| Host Regex | |
| Scheme | ANY |
| Method | POST|DELETE |
| Requirements | {id}: \d+ |
| Class | Symfony\Component\Routing\Route |
| Defaults | _controller: csapi_accompanying_course_controller:scopeApi |
| Options | compiler_class: Symfony\Component\Routing\RouteCompiler |
+--------------+---------------------------------------------------------------------------------------+
Then, create the controller action. Call the method:
.. code-block:: php
namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Chill\MainBundle\Entity\Scope;
class MyController extends ApiController
{
public function scopeApi($id, Request $request, string $_format): Response
{
return $this->addRemoveSomething('scope', $id, $request, $_format, 'scope', Scope::class, [ 'groups' => [ 'read' ] ]);
}
}
This will allow to add a scope by his id, and delete them.
Curl requests:
.. code-block:: bash
# add a scope with id 5
curl -X 'POST' \
'http://localhost:8001/api/1.0/person/accompanying-course/2868/scope.json' \
-H 'accept: */*' \
-H 'Content-Type: application/json' \
-d '{
"type": "scope",
"id": 5
}'
# remove a scope with id 5
curl -X 'DELETE' \
'http://localhost:8001/api/1.0/person/accompanying-course/2868/scope.json' \
-H 'accept: */*' \
-H 'Content-Type: application/json' \
-d '{
"id": 5,
"type": "scope"
}'
Serialization for collection
============================
****************************
A specific model has been defined for returning collection:
@ -432,8 +558,9 @@ A specific model has been defined for returning collection:
}
}
Where this is relevant, this model should be re-used in custom controller actions.
This can be achieved quickly by assembling results into a :code:`Chill\MainBundle\Serializer\Model\Collection`. The pagination information is given by using :code:`Paginator` (see :ref:`Pagination <pagination-ref>`).
In custom actions, this can be achieved quickly by assembling results into a :code:`Chill\MainBundle\Serializer\Model\Collection`. The pagination information is given by using :code:`Paginator` (see :ref:`Pagination <pagination-ref>`).
.. code-block:: php
@ -454,7 +581,7 @@ This can be achieved quickly by assembling results into a :code:`Chill\MainBundl
.. _api_full_configuration:
Full configuration example
==========================
**************************
.. code-block:: yaml
@ -501,39 +628,4 @@ Full configuration example
single-collection: single
base_role: null
Maintaining an OpenApi
======================
.. note::
This is experimental and may change. Keep reading this part.
Accessing swagger
-----------------
The swagger UI is accessible through `<http://localhost:8001/_dev/swagger>`_
This is possible only in development mode.
You must be authenticated with a valid user to access it.
Maintaining specs
-----------------
Each bundle should have an `open api 3.0 spec file <https://swagger.io/docs/specification/about/>`_ at the bundle's root, and name :code:`chill.api.specs.yaml`.
.. warning::
Update the command :code:`specs-build` into `package.json` when adding a new api file.
The specs may be compiled from the different bundles filed, and validated using docker node:
.. code-block:: bash
./docker-node.sh
# build the openapi
yarn specs-build
# validate
yarn specs-validate