Skip to content

Commit af7b4d5

Browse files
committed
Merge 4.1
2 parents f482260 + 34d30e8 commit af7b4d5

File tree

17 files changed

+362
-191
lines changed

17 files changed

+362
-191
lines changed

ApiPlatformProvider.php

Lines changed: 27 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@
6666
use ApiPlatform\JsonApi\Serializer\ItemNormalizer as JsonApiItemNormalizer;
6767
use ApiPlatform\JsonApi\Serializer\ObjectNormalizer as JsonApiObjectNormalizer;
6868
use ApiPlatform\JsonApi\Serializer\ReservedAttributeNameConverter;
69-
use ApiPlatform\JsonLd\Action\ContextAction;
7069
use ApiPlatform\JsonLd\AnonymousContextBuilderInterface;
7170
use ApiPlatform\JsonLd\ContextBuilder as JsonLdContextBuilder;
7271
use ApiPlatform\JsonLd\ContextBuilderInterface;
@@ -78,6 +77,7 @@
7877
use ApiPlatform\JsonSchema\SchemaFactory;
7978
use ApiPlatform\JsonSchema\SchemaFactoryInterface;
8079
use ApiPlatform\Laravel\ApiResource\Error;
80+
use ApiPlatform\Laravel\ApiResource\ValidationError;
8181
use ApiPlatform\Laravel\Controller\ApiPlatformController;
8282
use ApiPlatform\Laravel\Controller\DocumentationController;
8383
use ApiPlatform\Laravel\Controller\EntrypointController;
@@ -125,7 +125,6 @@
125125
use ApiPlatform\Laravel\State\SwaggerUiProcessor;
126126
use ApiPlatform\Laravel\State\SwaggerUiProvider;
127127
use ApiPlatform\Laravel\State\ValidateProvider;
128-
use ApiPlatform\Metadata\Exception\NotExposedHttpException;
129128
use ApiPlatform\Metadata\IdentifiersExtractor;
130129
use ApiPlatform\Metadata\IdentifiersExtractorInterface;
131130
use ApiPlatform\Metadata\InflectorInterface;
@@ -179,6 +178,7 @@
179178
use ApiPlatform\Serializer\SerializerContextBuilder;
180179
use ApiPlatform\State\CallableProcessor;
181180
use ApiPlatform\State\CallableProvider;
181+
use ApiPlatform\State\ErrorProvider;
182182
use ApiPlatform\State\Pagination\Pagination;
183183
use ApiPlatform\State\Pagination\PaginationOptions;
184184
use ApiPlatform\State\ParameterProviderInterface;
@@ -197,10 +197,6 @@
197197
use Illuminate\Config\Repository as ConfigRepository;
198198
use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandlerInterface;
199199
use Illuminate\Contracts\Foundation\Application;
200-
use Illuminate\Contracts\Foundation\CachesRoutes;
201-
use Illuminate\Http\Request;
202-
use Illuminate\Routing\Route;
203-
use Illuminate\Routing\RouteCollection;
204200
use Illuminate\Routing\Router;
205201
use Illuminate\Support\ServiceProvider;
206202
use Negotiation\Negotiator;
@@ -254,6 +250,7 @@ public function register(): void
254250
);
255251
});
256252

253+
$this->app->singleton(ModelMetadata::class);
257254
$this->app->bind(LoaderInterface::class, AttributeLoader::class);
258255
$this->app->bind(ClassMetadataFactoryInterface::class, ClassMetadataFactory::class);
259256
$this->app->singleton(ClassMetadataFactory::class, function (Application $app) {
@@ -323,7 +320,7 @@ public function register(): void
323320
$app->make(ResourceClassResolverInterface::class)
324321
),
325322
),
326-
true === $config->get('app.debug') ? 'array' : $config->get('cache.default', 'file')
323+
true === $config->get('app.debug') ? 'array' : $config->get('api-platform.cache', 'file')
327324
);
328325
});
329326

@@ -341,7 +338,7 @@ public function register(): void
341338
)
342339
)
343340
),
344-
true === $config->get('app.debug') ? 'array' : $config->get('cache.default', 'file')
341+
true === $config->get('app.debug') ? 'array' : $config->get('api-platform.cache', 'file')
345342
);
346343
});
347344

@@ -412,7 +409,7 @@ public function register(): void
412409
$app->make('filters')
413410
)
414411
),
415-
true === $config->get('app.debug') ? 'array' : $config->get('cache.default', 'file')
412+
true === $config->get('app.debug') ? 'array' : $config->get('api-platform.cache', 'file')
416413
);
417414
});
418415

@@ -779,6 +776,9 @@ public function register(): void
779776
licenseUrl: $config->get('api-platform.swagger_ui.license.url', ''),
780777
persistAuthorization: $config->get('api-platform.swagger_ui.persist_authorization', false),
781778
httpAuth: $config->get('api-platform.swagger_ui.http_auth', []),
779+
tags: $config->get('api-platform.openapi.tags', []),
780+
errorResourceClass: Error::class,
781+
validationErrorResourceClass: ValidationError::class
782782
);
783783
});
784784

@@ -852,7 +852,9 @@ public function register(): void
852852
null,
853853
$config->get('api-platform.formats'),
854854
$app->make(Options::class),
855-
$app->make(PaginationOptions::class), // ?PaginationOptions $paginationOptions = null,
855+
$app->make(PaginationOptions::class),
856+
null,
857+
$config->get('api-platform.error_formats'),
856858
// ?RouterInterface $router = null
857859
);
858860
});
@@ -1222,6 +1224,18 @@ private function registerGraphQl(Application $app): void
12221224
});
12231225
$app->alias(GraphQlReadProvider::class, 'api_platform.graphql.state_provider.read');
12241226

1227+
$app->singleton(ErrorProvider::class, function (Application $app) {
1228+
/** @var ConfigRepository */
1229+
$config = $app['config'];
1230+
1231+
return new ErrorProvider(
1232+
$config->get('app.debug'),
1233+
$app->make(ResourceClassResolver::class),
1234+
$app->make(ResourceMetadataCollectionFactoryInterface::class),
1235+
);
1236+
});
1237+
$app->tag([ErrorProvider::class], ProviderInterface::class);
1238+
12251239
$app->singleton(ResolverProvider::class, function (Application $app) {
12261240
$resolvers = iterator_to_array($app->tagged('api_platform.graphql.resolver'));
12271241
$taggedItemResolvers = iterator_to_array($app->tagged(QueryItemResolverInterface::class));
@@ -1320,7 +1334,7 @@ private function registerGraphQl(Application $app): void
13201334
/** @var ConfigRepository */
13211335
$config = $app['config'];
13221336

1323-
return new Executor($config->get('api-platform.graphql.introspection.enabled') ?? false, $config->get('api-platform.graphql.max_query_complexity'), $config->get('api-platform.graphql.max_query_depth'));
1337+
return new Executor($config->get('api-platform.graphql.introspection.enabled') ?? false, $config->get('api-platform.graphql.max_query_complexity') ?? 500, $config->get('api-platform.graphql.max_query_depth') ?? 200);
13241338
});
13251339

13261340
$app->singleton(GraphiQlController::class, function (Application $app) {
@@ -1351,7 +1365,7 @@ private function registerGraphQl(Application $app): void
13511365
/**
13521366
* Bootstrap services.
13531367
*/
1354-
public function boot(ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory, Router $router): void
1368+
public function boot(): void
13551369
{
13561370
if ($this->app->runningInConsole()) {
13571371
$this->publishes([
@@ -1373,94 +1387,6 @@ public function boot(ResourceNameCollectionFactoryInterface $resourceNameCollect
13731387
$typeBuilder->setFieldsBuilderLocator(new ServiceLocator(['api_platform.graphql.fields_builder' => $fieldsBuilder]));
13741388
}
13751389

1376-
if (!$this->shouldRegisterRoutes()) {
1377-
return;
1378-
}
1379-
1380-
$globalMiddlewares = $config->get('api-platform.routes.middleware');
1381-
$routeCollection = new RouteCollection();
1382-
foreach ($resourceNameCollectionFactory->create() as $resourceClass) {
1383-
foreach ($resourceMetadataFactory->create($resourceClass) as $resourceMetadata) {
1384-
foreach ($resourceMetadata->getOperations() as $operation) {
1385-
$uriTemplate = $operation->getUriTemplate();
1386-
// _format is read by the middleware
1387-
$uriTemplate = $operation->getRoutePrefix().str_replace('{._format}', '{_format?}', $uriTemplate);
1388-
$route = (new Route([$operation->getMethod()], $uriTemplate, [ApiPlatformController::class, '__invoke']))
1389-
->where('_format', '^\.[a-zA-Z]+')
1390-
->name($operation->getName())
1391-
->setDefaults(['_api_operation_name' => $operation->getName(), '_api_resource_class' => $operation->getClass()]);
1392-
1393-
$route->middleware(ApiPlatformMiddleware::class.':'.$operation->getName());
1394-
$route->middleware($globalMiddlewares);
1395-
$route->middleware($operation->getMiddleware());
1396-
1397-
$routeCollection->add($route);
1398-
}
1399-
}
1400-
}
1401-
1402-
$prefix = $config->get('api-platform.defaults.route_prefix') ?? '';
1403-
$route = new Route(['GET'], $prefix.'/contexts/{shortName?}{_format?}', [ContextAction::class, '__invoke']);
1404-
$route->name('api_jsonld_context');
1405-
$route->middleware(ApiPlatformMiddleware::class);
1406-
$route->middleware($globalMiddlewares);
1407-
$routeCollection->add($route);
1408-
$route = new Route(['GET'], $prefix.'/docs{_format?}', function (Request $request, Application $app) {
1409-
$documentationAction = $app->make(DocumentationController::class);
1410-
1411-
return $documentationAction->__invoke($request);
1412-
});
1413-
$route->name('api_doc');
1414-
$route->middleware(ApiPlatformMiddleware::class);
1415-
$route->middleware($globalMiddlewares);
1416-
$routeCollection->add($route);
1417-
1418-
$route = new Route(['GET'], $prefix.'/.well-known/genid/{id}', function (): void {
1419-
throw new NotExposedHttpException('This route is not exposed on purpose. It generates an IRI for a collection resource without identifier nor item operation.');
1420-
});
1421-
$route->name('api_genid');
1422-
$route->middleware(ApiPlatformMiddleware::class);
1423-
$route->middleware($globalMiddlewares);
1424-
$routeCollection->add($route);
1425-
1426-
if ($config->get('api-platform.graphql.enabled')) {
1427-
$route = new Route(['POST', 'GET'], $prefix.'/graphql', function (Application $app, Request $request) {
1428-
$entrypointAction = $app->make(GraphQlEntrypointController::class);
1429-
1430-
return $entrypointAction->__invoke($request);
1431-
});
1432-
$route->middleware($globalMiddlewares);
1433-
$routeCollection->add($route);
1434-
1435-
$route = new Route(['GET'], $prefix.'/graphiql', function (Application $app) {
1436-
$controller = $app->make(GraphiQlController::class);
1437-
1438-
return $controller->__invoke();
1439-
});
1440-
$route->middleware($globalMiddlewares);
1441-
$routeCollection->add($route);
1442-
}
1443-
1444-
$route = new Route(['GET'], $prefix.'/{index?}{_format?}', function (Request $request, Application $app) {
1445-
$entrypointAction = $app->make(EntrypointController::class);
1446-
1447-
return $entrypointAction->__invoke($request);
1448-
});
1449-
$route->where('index', 'index');
1450-
$route->name('api_entrypoint');
1451-
$route->middleware(ApiPlatformMiddleware::class);
1452-
$route->middleware($globalMiddlewares);
1453-
$routeCollection->add($route);
1454-
1455-
$router->setRoutes($routeCollection);
1456-
}
1457-
1458-
private function shouldRegisterRoutes(): bool
1459-
{
1460-
if ($this->app instanceof CachesRoutes && $this->app->routesAreCached()) {
1461-
return false;
1462-
}
1463-
1464-
return true;
1390+
$this->loadRoutesFrom(__DIR__.'/routes/api.php');
14651391
}
14661392
}

ApiResource/Error.php

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,51 +13,81 @@
1313

1414
namespace ApiPlatform\Laravel\ApiResource;
1515

16-
use ApiPlatform\JsonLd\ContextBuilderInterface;
16+
use ApiPlatform\JsonSchema\SchemaFactory;
1717
use ApiPlatform\Metadata\ApiProperty;
1818
use ApiPlatform\Metadata\Error as Operation;
1919
use ApiPlatform\Metadata\ErrorResource;
20+
use ApiPlatform\Metadata\ErrorResourceInterface;
2021
use ApiPlatform\Metadata\Exception\HttpExceptionInterface;
2122
use ApiPlatform\Metadata\Exception\ProblemExceptionInterface;
23+
use ApiPlatform\State\ErrorProvider;
2224
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface as SymfonyHttpExceptionInterface;
2325
use Symfony\Component\Serializer\Annotation\Groups;
2426
use Symfony\Component\Serializer\Annotation\Ignore;
2527
use Symfony\Component\Serializer\Annotation\SerializedName;
2628
use Symfony\Component\WebLink\Link;
2729

2830
#[ErrorResource(
29-
types: ['hydra:Error'],
31+
uriTemplate: '/errors/{status}{._format}',
3032
openapi: false,
33+
uriVariables: ['status'],
3134
operations: [
3235
new Operation(
36+
errors: [],
3337
name: '_api_errors_problem',
34-
outputFormats: ['json' => ['application/problem+json']],
38+
routeName: '_api_errors',
39+
outputFormats: ['json' => ['application/problem+json', 'application/json']],
40+
hideHydraOperation: true,
3541
normalizationContext: [
42+
SchemaFactory::OPENAPI_DEFINITION_NAME => '',
3643
'groups' => ['jsonproblem'],
3744
'skip_null_values' => true,
45+
'ignored_attributes' => ['trace', 'file', 'line', 'code', 'message', 'traceAsString', 'previous'],
3846
],
39-
uriTemplate: '/errors/{status}'
4047
),
4148
new Operation(
49+
errors: [],
4250
name: '_api_errors_hydra',
43-
outputFormats: ['jsonld' => ['application/problem+json']],
51+
routeName: '_api_errors',
52+
outputFormats: ['jsonld' => ['application/problem+json', 'application/ld+json']],
4453
normalizationContext: [
54+
SchemaFactory::OPENAPI_DEFINITION_NAME => '',
4555
'groups' => ['jsonld'],
4656
'skip_null_values' => true,
57+
'ignored_attributes' => ['trace', 'file', 'line', 'code', 'message', 'traceAsString', 'previous'],
4758
],
48-
links: [new Link(rel: ContextBuilderInterface::JSONLD_NS.'error', href: 'http://www.w3.org/ns/hydra/error')],
49-
uriTemplate: '/errors/{status}.jsonld'
59+
links: [new Link(rel: 'http://www.w3.org/ns/json-ld#error', href: 'http://www.w3.org/ns/hydra/error')],
5060
),
5161
new Operation(
62+
errors: [],
5263
name: '_api_errors_jsonapi',
64+
routeName: '_api_errors',
65+
hideHydraOperation: true,
5366
outputFormats: ['jsonapi' => ['application/vnd.api+json']],
54-
normalizationContext: ['groups' => ['jsonapi'], 'skip_null_values' => true],
55-
uriTemplate: '/errors/{status}.jsonapi'
67+
normalizationContext: [
68+
SchemaFactory::OPENAPI_DEFINITION_NAME => '',
69+
'disable_json_schema_serializer_groups' => false,
70+
'groups' => ['jsonapi'],
71+
'skip_null_values' => true,
72+
'ignored_attributes' => ['trace', 'file', 'line', 'code', 'message', 'traceAsString', 'previous'],
73+
],
74+
),
75+
new Operation(
76+
name: '_api_errors',
77+
hideHydraOperation: true,
78+
extraProperties: ['_api_disable_swagger_provider' => true],
79+
outputFormats: ['html' => ['text/html'], 'jsonapi' => ['application/vnd.api+json'], 'jsonld' => ['application/ld+json'], 'json' => ['application/problem+json', 'application/json']],
5680
),
5781
],
58-
graphQlOperations: []
82+
outputFormats: ['jsonapi' => ['application/vnd.api+json'], 'jsonld' => ['application/ld+json'], 'json' => ['application/problem+json', 'application/json']],
83+
provider: ErrorProvider::class,
84+
graphQlOperations: [],
85+
description: 'A representation of common errors.',
5986
)]
60-
class Error extends \Exception implements ProblemExceptionInterface, HttpExceptionInterface
87+
#[ApiProperty(property: 'previous', hydra: false, readable: false)]
88+
#[ApiProperty(property: 'traceAsString', hydra: false, readable: false)]
89+
#[ApiProperty(property: 'string', hydra: false, readable: false)]
90+
class Error extends \Exception implements ProblemExceptionInterface, HttpExceptionInterface, ErrorResourceInterface
6191
{
6292
/**
6393
* @var array<int, mixed>
@@ -72,7 +102,7 @@ public function __construct(
72102
private readonly string $title,
73103
private readonly string $detail,
74104
#[ApiProperty(identifier: true)] private int $status,
75-
array $originalTrace,
105+
array $originalTrace = [],
76106
private readonly ?string $instance = null,
77107
private string $type = 'about:blank',
78108
private array $headers = [],

0 commit comments

Comments
 (0)