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

IBX-8921: Add OpenAPI symfony command #135

Open
wants to merge 45 commits into
base: IBX-8778-introduce-api-platform-annotations
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
d1e031d
composer json with api-platform as dependency
tischsoic Jun 26, 2024
071286d
fix alias
tischsoic Jun 26, 2024
010800c
WIP
tischsoic Jul 1, 2024
52a2cfc
improvements, cleanup etc
tischsoic Jul 1, 2024
adf4b2b
autotagging - NOT working for every service
tischsoic Jul 1, 2024
d8ad694
prepare language controller attributes
tischsoic Jul 2, 2024
e9d9fde
Remove unnecessary controller
tischsoic Jul 2, 2024
d8473d1
Remove unnecessary comment
tischsoic Jul 2, 2024
6b56a98
fix cs
tischsoic Jul 2, 2024
cdc8f37
phpstan
tischsoic Jul 2, 2024
dfda25b
revert original php version requirement
tischsoic Jul 2, 2024
723516b
revert space character delete
tischsoic Jul 2, 2024
600717f
vcs
tischsoic Jul 3, 2024
998a687
fix doc route
tischsoic Jul 4, 2024
ec3b0d5
Read schemas from yaml files
tischsoic Jul 17, 2024
dd6c9d0
ci workflow
tischsoic Jul 19, 2024
ebff100
Adjust language openapi data
tischsoic Aug 13, 2024
f45837e
fix SchemasProvider
tischsoic Aug 13, 2024
40cff86
Add ApiPlatformBundle to IbexaTestKernel
tischsoic Aug 13, 2024
dab22d3
adjust schemas and annotations
tischsoic Aug 27, 2024
d860b11
string -> bool
tischsoic Aug 27, 2024
50f6a84
introduce x-ibexa-example-file custom property
tischsoic Aug 29, 2024
70c7f0c
null check
tischsoic Sep 4, 2024
e0b7992
IBX-8778: Introduce API Platform attributes with schemas
tischsoic Aug 27, 2024
87808d3
Missing attribues, & Introduce x-ibexa-example-file
tischsoic Sep 4, 2024
86a7387
fixes in Location /content/locations/{path}/children
tischsoic Sep 14, 2024
2fc7f29
split controllers - one action per controller: Bookmark,Content,Contn…
tischsoic Sep 16, 2024
36d6d07
split controllers - one action per controller: ObjectState without se…
tischsoic Sep 16, 2024
8011b64
split controllers - one action per controller: User without services
tischsoic Sep 17, 2024
142e347
split controllers - one action per controller: Role without services
tischsoic Sep 17, 2024
2abcfef
Add Head Api Platform operation
tischsoic Sep 17, 2024
5d17e46
Fix Head operation import in BookmarkIsBookmarkedController
tischsoic Sep 17, 2024
1d5a8b8
split controllers - one action per controller: Language without services
tischsoic Sep 17, 2024
0b4d822
split controllers - one action per controller: Location without services
tischsoic Sep 17, 2024
cf5ae43
split controllers - one action per controller: Section without services
tischsoic Sep 17, 2024
6c25182
split controllers - one action per controller: Session without services
tischsoic Sep 17, 2024
ef9879e
add missing Api Platform attributes in Location/ and ObjectState/
tischsoic Sep 17, 2024
ae237bf
split controllers - one action per controller: Trash without services
tischsoic Sep 17, 2024
25d750d
split controllers - one action per controller: URLAlias without services
tischsoic Sep 17, 2024
de9ac2d
split controllers - one action per controller: URLWildcard without se…
tischsoic Sep 17, 2024
0566728
delete old controllers which methods were split
tischsoic Sep 17, 2024
0b7a099
fix-cs after controllers splitting
tischsoic Sep 18, 2024
72073fe
Role policy attributes fixes
tischsoic Sep 19, 2024
773625b
update services config
tischsoic Sep 19, 2024
f06337c
IBX-8921: Add OpenAPI symfony command
tischsoic Sep 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:
- uses: ramsey/composer-install@v3
with:
dependency-versions: highest
composer-options: "--ignore-platform-reqs --optimize-autoloader"

- name: Run code style check
run: composer run-script check-cs -- --format=checkstyle | cs2pr
Expand Down
13 changes: 11 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"ext-libxml": "*",
"ext-simplexml": "*",
"ext-xmlwriter": "*",
"api-platform/core": "dev-downgraded-deps",
"hautelook/templated-uri-bundle": "^3.4",
"ibexa/core": "~5.0.x-dev",
"lexik/jwt-authentication-bundle": "^2.8",
Expand Down Expand Up @@ -78,7 +79,15 @@
},
"extra": {
"branch-alias": {
"dev-main": "5.0.x-dev"
"dev-main": "5.0.x-dev",
"dev-IBX-8335-full-api-platform": "5.0.x-dev"
}
}
},
"repositories": {
"api-platform/core": {
"type": "vcs",
"url": "https://github.com/tischsoic/core"
}
},
"minimum-stability": "dev"
}
36 changes: 36 additions & 0 deletions src/bundle/ApiPlatform/ClassNameResourceNameCollectionFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Bundle\Rest\ApiPlatform;

use ApiPlatform\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
use ApiPlatform\Metadata\Resource\ResourceNameCollection;

/**
* @internal
*/
final class ClassNameResourceNameCollectionFactory implements ResourceNameCollectionFactoryInterface
{
/**
* @var array<string>
*/
private array $resources = [];

public function create(): ResourceNameCollection
{
return new ResourceNameCollection($this->resources);
}

/**
* @param array<string> $newResources
*/
public function addResources(array $newResources): void
{
$this->resources = array_merge($this->resources, $newResources);
}
}
174 changes: 174 additions & 0 deletions src/bundle/ApiPlatform/Head.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Bundle\Rest\ApiPlatform;

use ApiPlatform\Metadata\HttpOperation;
use ApiPlatform\Metadata\Parameters;
use ApiPlatform\OpenApi\Attributes\Webhook;
use ApiPlatform\OpenApi\Model\Operation as OpenApiOperation;
use ApiPlatform\State\OptionsInterface;

#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]
final class Head extends HttpOperation
{
public function __construct(

Check failure on line 20 in src/bundle/ApiPlatform/Head.php

View workflow job for this annotation

GitHub Actions / Unit & integration tests (8.3)

Method Ibexa\Bundle\Rest\ApiPlatform\Head::__construct() has parameter $defaults with no value type specified in iterable type array.

Check failure on line 20 in src/bundle/ApiPlatform/Head.php

View workflow job for this annotation

GitHub Actions / Unit & integration tests (8.3)

Method Ibexa\Bundle\Rest\ApiPlatform\Head::__construct() has parameter $denormalizationContext with no value type specified in iterable type array.

Check failure on line 20 in src/bundle/ApiPlatform/Head.php

View workflow job for this annotation

GitHub Actions / Unit & integration tests (8.3)

Method Ibexa\Bundle\Rest\ApiPlatform\Head::__construct() has parameter $exceptionToStatus with no value type specified in iterable type array.

Check failure on line 20 in src/bundle/ApiPlatform/Head.php

View workflow job for this annotation

GitHub Actions / Unit & integration tests (8.3)

Method Ibexa\Bundle\Rest\ApiPlatform\Head::__construct() has parameter $extraProperties with no value type specified in iterable type array.

Check failure on line 20 in src/bundle/ApiPlatform/Head.php

View workflow job for this annotation

GitHub Actions / Unit & integration tests (8.3)

Method Ibexa\Bundle\Rest\ApiPlatform\Head::__construct() has parameter $hydraContext with no value type specified in iterable type array.

Check failure on line 20 in src/bundle/ApiPlatform/Head.php

View workflow job for this annotation

GitHub Actions / Unit & integration tests (8.3)

Method Ibexa\Bundle\Rest\ApiPlatform\Head::__construct() has parameter $mercure with no value type specified in iterable type array.

Check failure on line 20 in src/bundle/ApiPlatform/Head.php

View workflow job for this annotation

GitHub Actions / Unit & integration tests (8.3)

Method Ibexa\Bundle\Rest\ApiPlatform\Head::__construct() has parameter $normalizationContext with no value type specified in iterable type array.

Check failure on line 20 in src/bundle/ApiPlatform/Head.php

View workflow job for this annotation

GitHub Actions / Unit & integration tests (8.3)

Method Ibexa\Bundle\Rest\ApiPlatform\Head::__construct() has parameter $openapiContext with no value type specified in iterable type array.

Check failure on line 20 in src/bundle/ApiPlatform/Head.php

View workflow job for this annotation

GitHub Actions / Unit & integration tests (8.3)

Method Ibexa\Bundle\Rest\ApiPlatform\Head::__construct() has parameter $options with no value type specified in iterable type array.

Check failure on line 20 in src/bundle/ApiPlatform/Head.php

View workflow job for this annotation

GitHub Actions / Unit & integration tests (8.3)

Method Ibexa\Bundle\Rest\ApiPlatform\Head::__construct() has parameter $requirements with no value type specified in iterable type array.
?string $uriTemplate = null,
?array $types = null,
$formats = null,
$inputFormats = null,
$outputFormats = null,
$uriVariables = null,
?string $routePrefix = null,
?string $routeName = null,
?array $defaults = null,
?array $requirements = null,
?array $options = null,
?bool $stateless = null,
?string $sunset = null,
?string $acceptPatch = null,
$status = null,
?string $host = null,
?array $schemes = null,
?string $condition = null,
?string $controller = null,
?array $headers = null,
?array $cacheHeaders = null,
?array $paginationViaCursor = null,
?array $hydraContext = null,
?array $openapiContext = null,
bool|OpenApiOperation|Webhook|null $openapi = null,
?array $exceptionToStatus = null,
?bool $queryParameterValidationEnabled = null,
?array $links = null,
?string $shortName = null,
?string $class = null,
?bool $paginationEnabled = null,
?string $paginationType = null,
?int $paginationItemsPerPage = null,
?int $paginationMaximumItemsPerPage = null,
?bool $paginationPartial = null,
?bool $paginationClientEnabled = null,
?bool $paginationClientItemsPerPage = null,
?bool $paginationClientPartial = null,
?bool $paginationFetchJoinCollection = null,
?bool $paginationUseOutputWalkers = null,
?array $order = null,
?string $description = null,
?array $normalizationContext = null,
?array $denormalizationContext = null,
?bool $collectDenormalizationErrors = null,
string|\Stringable|null $security = null,
?string $securityMessage = null,
string|\Stringable|null $securityPostDenormalize = null,
?string $securityPostDenormalizeMessage = null,
string|\Stringable|null $securityPostValidation = null,
?string $securityPostValidationMessage = null,
?string $deprecationReason = null,
?array $filters = null,
?array $validationContext = null,
$input = null,
$output = null,
$mercure = null,
$messenger = null,
?bool $elasticsearch = null,
?int $urlGenerationStrategy = null,
?bool $read = null,
?bool $deserialize = null,
?bool $validate = null,
?bool $write = null,
?bool $serialize = null,
?bool $fetchPartial = null,
?bool $forceEager = null,
?int $priority = null,
?string $name = null,
$provider = null,
$processor = null,
?OptionsInterface $stateOptions = null,
array|Parameters|null $parameters = null,
array $extraProperties = [],
) {
parent::__construct(
method: self::METHOD_HEAD,
uriTemplate: $uriTemplate,
types: $types,
formats: $formats,
inputFormats: $inputFormats,
outputFormats: $outputFormats,
uriVariables: $uriVariables,
routePrefix: $routePrefix,
routeName: $routeName,
defaults: $defaults,
requirements: $requirements,
options: $options,
stateless: $stateless,
sunset: $sunset,
acceptPatch: $acceptPatch,
status: $status,
host: $host,
schemes: $schemes,
condition: $condition,
controller: $controller,
headers: $headers,
cacheHeaders: $cacheHeaders,
paginationViaCursor: $paginationViaCursor,
hydraContext: $hydraContext,
openapiContext: $openapiContext,
openapi: $openapi,
exceptionToStatus: $exceptionToStatus,
queryParameterValidationEnabled: $queryParameterValidationEnabled,
links: $links,
shortName: $shortName,
class: $class,
paginationEnabled: $paginationEnabled,
paginationType: $paginationType,
paginationItemsPerPage: $paginationItemsPerPage,
paginationMaximumItemsPerPage: $paginationMaximumItemsPerPage,
paginationPartial: $paginationPartial,
paginationClientEnabled: $paginationClientEnabled,
paginationClientItemsPerPage: $paginationClientItemsPerPage,
paginationClientPartial: $paginationClientPartial,
paginationFetchJoinCollection: $paginationFetchJoinCollection,
paginationUseOutputWalkers: $paginationUseOutputWalkers,
order: $order,
description: $description,
normalizationContext: $normalizationContext,
denormalizationContext: $denormalizationContext,
collectDenormalizationErrors: $collectDenormalizationErrors,
security: $security,
securityMessage: $securityMessage,
securityPostDenormalize: $securityPostDenormalize,
securityPostDenormalizeMessage: $securityPostDenormalizeMessage,
securityPostValidation: $securityPostValidation,
securityPostValidationMessage: $securityPostValidationMessage,
deprecationReason: $deprecationReason,
filters: $filters,
validationContext: $validationContext,
input: $input,
output: $output,
mercure: $mercure,
messenger: $messenger,
elasticsearch: $elasticsearch,
urlGenerationStrategy: $urlGenerationStrategy,
read: $read,
deserialize: $deserialize,
validate: $validate,
write: $write,
serialize: $serialize,
fetchPartial: $fetchPartial,
forceEager: $forceEager,
priority: $priority,
name: $name,
provider: $provider,
processor: $processor,
stateOptions: $stateOptions,
parameters: $parameters,
extraProperties: $extraProperties,
);
}
}
147 changes: 147 additions & 0 deletions src/bundle/ApiPlatform/OpenApiFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Bundle\Rest\ApiPlatform;

use ApiPlatform\OpenApi\Factory\OpenApiFactoryInterface;
use ApiPlatform\OpenApi\Model\Response;
use ApiPlatform\OpenApi\OpenApi;
use Symfony\Component\HttpKernel\KernelInterface;

final class OpenApiFactory implements OpenApiFactoryInterface
{
public function __construct(
private readonly OpenApiFactoryInterface $decorated,
private readonly SchemasCollectionFactory $schemaCollectionFactory,
private readonly KernelInterface $kernel,
) {
}

/**
* @param array<mixed> $context
*/
public function __invoke(array $context = []): OpenApi
{
$openApi = $this->decorated->__invoke($context);
$openApi = $this->addSchemas($openApi);

$this->insertExampleFilesContent($openApi);

return $openApi;
}

private function addSchemas(OpenApi $openApi): OpenApi
{
$schemasCollection = $this->schemaCollectionFactory->create();
$schemas = iterator_to_array($schemasCollection);

$components = $openApi->getComponents();
$components = $components->withSchemas(new \ArrayObject($schemas));

$openApi = $openApi->withComponents($components);

return $openApi;
}

private function insertExampleFilesContent(OpenApi $openApi): void
{
$paths = $openApi->getPaths();

/** @var \ApiPlatform\OpenApi\Model\PathItem $pathItem */
foreach ($paths->getPaths() as $path => $pathItem) {
$newPathItem = $pathItem;

/** @var array<string, \ApiPlatform\OpenApi\Model\Operation|null> $methods */
$methods = [
'GET' => $pathItem->getGet(),
'PUT' => $pathItem->getPut(),
'POST' => $pathItem->getPost(),
'DELETE' => $pathItem->getDelete(),
'OPTIONS' => $pathItem->getOptions(),
'HEAD' => $pathItem->getHead(),
'PATCH' => $pathItem->getPatch(),
'TRACE' => $pathItem->getTrace(),
];
foreach ($methods as $method => $operation) {
if (empty($operation)) {
continue;
}

$responses = $operation->getResponses();

if ($responses === null) {
continue;
}

$newOperation = $operation;

/**
* @var int $responseCode
* @var \ApiPlatform\OpenApi\Model\Response|array<string, array<mixed>> $response
*/
foreach ($operation->getResponses() as $responseCode => $response) {
if (!is_array($response) || !array_key_exists('content', $response)) {
continue;
}

$content = $response['content'];
$newContent = $content;

foreach ($newContent as $mediaType => $responseContent) {
if (array_key_exists('x-ibexa-example-file', $responseContent)) {
$exampleFilePath = $this->kernel->locateResource($responseContent['x-ibexa-example-file']);
$exampleFileContent = file_get_contents($exampleFilePath);
$newContent[$mediaType]['example'] = $exampleFileContent;
unset($newContent[$mediaType]['x-ibexa-example-file']);
}
}

if ($newContent !== $content) {
$newOperation = $newOperation->withResponse(
$responseCode,
new Response((string)$responseCode, new \ArrayObject($newContent)),
);
}
}

if ($newOperation !== $operation) {
switch ($method) {
case 'GET':
$newPathItem = $newPathItem->withGet($newOperation);
break;
case 'PUT':
$newPathItem = $newPathItem->withPut($newOperation);
break;
case 'POST':
$newPathItem = $newPathItem->withPost($newOperation);
break;
case 'DELETE':
$newPathItem = $newPathItem->withDelete($newOperation);
break;
case 'OPTIONS':
$newPathItem = $newPathItem->withOptions($newOperation);
break;
case 'HEAD':
$newPathItem = $newPathItem->withHead($newOperation);
break;
case 'PATCH':
$newPathItem = $newPathItem->withPatch($newOperation);
break;
case 'TRACE':
$newPathItem = $newPathItem->withTrace($newOperation);
break;
}
}
}

if ($newPathItem !== $pathItem) {
$paths->addPath($path, $newPathItem);
}
}
}
}
Loading
Loading