cnizzardini / cakephp-swagger-bake

Automatically generate OpenAPI, Swagger, and Redoc documentation from your existing CakePHP code.
http://cakephpswaggerbake.cnizz.com/
MIT License
60 stars 20 forks source link

OpenApiSchemaProperty - Set reference to another OpenApiSchema #558

Open marcdebus opened 2 months ago

marcdebus commented 2 months ago

Another question, or perhaps a suggestion for improvement.

Is it possible to set an OpenAPISchemaProperty as an object that references another OpenAPISchema like this?

namespace ApiPlugin\Model\DTO

#[OpenApiSchema(title: 'PersonDTO')]
class PersonDTO extends BaseDTO
{
    #[OpenApiSchemaProperty(name: 'id', type: 'integer', example: 1, description: 'The ID of person')]
    public $id;

    #[OpenApiSchemaProperty(name: 'name', type: 'string', example: 'Max', description: 'The firstname of person')]
    public $firstname;

    #[OpenApiSchemaProperty(name: 'name', type: 'string', example: 'Mustermann', description: 'The lastname of person')]
    public $lastname;

    #[OpenApiSchemaProperty(name: 'address', type: 'object', ref: '#/components/schemas/AddressDTO', description: 'Associated address for person')]
    public AddressDTO $address;

}

Additionally, how can I retrieve the DTOs as a schema from the ApiPlugin\Model\DTO namespace?

Below is my swagger_bake.php file.

return [
    'SwaggerBake' => [
        'prefix' => '/api/v1',
        'yml' => '/plugins/ApiPlugin/config/swagger.yml',
        'json' => '/webroot/swagger.json',
        'webPath' => '/swagger.json',
        'hotReload' => true,
        'jsonOptions' => JSON_PRETTY_PRINT,
        'requestAccepts' => [
            'application/json',
            'application/xml'
        ],
        'responseContentTypes' => [
            'application/json',
            'application/xml'
        ],
        'exceptionSchema' => 'Exception',
        'namespaces' => [
            'entities' => ['ApiPlugin\Model\DTO'],
            'tables' => []
        ]
    ]
];
cnizzardini commented 1 month ago

First: I'm not certain if thats possible. Does the documentation say that is and/or does it work when you try? Not being condescending, I just go months without looking at this code now :-/ If it doesn't work, it would be a neat feature to request but I have no idea when I could get around to it :-/

Second: DTOs have nothing to do with CakePHP entities, so I am not sure what you are trying to accomplish there. They are solely userland code that you create with no hard relation to Cake internals. So what are you actually trying to accomplish here?

marcdebus commented 1 month ago

Sorry, I don’t want to come across as rude. I had just hoped that there was already a solution for this. I think it’s not uncommon for APIs to also represent relationships, like in this case, between a person and an address.

The OpenAPI specification provides for this, and it works as well.

relation-schema
 "components": {
        "schemas": {
            "PersonDTO": {
                "title": "PersonDTO",
                "properties": {
                    "id": {
                        "example": 1,
                        "type": "integer",
                        "description": "The ID of person"
                    },
                    "firstname": {
                        "example": "Max",
                        "type": "string",
                        "description": "The name of person"
                    },
                    "lastname": {
                        "example": "Mustermann",
                        "type": "string",
                        "description": "The lastname of person"
                    },
                    "address": {
                        "type": "object",
                        "$ref": "#/components/schemas/AddressDTO",
                        "description": "Associated address"
                    }
                },
                "type": "object"
            },
            "AddressDTO": {
                "title": "AddressDTO",
                "properties": {
                    "id": {
                        "example": 1,
                        "type": "integer",
                        "description": "The ID of address"
                    },
                    "street": {
                        "example": "Street",
                        "type": "string",
                        "description": "The street of address"
                    },
                    "houseNumber": {
                        "example": 1,
                        "type": "integer",
                        "description": "The houseNumber of address"
                    }
                },
                "type": "object"
            },
 ...

I want to use DTOs to seperate the API from the database. I know that, unfortunately, CakePHP doesn't natively support this, and most examples rely on transferring entities directly. In my opinion, this isn’t a good practice, which is why I want to introduce DTOs. Currently, my API doesn’t implement this, and when there are changes to the database, I encounter issues in other applications that consume this API.

cnizzardini commented 1 month ago

Okay yeah that makes sense, but unfortunately, you can't accomplish this as-is as the library for better and worse follows the whole CakePHP MVC design pattern. I could use this functionality as well for the same reasons you describe.

OpenApiDto is strictly built into the schema of requests/responses, so they don't really show up in the /components/schema/ OpenAPI section which really limits you here. I think the library should support what you are ultimately trying to accomplish, but its a bit of work :-/. This library would need to support registering custom schemas and it does not today.

Once it did that, at a minimum, you could get to where you want to go with the use of event listeners. If you are interested in building this in I don't think its that hard...

Something would need to be added in here https://github.com/cnizzardini/cakephp-swagger-bake/blob/2.x/src/Lib/Swagger.php#L71 that generates the custom schema. Then the config https://github.com/cnizzardini/cakephp-swagger-bake/blob/2.x/src/Lib/Configuration.php would need to support registering those custom schemas (which you were hoping it did in your initial issue post). That would really just be giving it a list of namespaces to look at.

marcdebus commented 1 month ago

Hey, thank you for all that support.

I’ve now tried to address my requirements using Swagger-PHP, and there’s already an out-of-the-box solution available. However, this also means that a lot of manual annotations are required, which can be both a curse and a blessing. Your solution was quiet simple. :-) Thank you for that!

My first API endpoint already seems to work with DTOs, and the OpenAPI documentation is well-structured here with that relation between DTOs.

I think it’s best not to reinvent the wheel in this case.

best regards from germany

cnizzardini commented 1 month ago

If I were at the point where I had to annotate everything with Swagger-PHP I'd just maintain the OpenAPI YAML myself... but, do as you wish.