class RestGenerator in OpenAPI 8
Defines an OpenApi Schema Generator for the Rest module.
Plugin annotation
@OpenApiGenerator(
id = "rest",
label = @Translation("Rest"),
)
Hierarchy
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\openapi\Plugin\openapi\OpenApiGeneratorBase implements ContainerFactoryPluginInterface, OpenApiGeneratorInterface uses StringTranslationTrait
- class \Drupal\openapi\Plugin\openapi\OpenApiGenerator\RestGenerator uses RestInspectionTrait
- class \Drupal\openapi\Plugin\openapi\OpenApiGeneratorBase implements ContainerFactoryPluginInterface, OpenApiGeneratorInterface uses StringTranslationTrait
Expanded class hierarchy of RestGenerator
File
- src/
Plugin/ openapi/ OpenApiGenerator/ RestGenerator.php, line 30
Namespace
Drupal\openapi\Plugin\openapi\OpenApiGeneratorView source
class RestGenerator extends OpenApiGeneratorBase {
use RestInspectionTrait;
/**
* Return resources for non-entity resources.
*
* @return \Symfony\Component\HttpFoundation\JsonResponse
* A json response.
*/
public function nonBundleResourcesJson() {
/** @var \Drupal\rest\Entity\RestResourceConfig[] $resource_configs */
$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;
}
/**
* {@inheritdoc}
*/
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) {
// Remove any bundle schema property that is the same as the
// entity schema property.
unset($bundle_schema['properties'][$property_id]);
}
}
// Use Open API polymorphism support to show that bundles extend
// entity type.
// @todo Should base fields be removed from bundle schema?
// @todo Can base fields could be different from entity type base fields?
// @see hook_entity_bundle_field_info().
// @see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#models-with-polymorphism-support
$definitions[$this
->getEntityDefinitionKey($entity_type
->id(), $bundle)] = [
'allOf' => [
[
'$ref' => "#/definitions/{$entity_id}",
],
$bundle_schema,
],
];
}
}
}
}
return $definitions;
}
/**
* {@inheritdoc}
*/
public function getConsumes() {
return $this
->generateMimeTypesFromFormats($this
->getRestSupportedFormats());
}
/**
* {@inheritdoc}
*/
public function getProduces() {
return $this
->generateMimeTypesFromFormats($this
->getRestSupportedFormats());
}
/**
* Get tags.
*/
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;
}
/**
* {@inheritdoc}
*/
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) {
/** @var \Drupal\rest\Plugin\ResourceBase $plugin */
$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;
}
/**
* Gets the matching for route for the resource and method.
*
* @param \Drupal\rest\RestResourceConfigInterface $resource_config
* The REST config resource.
* @param string $method
* The HTTP method.
*
* @return \Symfony\Component\Routing\Route
* The route.
*
* @throws \Exception
* If no route is found.
*/
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}");
}
/**
* Get the error responses.
*
* @see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#responseObject
*
* @return array
* Keys are http codes. Values responses.
*/
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;
}
/**
* Get parameters for an entity type.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type.
* @param string $method
* The HTTP method.
* @param string $bundle_name
* The bundle name.
*
* @return array
* Parameters for the entity resource.
*/
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;
}
/**
* Get OpenAPI parameters for a route.
*
* @param \Symfony\Component\Routing\Route $route
* The route.
*
* @return array
* The resource 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;
}
/**
* Get the security information for the a resource.
*
* @param \Drupal\rest\RestResourceConfigInterface $resource_config
* The REST resource.
* @param string $method
* The HTTP method.
* @param string[] $formats
* The formats.
*
* @return array
* The security elements.
*
* @see http://swagger.io/specification/#securityDefinitionsObject
*/
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':
// @TODO: #2977109 - Calculate oauth scopes required.
$security[] = [
$auth => [],
];
break;
}
}
// @todo Handle tokens that need to be set in headers.
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);
// Check to see if route is protected by access checks in header.
if ($route
->getRequirement('_csrf_request_header_token')) {
$security[] = [
'csrf_token' => [],
];
}
}
}
return $security;
}
/**
* {@inheritdoc}
*/
public function getApiName() {
return $this
->t('REST API');
}
/**
* {@inheritdoc}
*/
protected function getApiDescription() {
return $this
->t('The REST API provide by the core REST module.');
}
/**
* List of supported Serializer Formats for HTTP Method on a Rest Resource.
*
* @param string $method
* The HTTP method for generating the MIME Types. Example: GET, POST, etc.
* @param \Drupal\rest\RestResourceConfigInterface $resource_config
* The resource configuration.
*
* @return array
* The list of MIME Types
*/
protected function getMethodSupportedFormats($method, RestResourceConfigInterface $resource_config) {
if (empty($method)) {
return [];
}
// The route ID.
$route_id = "rest.{$resource_config->id()}.{$method}";
// First Check the supported formats on the route level.
/** @var \Symfony\Component\Routing\Route[] $route */
$routes = $this->routingProvider
->getRoutesByNames([
$route_id,
]);
if (!empty($routes) && array_key_exists($route_id, $routes) && ($formats = $routes[$route_id]
->getRequirement('_format'))) {
return explode('|', $formats);
}
// If no route level format was found, lets use
// the RestResourceConfig formats for the given HTTP method.
return $resource_config
->getFormats($method);
}
/**
* Generate list of MIME Types based on a list of serializer formats.
*
* @param array $formats
* List of formats.
*
* @return array
* List of MIME Types based on $formats.
* The list is MIME Types are on the same order as the inserted $format
*/
protected function generateMimeTypesFromFormats(array $formats) {
$mime_types = [];
foreach ($formats as $format) {
$mime_types[] = 'application/' . preg_replace('/_/', '+', trim(strtolower($format)));
}
return $mime_types;
}
/**
* Returns a list of supported Format on REST.
*
* @return array
* The list of supported formats.
*/
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) {
/** @var \Drupal\rest\Plugin\ResourceBase $plugin */
$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;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
OpenApiGeneratorBase:: |
protected | property | The configuration object factory. | |
OpenApiGeneratorBase:: |
protected | property | The configuration object factory. | |
OpenApiGeneratorBase:: |
static | property | Separator for using in definition id strings. | 1 |
OpenApiGeneratorBase:: |
protected | property | The entity type manager. | |
OpenApiGeneratorBase:: |
protected | property | The Field Manager. | |
OpenApiGeneratorBase:: |
public | property | The generator label. | |
OpenApiGeneratorBase:: |
protected | property | The request options parameter. | |
OpenApiGeneratorBase:: |
protected | property | The configuration object factory. | |
OpenApiGeneratorBase:: |
protected | property | The route provider. | |
OpenApiGeneratorBase:: |
protected | property | The Schemata SchemaFactory. | |
OpenApiGeneratorBase:: |
protected | property | The serializer. | |
OpenApiGeneratorBase:: |
protected | function | Cleans JSON schema definitions for OpenAPI. | |
OpenApiGeneratorBase:: |
public static | function |
Creates an instance of the plugin. Overrides ContainerFactoryPluginInterface:: |
1 |
OpenApiGeneratorBase:: |
protected | function | Check whether a definitions exists for a key. | |
OpenApiGeneratorBase:: |
protected | function | Fix default field value as zero instead of FALSE. | |
OpenApiGeneratorBase:: |
public | function |
Get base path for schema. Overrides OpenApiGeneratorInterface:: |
1 |
OpenApiGeneratorBase:: |
protected | function | Gets the reference to the definition in the document. | |
OpenApiGeneratorBase:: |
protected | function | Gets the entity definition key. | 1 |
OpenApiGeneratorBase:: |
protected | function | Get possible responses for an entity type. | |
OpenApiGeneratorBase:: |
public | function |
Get plugin id. Overrides OpenApiGeneratorInterface:: |
|
OpenApiGeneratorBase:: |
protected | function | Creates the 'info' portion of the API. | |
OpenApiGeneratorBase:: |
protected | function | Gets the JSON Schema for an entity type or entity type and bundle. | |
OpenApiGeneratorBase:: |
public | function |
Get plugin label. Overrides OpenApiGeneratorInterface:: |
|
OpenApiGeneratorBase:: |
public | function |
Get the options for the current schema download. Overrides OpenApiGeneratorInterface:: |
|
OpenApiGeneratorBase:: |
public | function |
Returns a list of valid security types for the api. Overrides OpenApiGeneratorInterface:: |
|
OpenApiGeneratorBase:: |
public | function |
Get a list a valid security method definitions. Overrides OpenApiGeneratorInterface:: |
|
OpenApiGeneratorBase:: |
public | function |
Generates OpenAPI specification. Overrides OpenApiGeneratorInterface:: |
|
OpenApiGeneratorBase:: |
protected | function | Determines if an entity type and/or bundle show be included. | |
OpenApiGeneratorBase:: |
public | function |
Set the options for the current schema download. Overrides OpenApiGeneratorInterface:: |
|
OpenApiGeneratorBase:: |
public | function |
OpenApiGeneratorBase constructor. Overrides PluginBase:: |
1 |
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 1 |
PluginBase:: |
protected | property | The plugin implementation definition. | 1 |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
3 |
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function | Determines if the plugin is configurable. | |
RestGenerator:: |
protected | function | Generate list of MIME Types based on a list of serializer formats. | |
RestGenerator:: |
protected | function |
Gets API description. Overrides OpenApiGeneratorBase:: |
|
RestGenerator:: |
public | function |
Gets the API name. Overrides OpenApiGeneratorBase:: |
|
RestGenerator:: |
public | function |
Get a list of all MIME Type that the API Consumes Overrides OpenApiGeneratorBase:: |
|
RestGenerator:: |
public | function |
Get model definitions for Drupal entities and bundles. Overrides OpenApiGeneratorBase:: |
|
RestGenerator:: |
protected | function | Get parameters for an entity type. | |
RestGenerator:: |
protected | function | Get the error responses. | |
RestGenerator:: |
protected | function | List of supported Serializer Formats for HTTP Method on a Rest Resource. | |
RestGenerator:: |
public | function |
Returns the paths information. Overrides OpenApiGeneratorBase:: |
|
RestGenerator:: |
public | function |
Get a list of all MIME Type that the API Produces Overrides OpenApiGeneratorBase:: |
|
RestGenerator:: |
public | function | Get the security information for the a resource. | |
RestGenerator:: |
protected | function | Returns a list of supported Format on REST. | |
RestGenerator:: |
protected | function | Gets the matching for route for the resource and method. | |
RestGenerator:: |
protected | function | Get OpenAPI parameters for a route. | |
RestGenerator:: |
public | function |
Get tags. Overrides OpenApiGeneratorBase:: |
|
RestGenerator:: |
public | function | Return resources for non-entity resources. | |
RestInspectionTrait:: |
protected | function | Gets the entity type if any for REST resource. | |
RestInspectionTrait:: |
protected | function | Gets the REST config resources. | |
RestInspectionTrait:: |
protected | function | Gets entity types that are enabled for rest. | |
RestInspectionTrait:: |
protected | function | Determines if an REST resource is for an entity. | |
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |