View source
<?php
namespace Drupal\openapi\Plugin\openapi\OpenApiGenerator;
use Drupal\openapi\Plugin\openapi\OpenApiGeneratorBase;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Routing\RouteProviderInterface;
use Drupal\openapi\RestInspectionTrait;
use Drupal\rest\RestResourceConfigInterface;
use Drupal\schemata\SchemaFactory;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\Route;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class RestGenerator extends OpenApiGeneratorBase {
use RestInspectionTrait;
public function nonBundleResourcesJson() {
$resource_configs = $this->entityTypeManager
->getStorage('rest_resource_config')
->loadMultiple();
$non_entity_configs = [];
foreach ($resource_configs as $resource_config) {
if (!$this
->isEntityResource($resource_config)) {
$non_entity_configs[] = $resource_config;
}
else {
$entity_type = $this
->getEntityType($resource_config);
if (!$entity_type
->getBundleEntityType()) {
$non_entity_configs[] = $resource_config;
}
}
}
$spec = $this
->getSpecification($non_entity_configs);
$response = new JsonResponse($spec);
return $response;
}
public function getDefinitions() {
$bundle_name = isset($this
->getOptions()['bundle_name']) ? $this
->getOptions()['bundle_name'] : NULL;
$entity_type_id = isset($this
->getOptions()['entity_type_id']) ? $this
->getOptions()['entity_type_id'] : NULL;
static $definitions = [];
if (!$definitions) {
$entity_types = $this
->getRestEnabledEntityTypes($entity_type_id);
$definitions = [];
foreach ($entity_types as $entity_id => $entity_type) {
$entity_schema = $this
->getJsonSchema('json', $entity_id);
$definitions[$entity_id] = $entity_schema;
if ($bundle_type = $entity_type
->getBundleEntityType()) {
$bundle_storage = $this->entityTypeManager
->getStorage($bundle_type);
if ($bundle_name) {
$bundles[$bundle_name] = $bundle_storage
->load($bundle_name);
}
else {
$bundles = $bundle_storage
->loadMultiple();
}
foreach ($bundles as $bundle => $bundle_data) {
$bundle_schema = $this
->getJsonSchema('json', $entity_id, $bundle);
foreach ($entity_schema['properties'] as $property_id => $property) {
if (isset($bundle_schema['properties'][$property_id]) && $bundle_schema['properties'][$property_id] === $property) {
unset($bundle_schema['properties'][$property_id]);
}
}
$definitions[$this
->getEntityDefinitionKey($entity_type
->id(), $bundle)] = [
'allOf' => [
[
'$ref' => "#/definitions/{$entity_id}",
],
$bundle_schema,
],
];
}
}
}
}
return $definitions;
}
public function getConsumes() {
return $this
->generateMimeTypesFromFormats($this
->getRestSupportedFormats());
}
public function getProduces() {
return $this
->generateMimeTypesFromFormats($this
->getRestSupportedFormats());
}
public function getTags() {
$entity_types = $this
->getRestEnabledEntityTypes();
$tags = [];
foreach ($entity_types as $entity_type) {
if ($this
->includeEntityTypeBundle($entity_type
->id())) {
$tag = [
'name' => $entity_type
->id(),
'description' => $this
->t("Entity type: @label", [
'@label' => $entity_type
->getLabel(),
]),
'x-entity-type' => $entity_type
->id(),
'x-definition' => [
'$ref' => '#/definitions/' . $this
->getEntityDefinitionKey($entity_type
->id()),
],
];
$tags[] = $tag;
}
}
return $tags;
}
public function getPaths() {
$bundle_name = isset($this
->getOptions()['bundle_name']) ? $this
->getOptions()['bundle_name'] : NULL;
$resource_configs = $this
->getResourceConfigs($this
->getOptions());
if (!$resource_configs) {
return [];
}
$api_paths = [];
foreach ($resource_configs as $resource_config) {
$resource_plugin = $resource_config
->getResourcePlugin();
foreach ($resource_config
->getMethods() as $method) {
if ($route = $this
->getRouteForResourceMethod($resource_config, $method)) {
$open_api_method = strtolower($method);
$path = $route
->getPath();
$path_method_spec = [];
$formats = $this
->getMethodSupportedFormats($method, $resource_config);
$format_parameter = [
'name' => '_format',
'in' => 'query',
'type' => 'string',
'enum' => $formats,
'required' => TRUE,
'description' => 'Request format',
];
if (count($formats) == 1) {
$format_parameter['default'] = $formats[0];
}
$path_method_spec['parameters'][] = $format_parameter;
$path_method_spec['responses'] = $this
->getErrorResponses();
if ($this
->isEntityResource($resource_config)) {
$entity_type = $this
->getEntityType($resource_config);
$path_method_spec['tags'] = [
$entity_type
->id(),
];
$path_method_spec['summary'] = $this
->t('@method a @entity_type', [
'@method' => ucfirst($open_api_method),
'@entity_type' => $entity_type
->getLabel(),
]);
$path_method_spec['parameters'] = array_merge($path_method_spec['parameters'], $this
->getEntityParameters($entity_type, $method, $bundle_name));
$path_method_spec['responses'] = $this
->getEntityResponses($entity_type
->id(), $method, $bundle_name) + $path_method_spec['responses'];
}
else {
$path_method_spec['responses']['200'] = [
'description' => 'successful operation',
];
$path_method_spec['summary'] = $resource_plugin
->getPluginDefinition()['label'];
$path_method_spec['parameters'] = array_merge($path_method_spec['parameters'], $this
->getRouteParameters($route));
}
$path_method_spec['operationId'] = $resource_plugin
->getPluginId() . ":" . $method;
$path_method_spec['schemes'] = [
$this->request
->getScheme(),
];
$path_method_spec['security'] = $this
->getResourceSecurity($resource_config, $method, $formats);
$api_paths[$path][$open_api_method] = $path_method_spec;
}
}
}
return $api_paths;
}
protected function getRouteForResourceMethod(RestResourceConfigInterface $resource_config, $method) {
if ($this
->isEntityResource($resource_config)) {
$route_name = 'rest.' . $resource_config
->id() . ".{$method}";
$routes = $this->routingProvider
->getRoutesByNames([
$route_name,
]);
if (empty($routes)) {
$formats = $resource_config
->getFormats($method);
if (count($formats) > 0) {
$route_name .= ".{$formats[0]}";
$routes = $this->routingProvider
->getRoutesByNames([
$route_name,
]);
}
}
if ($routes) {
return array_pop($routes);
}
}
else {
$resource_plugin = $resource_config
->getResourcePlugin();
foreach ($resource_plugin
->routes() as $route) {
$methods = $route
->getMethods();
if (array_search($method, $methods) !== FALSE) {
return $route;
}
}
}
throw new \Exception("No route found for REST resource, {$resource_config->id()}, for method {$method}");
}
protected function getErrorResponses() {
$responses['400'] = [
'description' => 'Bad request',
'schema' => [
'type' => 'object',
'properties' => [
'error' => [
'type' => 'string',
'example' => 'Bad data',
],
],
],
];
$responses['500'] = [
'description' => 'Internal server error.',
'schema' => [
'type' => 'object',
'properties' => [
'message' => [
'type' => 'string',
'example' => 'Internal server error.',
],
],
],
];
return $responses;
}
protected function getEntityParameters(EntityTypeInterface $entity_type, $method, $bundle_name = NULL) {
$parameters = [];
if (in_array($method, [
'GET',
'DELETE',
'PATCH',
])) {
$keys = $entity_type
->getKeys();
if ($entity_type instanceof ConfigEntityTypeInterface) {
$key_type = 'string';
}
else {
if ($entity_type instanceof FieldableEntityInterface) {
$key_field = $this->fieldManager
->getFieldStorageDefinitions($entity_type
->id())[$keys['id']];
$key_type = $key_field
->getType();
}
else {
$key_type = 'string';
}
}
$parameters[] = [
'name' => $entity_type
->id(),
'in' => 'path',
'required' => TRUE,
'type' => $key_type,
'description' => $this
->t('The @id,id, of the @type.', [
'@id' => $keys['id'],
'@type' => $entity_type
->id(),
]),
];
}
if (in_array($method, [
'POST',
'PATCH',
])) {
$parameters[] = [
'name' => 'body',
'in' => 'body',
'description' => $this
->t('The @label object', [
'@label' => $entity_type
->getLabel(),
]),
'required' => TRUE,
'schema' => [
'$ref' => '#/definitions/' . $this
->getEntityDefinitionKey($entity_type
->id(), $bundle_name),
],
];
}
return $parameters;
}
protected function getRouteParameters(Route $route) {
$parameters = [];
$vars = $route
->compile()
->getPathVariables();
foreach ($vars as $var) {
$parameters[] = [
'name' => $var,
'type' => 'string',
'in' => 'path',
'required' => TRUE,
];
}
return $parameters;
}
public function getResourceSecurity(RestResourceConfigInterface $resource_config, $method, array $formats) {
$security = [];
foreach ($resource_config
->getAuthenticationProviders($method) as $auth) {
switch ($auth) {
case 'basic_auth':
case 'cookie':
case 'oauth':
case 'oauth2':
$security[] = [
$auth => [],
];
break;
}
}
if ($this
->isEntityResource($resource_config)) {
$route_name = 'rest.' . $resource_config
->id() . ".{$method}";
$routes = $this->routingProvider
->getRoutesByNames([
$route_name,
]);
if (empty($routes) && count($formats) > 1) {
$route_name .= ".{$formats[0]}";
$routes = $this->routingProvider
->getRoutesByNames([
$route_name,
]);
}
if ($routes) {
$route = array_pop($routes);
if ($route
->getRequirement('_csrf_request_header_token')) {
$security[] = [
'csrf_token' => [],
];
}
}
}
return $security;
}
public function getApiName() {
return $this
->t('REST API');
}
protected function getApiDescription() {
return $this
->t('The REST API provide by the core REST module.');
}
protected function getMethodSupportedFormats($method, RestResourceConfigInterface $resource_config) {
if (empty($method)) {
return [];
}
$route_id = "rest.{$resource_config->id()}.{$method}";
$routes = $this->routingProvider
->getRoutesByNames([
$route_id,
]);
if (!empty($routes) && array_key_exists($route_id, $routes) && ($formats = $routes[$route_id]
->getRequirement('_format'))) {
return explode('|', $formats);
}
return $resource_config
->getFormats($method);
}
protected function generateMimeTypesFromFormats(array $formats) {
$mime_types = [];
foreach ($formats as $format) {
$mime_types[] = 'application/' . preg_replace('/_/', '+', trim(strtolower($format)));
}
return $mime_types;
}
protected function getRestSupportedFormats() {
static $supported_formats = [];
if (empty($supported_formats)) {
$resource_configs = $this
->getResourceConfigs($this
->getOptions());
if (empty($resource_configs)) {
return [];
}
foreach ($resource_configs as $resource_config) {
$resource_plugin = $resource_config
->getResourcePlugin();
foreach ($resource_config
->getMethods() as $method) {
$formats = $this
->getMethodSupportedFormats($method, $resource_config);
$supported_formats = array_unique(array_merge($supported_formats, $formats), SORT_REGULAR);
}
}
}
return $supported_formats;
}
}