Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #53: Created OpenAPI documentation #54

Merged
merged 2 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions docs/book/v5/openapi/generate-documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Generating the documentation file

> Make sure that in `src/App/src/OpenAPI.php`, on the line with `#[OA\Server` the value of `url` is set to the of URL of
> your instance of **Dotkernel API**.

Using your terminal, move to the root directory of your project.

Dotkernel API stores the OpenAPI attributes in the `src` directory, so that's the path we will use for generating the
static documentation file.

## Methods of generating documentation file

### Without saving it to a file

```shell
./vendor/bin/openapi ./src
```

This will output the generated content to the terminal.

### Place it in a custom location

```shell
./vendor/bin/openapi ./src --output public/openapi.yaml
```

This will place the generated file `openapi.yaml` in the `public` directory.

### Specify OpenAPI version

Supported OpenAPI versions are `3.0.0` and `3.1.0`, `3.0.0` being the default version.

The below command will specify both the output location and the OpenAPI version:

```shell
./vendor/bin/openapi ./src --version 3.1.0
```

### Specify output file format

Supported file formats are `yaml` and `json`, `yaml` being the default format.

The below command will specify the output location and `zircote/swagger-php` will determine the file format:

```shell
./vendor/bin/openapi ./src --output public/openapi.json
```

Or be specific about the format by appending the `--format` argument:

```shell
./vendor/bin/openapi ./src --output public/openapi.json --format json
```

These will place the generated file `openapi.json` in the `public` directory.
13 changes: 13 additions & 0 deletions docs/book/v5/openapi/getting-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Getting help

- consult the OpenAPI [specs](https://spec.openapis.org/oas/latest.html) for a complete
reference of the presented objects
- see more examples of OpenAPI object representations in `zircote/swagger-php`'s
[GitHub repository](https://github.com/zircote/swagger-php/tree/master/Examples)
- consult `zircote/swagger-php`'s
[online documentation](http://zircote.github.io/swagger-php/guide/generating-openapi-documents.html) or run the
following command to see their help page:

```shell
./vendor/bin/openapi --help
```
237 changes: 237 additions & 0 deletions docs/book/v5/openapi/initialized-components.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
# Initialized OpenAPI components

Below you will find details on some prepopulated OpenAPI components we added to Dotkernel API.

## OA\Info

Defined in `src/App/src/OpenAPI.php`, this object provides general info about the API:

- `version`: API version (example: `1.0.0`)
- `title`: title shown in the UI (example: `Dotkernel API`)

For more info, see [this page](https://spec.openapis.org/oas/latest.html#info-object).

## OA\Server

Defined in `src/App/src/OpenAPI.php`, this object provides API server entries:

- `url`: API server URL (example: `https://api.example.com` - use no trailing slash!)
- `description`: describes the purpose of the server (example: `Dev`, `Staging`, `Production` or even `Auth` if you use
a separate authentication server)

You can have multiple `Server` definitions, one for each of your Dotkernel API instances.

For more info, see [this page](https://spec.openapis.org/oas/latest.html#server-object).

## OA\SecurityScheme

Defined in `src/App/src/OpenAPI.php`, you will find an object for the `AuthToken` security header:

- `securityScheme`: the name of the security scheme - you will provide this to indicate that an endpoint is protected
- `type`: whether it's an API key, an authorization header etc
- `in`: indicates where the scheme is applied (`query`/`header`/`cookie`)
- `bearerFormat`: a hint to the client to identify how the bearer token is formatted
- `scheme`: the name of the authorization scheme to be used

And another object for the `ErrorReportingToken` security token:

- `securityScheme`: the name of the security scheme - you will provide this to indicate that an endpoint is protected
- `type`: whether it's an API key, an authorization header etc
- `in`: indicates where the scheme is applied (`query`/`header`/`cookie`)
- `name`: the name of the header

For more info, see [this page](https://spec.openapis.org/oas/latest.html#security-scheme-object).

## OA\ExternalDocumentation

Defined in `src/App/src/OpenAPI.php`, in this object we provide the following details:

- `description`: describes the purpose of the document
- `url`: external documentation URL

For more info, see [this page](https://spec.openapis.org/oas/latest.html#external-documentation-object).

## OA\Schema

Schemas are OpenAPI objects describing an object or collection of objects existing in your project.

### Schemas describing objects

In order to describe an object (entity) you will need to transform in into a schema.

Object:

```php
<?php

declare(strict_types=1);

namespace Api\User\Entity;

use Api\App\Entity\AbstractEntity;
use Api\App\Entity\RoleInterface;
use Api\App\Entity\TimestampsTrait;
use Doctrine\ORM\Mapping as ORM;

class UserRole extends AbstractEntity implements RoleInterface
{
use TimestampsTrait;

#[ORM\Column(name: "name", type: "string", length: 20, unique: true)]
protected ?string $name = null;

// methods
}
```

Schema:

```php
<?php

declare(strict_types=1);

namespace Api\User;

use Api\User\Entity\UserRole;
use OpenApi\Attributes as OA;

...

/**
* @see UserRole
*/
#[OA\Schema(
schema: 'UserRole',
properties: [
new OA\Property(property: 'uuid', type: 'string', example: '1234abcd-abcd-4321-12ab-123456abcdef'),
new OA\Property(property: 'name', type: 'string', example: UserRole::ROLE_USER),
new OA\Property(
property: '_links',
properties: [
new OA\Property(
property: 'self',
properties: [
new OA\Property(
property: 'href',
type: 'string',
example: 'https://example.com/user/role/1234abcd-abcd-4321-12ab-123456abcdef',
),
],
type: 'object',
),
],
type: 'object',
),
],
type: 'object',
)]
```

Then, when generating the documentation file, `OpenAPI` will transform it into the specified format (**json**/**yaml**).

```yaml
UserRole:
properties:
uuid:
type: string
example: 1234abcd-abcd-4321-12ab-123456abcdef
name:
type: string
example: user
_links:
properties:
self:
properties:
href:
type: string
example: 'https://example.com/user/role/1234abcd-abcd-4321-12ab-123456abcdef'
type: object
type: object
type: object
```

### Schemas describing collections of objects

Collections of objects are just as easy to describe in `OpenAPI` as they are in PHP.

PHP collection:

```php
<?php

declare(strict_types=1);

namespace Api\User\Collection;

use Api\App\Collection\ResourceCollection;

class UserRoleCollection extends ResourceCollection
{
}
```

Schema:

```php
#[OA\Schema(
schema: 'UserRoleCollection',
properties: [
new OA\Property(
property: '_embedded',
properties: [
new OA\Property(
property: 'roles',
type: 'array',
items: new OA\Items(
ref: '#/components/schemas/UserRole',
),
),
],
type: 'object',
),
],
type: 'object',
allOf: [
new OA\Schema(ref: '#/components/schemas/Collection'),
],
)]
```

Using `ref: '#/components/schemas/UserRole',` in our code, we instruct `OpenAPI` to grab the existing schema `UserRole`
(not the entity, but the schema) that we just described earlier. This way we do not need to repeat code by describing
again the same object and any future modifications will happen in only one place.

Then, when generating the documentation file, `OpenAPI` will transform it into the specified format (**json**/**yaml**).

```php
UserRoleCollection:
type: object
allOf:
-
$ref: '#/components/schemas/Collection'
-
properties:
_embedded:
properties:
roles:
type: array
items: { $ref: '#/components/schemas/UserRole' }
type: object
type: object
```

> Make sure that in `src/App/src/OpenAPI.php`, on the line with `#[OA\Server` the value of `url` is set to the of URL of
> your instance of **Dotkernel API**.
>
> You can add multiple servers (for staging, production etc) by duplicating the existing one.

For more info, see [this page](https://spec.openapis.org/oas/latest.html#schema).

### Common schemas

We provided some schemas that are reusable across the entire project. They are defined in `src/App/src/OpenAPI.php`:

- `#/components/schemas/Collection`: provides the default **HAL** structure to all the collections extending it
- `#/components/schemas/ErrorMessage`: describes an operation that resulted in an error - may contain multiple messages
- `#/components/schemas/InfoMessage`: describes an operation that completed successfully - may contain multiple messages
8 changes: 8 additions & 0 deletions docs/book/v5/openapi/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# OpenAPI documentation

In order to provide an interactive documentation, Dotkernel API implemented
[zircote/swagger-php](https://github.com/zircote/swagger-php).

Using this library, developers are able to automatically generate documentation files that later can be used to provide
a comprehensive overview of the available endpoints, all the details on the requests that it can receive and the
responses these can return.
82 changes: 82 additions & 0 deletions docs/book/v5/openapi/render-documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Rendering the documentation file

At this step, you only have a static documentation file. You will need an interface that can render it so that you will
be able to interact with your Dotkernel API.

In order to do this, we recommend using either of:

- [swagger-api/swagger-ui](https://github.com/swagger-api/swagger-ui)
- [Redocly/redoc](https://github.com/Redocly/redoc)

## Using Swagger UI

Navigate to the `public` directory of your instance of Dotkernel API and create an HTML (you can call it `swagger.html`,
the name is up to you) and place the following HTML content in it:

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Dotkernel API Documentation" />
<title>Dotkernel API Documentation</title>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/swagger-ui.css" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/[email protected]/swagger-ui-bundle.js" crossorigin></script>
<script>
window.onload = () => {
window.ui = SwaggerUIBundle({url: 'PATH_TO_YOUR_OPENAPI_FILE', dom_id: '#swagger-ui'});
};
</script>
</body>
</html>
```

Make sure that you replace `PATH_TO_YOUR_OPENAPI_FILE` with the relative path to your documentation file
(openapi.json/openapi.yaml). The line should look similar to this:

```js
window.ui = SwaggerUIBundle({url: './openapi.yaml', dom_id: '#swagger-ui'});
```

Using your browser, open a new tab and type in the URL of your instance of Dotkernel API and append `/swagger.html` to
it. You should see the Redoc interface with your documentation file loaded in it. From here, you can inspect each
endpoint, see it's URL, check if it needs authentication, the request payload (if any) and the possible response(s).

## Using Redoc

Navigate to the `public` directory of your instance of Dotkernel API and create an HTML (you can call it `redoc.html`,
the name is up to you) and place the following HTML content in it:

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Dotkernel API Documentation" />
<title>Dotkernel API Documentation</title>
<script src="https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.standalone.js"></script>
</head>
<body>
<div id="redoc-container"></div>
<script>
Redoc.init('PATH_TO_YOUR_OPENAPI_FILE', {}, document.getElementById('redoc-container'));
</script>
</body>
</html>
```

Make sure that you replace `PATH_TO_YOUR_OPENAPI_FILE` with the relative path to your documentation file
(openapi.json/openapi.yaml). The line should look similar to this:

```js
Redoc.init('./openapi.yaml', {}, document.getElementById('redoc-container'));
```

Using your browser, open a new tab and type in the URL of your instance of Dotkernel API and append `/redoc.html` to it.
You should see the Redoc interface with your documentation file loaded in it. From here, you can inspect each endpoint,
see it's URL, check if it needs authentication, the request payload (if any) and the possible response(s).
Loading