Skip to content

Commit 64f1f2a

Browse files
Merge pull request #1 from freelancerwebro/feature/swagger_doc
Implement api swagger documentation
2 parents 8a56f04 + 7b38579 commit 64f1f2a

15 files changed

+654
-34
lines changed

.env.example

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,7 @@ DATABASE_URL="mysql://symfony:symfony@database/bands-service?serverVersion=8.0.3
2222
KERNEL_CLASS='App\Kernel'
2323
SYMFONY_DEPRECATIONS_HELPER=999999
2424
PANTHER_APP_ENV=panther
25-
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots
25+
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots
26+
27+
SWAGGER_JSON_URL=http://localhost:8078/api/doc.json
28+
CORS_ALLOW_ORIGIN=*

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,5 @@
2828
/.php-cs-fixer.php
2929
/.php-cs-fixer.cache
3030
###< friendsofphp/php-cs-fixer ###
31+
32+
swagger.json

composer.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
"php": ">=8.2",
88
"ext-ctype": "*",
99
"ext-iconv": "*",
10+
"api-platform/core": "^4.0",
1011
"doctrine/dbal": "^3",
1112
"doctrine/doctrine-bundle": "^2.12",
1213
"doctrine/doctrine-migrations-bundle": "^3.3",
1314
"doctrine/orm": "^3.1",
1415
"friendsofsymfony/rest-bundle": "^3.7",
1516
"jms/serializer-bundle": "^5.4",
16-
"nelmio/api-doc-bundle": "^4.26",
17+
"nelmio/api-doc-bundle": "^4.34",
18+
"nelmio/cors-bundle": "^2.5",
1719
"phpdocumentor/reflection-docblock": "^5.4",
1820
"phpoffice/phpspreadsheet": "^2.1",
1921
"phpstan/phpdoc-parser": "^1.29",

composer.lock

+451-29
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/bundles.php

+2
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@
1111
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
1212
Nelmio\ApiDocBundle\NelmioApiDocBundle::class => ['all' => true],
1313
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
14+
ApiPlatform\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true],
15+
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
1416
];

config/packages/api_platform.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
api_platform:
2+
title: Hello API Platform
3+
version: 1.0.0
4+
defaults:
5+
stateless: true
6+
cache_headers:
7+
vary: ['Content-Type', 'Authorization', 'Origin']
8+
error_formats: {}

config/packages/nelmio_api_doc.yaml

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
nelmio_api_doc:
22
documentation:
33
info:
4-
title: My App
5-
description: This is an awesome app!
4+
title: Bands Service - API Documentation
5+
description: Swagger documentation for the Bands Service API
66
version: 1.0.0
77
areas: # to filter documented areas
88
path_patterns:
9-
- ^/api(?!/doc$) # Accepts routes under /api except /api/doc
9+
- ^/ # Accepts routes under /api except /api/doc
10+
name_patterns:
11+
- ^app_ # Include only routes starting with api_
12+
host_patterns: [ ]

config/packages/nelmio_cors.yaml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
nelmio_cors:
2+
defaults:
3+
allow_origin: ['*'] # Allow Swagger UI origin
4+
allow_methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
5+
allow_headers: ['Content-Type', 'Authorization']
6+
expose_headers: ['Link']
7+
max_age: 3600
8+
hosts: []
9+
paths:
10+
'^/': # Apply CORS only to API routes
11+
allow_origin: ['*']
12+
allow_headers: ['Content-Type', 'Authorization']
13+
allow_methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
14+
max_age: 3600

config/routes/api_platform.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
api_platform:
2+
resource: .
3+
type: api_platform
4+
prefix: /api

deploy.sh

+2
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ echo 'Running migrations...'
1515
docker exec -it bands-service-php php bin/console doctrine:migrations:migrate --no-interaction
1616
docker exec -it bands-service-php php bin/console cache:clear --no-interaction
1717

18+
echo 'Generate swagger documentation...'
19+
docker exec -it bands-service-php php bin/console api:openapi:export --format=json > swagger.json

docker-compose.yml

+14
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ services:
88
working_dir: /var/www/html
99
volumes:
1010
- .:/var/www/html
11+
environment:
12+
CORS_ALLOW_ORIGIN: "*"
1113
depends_on:
1214
- database
1315
networks:
@@ -42,6 +44,18 @@ services:
4244
networks:
4345
- symfony
4446

47+
swagger-ui:
48+
image: swaggerapi/swagger-ui
49+
container_name: bands-service-swagger-ui
50+
restart: unless-stopped
51+
ports:
52+
- "8079:8080"
53+
depends_on:
54+
- nginx
55+
environment:
56+
SWAGGER_JSON_URL: "${SWAGGER_JSON_URL}"
57+
networks:
58+
- symfony
4559
networks:
4660
symfony:
4761
driver: bridge

src/ApiResource/.gitignore

Whitespace-only changes.

src/Controller/BandController.php

+91
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,26 @@
1212
use Symfony\Component\Serializer\SerializerInterface;
1313
use Symfony\Component\Validator\Validator\ValidatorInterface;
1414

15+
/**
16+
* @OA\Tag(name="Bands")
17+
*/
1518
class BandController extends AbstractController
1619
{
1720
public function __construct(
1821
private readonly BandRepository $bandRepository,
1922
) {
2023
}
2124

25+
/**
26+
* List all bands.
27+
*
28+
* @OA\Get(
29+
* path="/band",
30+
* summary="List all bands",
31+
*
32+
* @OA\Response(response=200, description="List of bands")
33+
* )
34+
*/
2235
#[Route('/band', name: 'app_band_list', methods: ['GET'])]
2336
public function index(): JsonResponse
2437
{
@@ -27,12 +40,54 @@ public function index(): JsonResponse
2740
);
2841
}
2942

43+
/**
44+
* Retrieve a single band by ID.
45+
*
46+
* @OA\Get(
47+
* path="/band/{id}",
48+
* summary="Retrieve a single band",
49+
*
50+
* @OA\Parameter(
51+
* name="id",
52+
* in="path",
53+
* required=true,
54+
*
55+
* @OA\Schema(type="integer"),
56+
* description="Band ID"
57+
* ),
58+
*
59+
* @OA\Response(response=200, description="Band details"),
60+
* @OA\Response(response=404, description="Band not found")
61+
* )
62+
*/
3063
#[Route('/band/{id}', name: 'app_band_show_one', methods: ['GET'])]
3164
public function show(Band $band): JsonResponse
3265
{
3366
return $this->json($band);
3467
}
3568

69+
/**
70+
* Add a new band.
71+
*
72+
* @OA\Post(
73+
* path="/band",
74+
* summary="Add a new band",
75+
*
76+
* @OA\RequestBody(
77+
* required=true,
78+
*
79+
* @OA\JsonContent(
80+
* required={"name"},
81+
*
82+
* @OA\Property(property="name", type="string", description="Band name"),
83+
* @OA\Property(property="genre", type="string", description="Music genre")
84+
* )
85+
* ),
86+
*
87+
* @OA\Response(response=201, description="Band created"),
88+
* @OA\Response(response=400, description="Invalid input")
89+
* )
90+
*/
3691
#[Route('/band', name: 'app_band_create', methods: ['POST'])]
3792
public function create(
3893
Request $request,
@@ -55,6 +110,29 @@ public function create(
55110
return $this->json($band, 201);
56111
}
57112

113+
/**
114+
* Update an existing band.
115+
*
116+
* @OA\Put(
117+
* path="/band/{id}",
118+
* summary="Update an existing band",
119+
*
120+
* @OA\Parameter(name="id", in="path", required=true, @OA\Schema(type="integer")),
121+
*
122+
* @OA\RequestBody(
123+
* required=true,
124+
*
125+
* @OA\JsonContent(
126+
*
127+
* @OA\Property(property="name", type="string"),
128+
* @OA\Property(property="genre", type="string")
129+
* )
130+
* ),
131+
*
132+
* @OA\Response(response=200, description="Band updated"),
133+
* @OA\Response(response=404, description="Band not found")
134+
* )
135+
*/
58136
#[Route('/band/{id}', name: 'app_band_update', methods: ['PUT'])]
59137
public function update(
60138
Request $request,
@@ -82,6 +160,19 @@ public function update(
82160
return $this->json($band);
83161
}
84162

163+
/**
164+
* Delete a band.
165+
*
166+
* @OA\Delete(
167+
* path="/band/{id}",
168+
* summary="Delete a band",
169+
*
170+
* @OA\Parameter(name="id", in="path", required=true, @OA\Schema(type="integer")),
171+
*
172+
* @OA\Response(response=204, description="Band deleted"),
173+
* @OA\Response(response=404, description="Band not found")
174+
* )
175+
*/
85176
#[Route('/band/{id}', name: 'app_band_delete', methods: ['DELETE'])]
86177
public function delete(Band $band): JsonResponse
87178
{

src/Controller/ImportController.php

+27
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,35 @@
1212
use Symfony\Component\HttpFoundation\Response;
1313
use Symfony\Component\Routing\Attribute\Route;
1414

15+
/**
16+
* @OA\Tag(name="Import")
17+
*/
1518
class ImportController extends AbstractController
1619
{
20+
/**
21+
* Import bands from an Excel/CSV file.
22+
*
23+
* @OA\Post(
24+
* path="/import",
25+
* summary="Import bands from an Excel/CSV file",
26+
*
27+
* @OA\RequestBody(
28+
* required=true,
29+
*
30+
* @OA\MediaType(
31+
* mediaType="multipart/form-data",
32+
*
33+
* @OA\Schema(
34+
*
35+
* @OA\Property(property="file", type="string", format="binary", description="The Excel or CSV file")
36+
* )
37+
* )
38+
* ),
39+
*
40+
* @OA\Response(response=201, description="File processed successfully"),
41+
* @OA\Response(response=400, description="Invalid file format")
42+
* )
43+
*/
1744
#[Route('/import', name: 'app_import', methods: ['POST'])]
1845
public function index(
1946
Request $request,

symfony.lock

+26
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
11
{
2+
"api-platform/core": {
3+
"version": "4.0",
4+
"recipe": {
5+
"repo": "github.com/symfony/recipes",
6+
"branch": "main",
7+
"version": "4.0",
8+
"ref": "cb9e6b8ceb9b62f32d41fc8ad72a25d5bd674c6d"
9+
},
10+
"files": [
11+
"config/packages/api_platform.yaml",
12+
"config/routes/api_platform.yaml",
13+
"src/ApiResource/.gitignore"
14+
]
15+
},
216
"doctrine/doctrine-bundle": {
317
"version": "2.12",
418
"recipe": {
@@ -87,6 +101,18 @@
87101
"config/routes/nelmio_api_doc.yaml"
88102
]
89103
},
104+
"nelmio/cors-bundle": {
105+
"version": "2.5",
106+
"recipe": {
107+
"repo": "github.com/symfony/recipes",
108+
"branch": "main",
109+
"version": "1.5",
110+
"ref": "6bea22e6c564fba3a1391615cada1437d0bde39c"
111+
},
112+
"files": [
113+
"config/packages/nelmio_cors.yaml"
114+
]
115+
},
90116
"phpstan/phpstan": {
91117
"version": "2.1",
92118
"recipe": {

0 commit comments

Comments
 (0)