Skip to content
Snippets Groups Projects
Commit afb7be83 authored by Carlos Gratacos's avatar Carlos Gratacos Committed by Rich Gerdes
Browse files

Issue #2940817 by richgerdes, cjgratacos: Add consumer and producers to schema

parent 9fc8e72a
No related branches found
No related tags found
No related merge requests found
......@@ -451,6 +451,24 @@ class JsonApiGenerator extends OpenApiGeneratorBase {
return $tags;
}
/**
* {@inheritdoc}
*/
public function getConsumes() {
return [
'application/vdn.api+json',
];
}
/**
* {@inheritdoc}
*/
public function getProduces() {
return [
'application/vdn.api+json',
];
}
/**
* Get the tag to use for a bundle.
*
......
......@@ -13,7 +13,6 @@ use Drupal\Core\Routing\RouteProviderInterface;
use Drupal\openapi\RestInspectionTrait;
use Drupal\rest\Plugin\Type\ResourcePluginManager;
use Drupal\rest\RestResourceConfigInterface;
use Drupal\rest\Plugin\rest\resource\EntityResource;
use Drupal\schemata\SchemaFactory;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RequestStack;
......@@ -33,6 +32,13 @@ class RestGenerator extends OpenApiGeneratorBase {
use RestInspectionTrait;
/**
* The Rest Resource Plugin Manager.
*
* @var \Drupal\rest\Plugin\Type\ResourcePluginManager
*/
private $manager;
/**
* RestGenerator constructor.
*
......@@ -162,6 +168,20 @@ class RestGenerator extends OpenApiGeneratorBase {
return $definitions;
}
/**
* {@inheritdoc}
*/
public function getConsumes() {
return $this->generateMimeTypesFromFormats($this->getRestSupportedFormats());
}
/**
* {@inheritdoc}
*/
public function getProduces() {
return $this->generateMimeTypesFromFormats($this->getRestSupportedFormats());
}
/**
* Gets available security definitions.
*
......@@ -222,7 +242,7 @@ class RestGenerator extends OpenApiGeneratorBase {
$open_api_method = strtolower($method);
$path = $route->getPath();
$path_method_spec = [];
$formats = $resource_config->getFormats($method);
$formats = $this->getMethodSupportedFormats($method, $resource_config);
$format_parameter = [
'name' => '_format',
'in' => 'query',
......@@ -245,7 +265,6 @@ class RestGenerator extends OpenApiGeneratorBase {
'@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'];
}
......@@ -483,4 +502,77 @@ class RestGenerator extends OpenApiGeneratorBase {
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;
}
}
......@@ -195,6 +195,8 @@ abstract class OpenApiGeneratorBase extends PluginBase implements OpenApiGenerat
'securityDefinitions' => $this->getSecurityDefinitions(),
'tags' => $this->getTags(),
'definitions' => $this->getDefinitions(),
'consumes' => $this->getConsumes(),
'produces' => $this->getProduces(),
'paths' => $this->getPaths(),
];
return $spec;
......@@ -258,6 +260,20 @@ abstract class OpenApiGeneratorBase extends PluginBase implements OpenApiGenerat
return [];
}
/**
* {@inheritdoc}
*/
public function getConsumes() {
return [];
}
/**
* {@inheritdoc}
*/
public function getProduces() {
return [];
}
/**
* Gets the JSON Schema for an entity type or entity type and bundle.
*
......
......@@ -112,4 +112,20 @@ interface OpenApiGeneratorInterface {
*/
public function getDefinitions();
/**
* Get a list of all MIME Type that the API Consumes
*
* @return array
* An array of all MIME Type that the API Consumes
*/
public function getConsumes();
/**
* Get a list of all MIME Type that the API Produces
*
* @return array
* An array of all MIME Type that the API Produces
*/
public function getProduces();
}
......@@ -862,11 +862,11 @@
"in": "query",
"type": "string",
"enum": [
"json"
"json",
"hal_json"
],
"required": true,
"description": "Request format",
"default": "json"
"description": "Request format"
},
{
"name": "openapi_test_entity",
......@@ -918,11 +918,11 @@
"in": "query",
"type": "string",
"enum": [
"json"
"json",
"hal_json"
],
"required": true,
"description": "Request format",
"default": "json"
"description": "Request format"
},
{
"name": "openapi_test_entity",
......@@ -977,11 +977,11 @@
"in": "query",
"type": "string",
"enum": [
"json"
"json",
"hal_json"
],
"required": true,
"description": "Request format",
"default": "json"
"description": "Request format"
},
{
"name": "openapi_test_entity",
......@@ -1032,11 +1032,11 @@
"in": "query",
"type": "string",
"enum": [
"json"
"json",
"hal_json"
],
"required": true,
"description": "Request format",
"default": "json"
"description": "Request format"
},
{
"name": "body",
......
......@@ -143,11 +143,11 @@
"in": "query",
"type": "string",
"enum": [
"json"
"json",
"hal_json"
],
"required": true,
"description": "Request format",
"default": "json"
"description": "Request format"
},
{
"name": "openapi_test_entity",
......@@ -199,11 +199,11 @@
"in": "query",
"type": "string",
"enum": [
"json"
"json",
"hal_json"
],
"required": true,
"description": "Request format",
"default": "json"
"description": "Request format"
},
{
"name": "openapi_test_entity",
......@@ -258,11 +258,11 @@
"in": "query",
"type": "string",
"enum": [
"json"
"json",
"hal_json"
],
"required": true,
"description": "Request format",
"default": "json"
"description": "Request format"
},
{
"name": "openapi_test_entity",
......@@ -313,11 +313,11 @@
"in": "query",
"type": "string",
"enum": [
"json"
"json",
"hal_json"
],
"required": true,
"description": "Request format",
"default": "json"
"description": "Request format"
},
{
"name": "body",
......
......@@ -184,11 +184,11 @@
"in": "query",
"type": "string",
"enum": [
"json"
"json",
"hal_json"
],
"required": true,
"description": "Request format",
"default": "json"
"description": "Request format"
},
{
"name": "openapi_test_entity",
......@@ -240,11 +240,11 @@
"in": "query",
"type": "string",
"enum": [
"json"
"json",
"hal_json"
],
"required": true,
"description": "Request format",
"default": "json"
"description": "Request format"
},
{
"name": "openapi_test_entity",
......@@ -299,11 +299,11 @@
"in": "query",
"type": "string",
"enum": [
"json"
"json",
"hal_json"
],
"required": true,
"description": "Request format",
"default": "json"
"description": "Request format"
},
{
"name": "openapi_test_entity",
......@@ -354,11 +354,11 @@
"in": "query",
"type": "string",
"enum": [
"json"
"json",
"hal_json"
],
"required": true,
"description": "Request format",
"default": "json"
"description": "Request format"
},
{
"name": "body",
......
......@@ -143,11 +143,11 @@
"in": "query",
"type": "string",
"enum": [
"json"
"json",
"hal_json"
],
"required": true,
"description": "Request format",
"default": "json"
"description": "Request format"
},
{
"name": "openapi_test_entity",
......@@ -199,11 +199,11 @@
"in": "query",
"type": "string",
"enum": [
"json"
"json",
"hal_json"
],
"required": true,
"description": "Request format",
"default": "json"
"description": "Request format"
},
{
"name": "openapi_test_entity",
......@@ -258,11 +258,11 @@
"in": "query",
"type": "string",
"enum": [
"json"
"json",
"hal_json"
],
"required": true,
"description": "Request format",
"default": "json"
"description": "Request format"
},
{
"name": "openapi_test_entity",
......@@ -313,11 +313,11 @@
"in": "query",
"type": "string",
"enum": [
"json"
"json",
"hal_json"
],
"required": true,
"description": "Request format",
"default": "json"
"description": "Request format"
},
{
"name": "body",
......
......@@ -41,6 +41,8 @@ class RequestTest extends BrowserTestBase {
'tags' => 'tags',
'definitions' => 'definitions',
'paths' => 'paths',
'consumes' => 'consumes',
'produces' => 'produces',
];
const ENTITY_TEST_BUNDLES = [
......@@ -105,16 +107,16 @@ class RequestTest extends BrowserTestBase {
}
foreach (array_filter(static::ENTITY_TEST_BUNDLES) as $entity_type => $bundles) {
// Add single value and multi value fields.
FieldStorageConfig::create([
'entity_type' => $entity_type,
'field_name' => 'field_test_' . $entity_type,
'type' => 'text',
])
->setCardinality(1)
->save();
// Add single value and multi value fields.
FieldStorageConfig::create([
'entity_type' => $entity_type,
'field_name' => 'field_test_' . $entity_type,
'type' => 'text',
])
->setCardinality(1)
->save();
foreach ($bundles as $bundle) {
// Add field to each bundle
// Add field to each bundle.
FieldConfig::create([
'entity_type' => $entity_type,
'field_name' => 'field_test_' . $entity_type,
......@@ -150,7 +152,10 @@ class RequestTest extends BrowserTestBase {
];
foreach ($enable_entity_types as $entity_type_id => $methods) {
foreach ($methods as $method) {
$this->enableRestService("entity:$entity_type_id", $method);
$this->enableRestService("entity:$entity_type_id", $method, 'json');
if ($entity_type_id === 'openapi_test_entity') {
$this->enableRestService("entity:$entity_type_id", $method, 'hal_json');
}
}
}
$this->container->get('router.builder')->rebuild();
......@@ -238,7 +243,8 @@ class RequestTest extends BrowserTestBase {
$this->assertTrue(!empty($decoded_response['schemes']), 'Schema for ' . $api_module . ' should define at least one scheme.');
// Test basePath and host.
$host = parse_url($this->baseUrl, PHP_URL_HOST);
$port = parse_url($this->baseUrl, PHP_URL_PORT);
$host = parse_url($this->baseUrl, PHP_URL_HOST) . ($port ? ':' . $port : '');
$this->assertEquals($host, $decoded_response['host'], 'Schema has invalid host.');
$basePath = $this->getBasePath();
$response_basePath = $decoded_response['basePath'];
......@@ -248,6 +254,24 @@ class RequestTest extends BrowserTestBase {
// Verify that with the subdirectory removed, that the basePath is correct.
$this->assertEquals($routeBase, ltrim($response_routeBase, '/'), 'Route base path is invalid.');
// Verify that root consumes and produces exists and is not empty.
foreach (['consumes', 'produces'] as $key) {
$this->assertArrayHasKey($key, $decoded_response, "Schema does not contains a root $key");
$this->assertNotEmpty($decoded_response[$key], "Schema has empty root $key");
if (!isset($decoded_response[$key])) {
if ($api_module == 'jsonapi') {
$this->assertEquals(['application/vdn.api+json'], $decoded_response[$key], "$api_module root $key should only contain application/vdn.api+json");
}
elseif ($api_module == 'rest') {
$rest_mimetypes = ['application/json'];
if (isset($options['entity_type_id']) && $options['entity_type_id'] === 'openapi_test_entity') {
$rest_mimetypes[] = 'application/hal+json';
}
$this->assertEquals($rest_mimetypes, $decoded_response[$key], "$api_module root $key should only contain " . implode(' and ', $rest_mimetypes));
}
}
}
/*
* Tags for rest schema define 'x-entity-type' to reference the entity type
* associated with the entity. This value should exist in the definitions.
......@@ -277,7 +301,7 @@ class RequestTest extends BrowserTestBase {
foreach ($methods as $method => &$method_schema) {
// Ensure all tags are defined.
$missing_tags = array_diff($method_schema['tags'], $tag_names);
$all_method_tags = array_merge($all_method_tags,$method_schema['tags']);
$all_method_tags = array_merge($all_method_tags, $method_schema['tags']);
$this->assertTrue(empty($missing_tags), 'Method ' . $method . ' for ' . $path . ' has invalid tag(s): ' . implode(', ', $missing_tags));
// The security and scheme indexes are not present for jsonapi.
......@@ -295,6 +319,21 @@ class RequestTest extends BrowserTestBase {
};
}
foreach (['consumes', 'produces'] as $key) {
if (isset($method_schema[$key]) && !empty($method_schema[$key])) {
// Filter out mimetypes that exist in parent.
$method_extra_mimetypes = array_diff($method_schema[$key], $decoded_response[$key]);
$this->assertEmpty($method_extra_mimetypes, 'Method ' . $method . ' for ' . $path . ' has invalid mime type(s): ' . implode(', ', $method_extra_mimetypes));
if ($api_module == 'rest') {
$rest_mimetypes = ['application/json'];
if (isset($options['entity_type_id']) && $options['entity_type_id'] === 'openapi_test_entity') {
$rest_mimetypes[] = 'application/hal+json';
}
$this->assertEquals($rest_mimetypes, $method_schema[$key], 'Entity type ' . $options['entity_type_id'] . ' should only have REST mimetype(s): ' . implode(', ', $rest_mimetypes));
}
}
}
// Remove all tested properties from method schema.
unset($method_schema['tags']);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment