Issue #1850734 by klausi: Make serialization formats configurable.
parent
00dfa02242
commit
73f3973425
|
@ -54,15 +54,30 @@ class RouteSubscriber implements EventSubscriberInterface {
|
||||||
public function dynamicRoutes(RouteBuildEvent $event) {
|
public function dynamicRoutes(RouteBuildEvent $event) {
|
||||||
|
|
||||||
$collection = $event->getRouteCollection();
|
$collection = $event->getRouteCollection();
|
||||||
|
$enabled_resources = $this->config->get('rest.settings')->load()->get('resources');
|
||||||
|
|
||||||
$resources = $this->config->get('rest.settings')->load()->get('resources');
|
// Iterate over all enabled resource plugins.
|
||||||
if ($resources && $enabled = array_intersect_key($this->manager->getDefinitions(), $resources)) {
|
foreach ($enabled_resources as $id => $enabled_methods) {
|
||||||
foreach ($enabled as $key => $resource) {
|
$plugin = $this->manager->getInstance(array('id' => $id));
|
||||||
$plugin = $this->manager->getInstance(array('id' => $key));
|
|
||||||
|
|
||||||
foreach ($plugin->routes() as $name => $route) {
|
foreach ($plugin->routes() as $name => $route) {
|
||||||
|
$method = $route->getRequirement('_method');
|
||||||
|
// Only expose routes where the method is enabled in the configuration.
|
||||||
|
if ($method && isset($enabled_methods[$method])) {
|
||||||
$route->setRequirement('_access_rest_csrf', 'TRUE');
|
$route->setRequirement('_access_rest_csrf', 'TRUE');
|
||||||
$collection->add("rest.$name", $route);
|
|
||||||
|
// If the array of configured format restrictions is empty for a
|
||||||
|
// method always add the route.
|
||||||
|
if (empty($enabled_methods[$method])) {
|
||||||
|
$collection->add("rest.$name", $route);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// If there is no format requirement or if it matches the
|
||||||
|
// configuration also add the route.
|
||||||
|
$format_requirement = $route->getRequirement('_format');
|
||||||
|
if (!$format_requirement || isset($enabled_methods[$method][$format_requirement])) {
|
||||||
|
$collection->add("rest.$name", $route);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,15 +26,11 @@ abstract class ResourceBase extends PluginBase implements ResourceInterface {
|
||||||
public function permissions() {
|
public function permissions() {
|
||||||
$permissions = array();
|
$permissions = array();
|
||||||
$definition = $this->getDefinition();
|
$definition = $this->getDefinition();
|
||||||
foreach ($this->requestMethods() as $method) {
|
foreach ($this->availableMethods() as $method) {
|
||||||
$lowered_method = strtolower($method);
|
$lowered_method = strtolower($method);
|
||||||
// Only expose permissions where the HTTP request method exists on the
|
$permissions["restful $lowered_method $this->plugin_id"] = array(
|
||||||
// plugin.
|
'title' => t('Access @method on %label resource', array('@method' => $method, '%label' => $definition['label'])),
|
||||||
if (method_exists($this, $lowered_method)) {
|
);
|
||||||
$permissions["restful $lowered_method $this->plugin_id"] = array(
|
|
||||||
'title' => t('Access @method on %label resource', array('@method' => $method, '%label' => $definition['label'])),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return $permissions;
|
return $permissions;
|
||||||
}
|
}
|
||||||
|
@ -47,38 +43,44 @@ abstract class ResourceBase extends PluginBase implements ResourceInterface {
|
||||||
$path_prefix = strtr($this->plugin_id, ':', '/');
|
$path_prefix = strtr($this->plugin_id, ':', '/');
|
||||||
$route_name = strtr($this->plugin_id, ':', '.');
|
$route_name = strtr($this->plugin_id, ':', '.');
|
||||||
|
|
||||||
$methods = $this->requestMethods();
|
$methods = $this->availableMethods();
|
||||||
foreach ($methods as $method) {
|
foreach ($methods as $method) {
|
||||||
$lower_method = strtolower($method);
|
$lower_method = strtolower($method);
|
||||||
// Only expose routes where the HTTP request method exists on the plugin.
|
$route = new Route("/$path_prefix/{id}", array(
|
||||||
if (method_exists($this, $lower_method)) {
|
'_controller' => 'Drupal\rest\RequestHandler::handle',
|
||||||
$route = new Route("/$path_prefix/{id}", array(
|
// Pass the resource plugin ID along as default property.
|
||||||
'_controller' => 'Drupal\rest\RequestHandler::handle',
|
'_plugin' => $this->plugin_id,
|
||||||
// Pass the resource plugin ID along as default property.
|
), array(
|
||||||
'_plugin' => $this->plugin_id,
|
// The HTTP method is a requirement for this route.
|
||||||
), array(
|
'_method' => $method,
|
||||||
// The HTTP method is a requirement for this route.
|
'_permission' => "restful $lower_method $this->plugin_id",
|
||||||
'_method' => $method,
|
));
|
||||||
'_permission' => "restful $lower_method $this->plugin_id",
|
|
||||||
));
|
|
||||||
|
|
||||||
switch ($method) {
|
switch ($method) {
|
||||||
case 'POST':
|
case 'POST':
|
||||||
// POST routes do not require an ID in the URL path.
|
// POST routes do not require an ID in the URL path.
|
||||||
$route->setPattern("/$path_prefix");
|
$route->setPattern("/$path_prefix");
|
||||||
$route->addDefaults(array('id' => NULL));
|
$route->addDefaults(array('id' => NULL));
|
||||||
break;
|
$collection->add("$route_name.$method", $route);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'GET':
|
case 'GET':
|
||||||
case 'HEAD':
|
case 'HEAD':
|
||||||
// Restrict GET and HEAD requests to the media type specified in the
|
// Restrict GET and HEAD requests to the media type specified in the
|
||||||
// HTTP Accept headers.
|
// HTTP Accept headers.
|
||||||
// @todo Replace hard coded format here with available formats.
|
$formats = drupal_container()->getParameter('serializer.formats');
|
||||||
$route->addRequirements(array('_format' => 'drupal_jsonld'));
|
foreach ($formats as $format_name => $label) {
|
||||||
break;
|
// Expose one route per available format.
|
||||||
}
|
//$format_route = new Route($route->getPattern(), $route->getDefaults(), $route->getRequirements());
|
||||||
|
$format_route = clone $route;
|
||||||
|
$format_route->addRequirements(array('_format' => $format_name));
|
||||||
|
$collection->add("$route_name.$method.$format_name", $format_route);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
$collection->add("$route_name.$method", $route);
|
default:
|
||||||
|
$collection->add("$route_name.$method", $route);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +97,7 @@ abstract class ResourceBase extends PluginBase implements ResourceInterface {
|
||||||
* The list of allowed HTTP request method strings.
|
* The list of allowed HTTP request method strings.
|
||||||
*/
|
*/
|
||||||
protected function requestMethods() {
|
protected function requestMethods() {
|
||||||
return drupal_map_assoc(array(
|
return array(
|
||||||
'HEAD',
|
'HEAD',
|
||||||
'GET',
|
'GET',
|
||||||
'POST',
|
'POST',
|
||||||
|
@ -105,6 +107,21 @@ abstract class ResourceBase extends PluginBase implements ResourceInterface {
|
||||||
'OPTIONS',
|
'OPTIONS',
|
||||||
'CONNECT',
|
'CONNECT',
|
||||||
'PATCH',
|
'PATCH',
|
||||||
));
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements ResourceInterface::availableMethods().
|
||||||
|
*/
|
||||||
|
public function availableMethods() {
|
||||||
|
$methods = $this->requestMethods();
|
||||||
|
$available = array();
|
||||||
|
foreach ($methods as $method) {
|
||||||
|
// Only expose methods where the HTTP request method exists on the plugin.
|
||||||
|
if (method_exists($this, strtolower($method))) {
|
||||||
|
$available[] = $method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $available;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,4 +36,12 @@ interface ResourceInterface extends PluginInspectionInterface {
|
||||||
*/
|
*/
|
||||||
public function permissions();
|
public function permissions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the available HTTP request methods on this plugin.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* The list of supported methods. Example: array('GET', 'POST', 'PATCH').
|
||||||
|
*/
|
||||||
|
public function availableMethods();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,12 +50,7 @@ class DBLogResource extends ResourceBase {
|
||||||
$record = db_query("SELECT * FROM {watchdog} WHERE wid = :wid", array(':wid' => $id))
|
$record = db_query("SELECT * FROM {watchdog} WHERE wid = :wid", array(':wid' => $id))
|
||||||
->fetchObject();
|
->fetchObject();
|
||||||
if (!empty($record)) {
|
if (!empty($record)) {
|
||||||
// Serialization is done here, so we indicate with NULL that there is no
|
return new ResourceResponse((array) $record);
|
||||||
// subsequent serialization necessary.
|
|
||||||
$response = new ResourceResponse(NULL, 200, array('Content-Type' => 'application/vnd.drupal.ld+json'));
|
|
||||||
// @todo remove hard coded format here.
|
|
||||||
$response->setContent(drupal_json_encode($record));
|
|
||||||
return $response;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NotFoundHttpException(t('Log entry with ID @id was not found', array('@id' => $id)));
|
throw new NotFoundHttpException(t('Log entry with ID @id was not found', array('@id' => $id)));
|
||||||
|
|
|
@ -12,6 +12,7 @@ use Symfony\Component\DependencyInjection\ContainerAware;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
|
||||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,17 +44,28 @@ class RequestHandler extends ContainerAware {
|
||||||
$received = $request->getContent();
|
$received = $request->getContent();
|
||||||
$unserialized = NULL;
|
$unserialized = NULL;
|
||||||
if (!empty($received)) {
|
if (!empty($received)) {
|
||||||
$definition = $resource->getDefinition();
|
$format = $request->getContentType();
|
||||||
$class = $definition['serialization_class'];
|
|
||||||
try {
|
// Only allow serialization formats that are explicitly configured. If no
|
||||||
// @todo Replace the format here with something we get from the HTTP
|
// formats are configured allow all and hope that the serializer knows the
|
||||||
// Content-type header. See http://drupal.org/node/1850704
|
// format. If the serializer cannot handle it an exception will be thrown
|
||||||
$unserialized = $serializer->deserialize($received, $class, 'drupal_jsonld');
|
// that bubbles up to the client.
|
||||||
|
$config = $this->container->get('config.factory')->get('rest.settings')->get('resources');
|
||||||
|
$enabled_formats = $config[$plugin][$request->getMethod()];
|
||||||
|
if (empty($enabled_formats) || isset($enabled_formats[$format])) {
|
||||||
|
$definition = $resource->getDefinition();
|
||||||
|
$class = $definition['serialization_class'];
|
||||||
|
try {
|
||||||
|
$unserialized = $serializer->deserialize($received, $class, $format);
|
||||||
|
}
|
||||||
|
catch (UnexpectedValueException $e) {
|
||||||
|
$error['error'] = $e->getMessage();
|
||||||
|
$content = $serializer->serialize($error, $format);
|
||||||
|
return new Response($content, 400, array('Content-Type' => $request->getMimeType($format)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (UnexpectedValueException $e) {
|
else {
|
||||||
$error['error'] = $e->getMessage();
|
throw new UnsupportedMediaTypeHttpException();
|
||||||
$content = $serializer->serialize($error, 'drupal_jsonld');
|
|
||||||
return new Response($content, 400, array('Content-Type' => 'application/vnd.drupal.ld+json'));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,21 +75,26 @@ class RequestHandler extends ContainerAware {
|
||||||
}
|
}
|
||||||
catch (HttpException $e) {
|
catch (HttpException $e) {
|
||||||
$error['error'] = $e->getMessage();
|
$error['error'] = $e->getMessage();
|
||||||
$content = $serializer->serialize($error, 'drupal_jsonld');
|
$format = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)->getRequirement('_format') ?: 'drupal_jsonld';
|
||||||
|
$content = $serializer->serialize($error, $format);
|
||||||
// Add the default content type, but only if the headers from the
|
// Add the default content type, but only if the headers from the
|
||||||
// exception have not specified it already.
|
// exception have not specified it already.
|
||||||
$headers = $e->getHeaders() + array('Content-Type' => 'application/vnd.drupal.ld+json');
|
$headers = $e->getHeaders() + array('Content-Type' => $request->getMimeType($format));
|
||||||
return new Response($content, $e->getStatusCode(), $headers);
|
return new Response($content, $e->getStatusCode(), $headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize the outgoing data for the response, if available.
|
// Serialize the outgoing data for the response, if available.
|
||||||
$data = $response->getResponseData();
|
$data = $response->getResponseData();
|
||||||
if ($data != NULL) {
|
if ($data != NULL) {
|
||||||
// @todo Replace the format here with something we get from the HTTP
|
// All REST routes are restricted to exactly one format, so instead of
|
||||||
// Accept headers. See http://drupal.org/node/1833440
|
// parsing it out of the Accept headers again we can simply retrieve the
|
||||||
$output = $serializer->serialize($data, 'drupal_jsonld');
|
// format requirement. If there is no format associated just pick Drupal
|
||||||
|
// JSON-LD.
|
||||||
|
$format = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)->getRequirement('_format') ?: 'drupal_jsonld';
|
||||||
|
|
||||||
|
$output = $serializer->serialize($data, $format);
|
||||||
$response->setContent($output);
|
$response->setContent($output);
|
||||||
$response->headers->set('Content-Type', 'application/vnd.drupal.ld+json');
|
$response->headers->set('Content-Type', $request->getMimeType($format));
|
||||||
}
|
}
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ class CreateTest extends RESTTestBase {
|
||||||
// entity types here as well.
|
// entity types here as well.
|
||||||
$entity_type = 'entity_test';
|
$entity_type = 'entity_test';
|
||||||
|
|
||||||
$this->enableService('entity:' . $entity_type);
|
$this->enableService('entity:' . $entity_type, 'POST');
|
||||||
// Create a user account that has the required permissions to create
|
// Create a user account that has the required permissions to create
|
||||||
// resources via the web API.
|
// resources via the web API.
|
||||||
$account = $this->drupalCreateUser(array('restful post entity:' . $entity_type));
|
$account = $this->drupalCreateUser(array('restful post entity:' . $entity_type));
|
||||||
|
|
|
@ -36,7 +36,7 @@ class DeleteTest extends RESTTestBase {
|
||||||
// Define the entity types we want to test.
|
// Define the entity types we want to test.
|
||||||
$entity_types = array('entity_test', 'node', 'user');
|
$entity_types = array('entity_test', 'node', 'user');
|
||||||
foreach ($entity_types as $entity_type) {
|
foreach ($entity_types as $entity_type) {
|
||||||
$this->enableService('entity:' . $entity_type);
|
$this->enableService('entity:' . $entity_type, 'DELETE');
|
||||||
// Create a user account that has the required permissions to delete
|
// Create a user account that has the required permissions to delete
|
||||||
// resources via the web API.
|
// resources via the web API.
|
||||||
$account = $this->drupalCreateUser(array('restful delete entity:' . $entity_type));
|
$account = $this->drupalCreateUser(array('restful delete entity:' . $entity_type));
|
||||||
|
|
|
@ -156,18 +156,24 @@ abstract class RESTTestBase extends WebTestBase {
|
||||||
* @param string|FALSE $resource_type
|
* @param string|FALSE $resource_type
|
||||||
* The resource type that should get web API enabled or FALSE to disable all
|
* The resource type that should get web API enabled or FALSE to disable all
|
||||||
* resource types.
|
* resource types.
|
||||||
|
* @param string $method
|
||||||
|
* The HTTP method to enable, e.g. GET, POST etc.
|
||||||
|
* @param string $format
|
||||||
|
* (Optional) The serialization format, e.g. jsonld.
|
||||||
*/
|
*/
|
||||||
protected function enableService($resource_type) {
|
protected function enableService($resource_type, $method = 'GET', $format = NULL) {
|
||||||
// Enable web API for this entity type.
|
// Enable web API for this entity type.
|
||||||
$config = config('rest.settings');
|
$config = config('rest.settings');
|
||||||
|
$settings = array();
|
||||||
if ($resource_type) {
|
if ($resource_type) {
|
||||||
$config->set('resources', array(
|
if ($format) {
|
||||||
$resource_type => $resource_type,
|
$settings[$resource_type][$method][$format] = 'TRUE';
|
||||||
));
|
}
|
||||||
}
|
else {
|
||||||
else {
|
$settings[$resource_type][$method] = array();
|
||||||
$config->set('resources', array());
|
}
|
||||||
}
|
}
|
||||||
|
$config->set('resources', $settings);
|
||||||
$config->save();
|
$config->save();
|
||||||
|
|
||||||
// Rebuild routing cache, so that the web API paths are available.
|
// Rebuild routing cache, so that the web API paths are available.
|
||||||
|
|
|
@ -38,7 +38,7 @@ class ReadTest extends RESTTestBase {
|
||||||
// Define the entity types we want to test.
|
// Define the entity types we want to test.
|
||||||
$entity_types = array('entity_test');
|
$entity_types = array('entity_test');
|
||||||
foreach ($entity_types as $entity_type) {
|
foreach ($entity_types as $entity_type) {
|
||||||
$this->enableService('entity:' . $entity_type);
|
$this->enableService('entity:' . $entity_type, 'GET');
|
||||||
// Create a user account that has the required permissions to delete
|
// Create a user account that has the required permissions to delete
|
||||||
// resources via the web API.
|
// resources via the web API.
|
||||||
$account = $this->drupalCreateUser(array('restful get entity:' . $entity_type));
|
$account = $this->drupalCreateUser(array('restful get entity:' . $entity_type));
|
||||||
|
@ -57,12 +57,8 @@ class ReadTest extends RESTTestBase {
|
||||||
$this->assertEqual($data['uuid'][LANGUAGE_DEFAULT][0]['value'], $entity->uuid(), 'Entity UUID is correct');
|
$this->assertEqual($data['uuid'][LANGUAGE_DEFAULT][0]['value'], $entity->uuid(), 'Entity UUID is correct');
|
||||||
|
|
||||||
// Try to read the entity with an unsupported mime format.
|
// Try to read the entity with an unsupported mime format.
|
||||||
// Because the matcher checks mime type first, then method, this will hit
|
|
||||||
// zero viable routes on the method. If the mime matcher wasn't working,
|
|
||||||
// we would still find an existing GET route with the wrong format. That
|
|
||||||
// means this is a valid functional test for mime-matching.
|
|
||||||
$response = $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'GET', NULL, 'application/wrongformat');
|
$response = $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'GET', NULL, 'application/wrongformat');
|
||||||
$this->assertResponse(405);
|
$this->assertResponse(415);
|
||||||
|
|
||||||
// Try to read an entity that does not exist.
|
// Try to read an entity that does not exist.
|
||||||
$response = $this->httpRequest('entity/' . $entity_type . '/9999', 'GET', NULL, 'application/vnd.drupal.ld+json');
|
$response = $this->httpRequest('entity/' . $entity_type . '/9999', 'GET', NULL, 'application/vnd.drupal.ld+json');
|
||||||
|
|
|
@ -38,7 +38,7 @@ class UpdateTest extends RESTTestBase {
|
||||||
// entity types here as well.
|
// entity types here as well.
|
||||||
$entity_type = 'entity_test';
|
$entity_type = 'entity_test';
|
||||||
|
|
||||||
$this->enableService('entity:' . $entity_type);
|
$this->enableService('entity:' . $entity_type, 'PATCH');
|
||||||
// Create a user account that has the required permissions to create
|
// Create a user account that has the required permissions to create
|
||||||
// resources via the web API.
|
// resources via the web API.
|
||||||
$account = $this->drupalCreateUser(array('restful patch entity:' . $entity_type));
|
$account = $this->drupalCreateUser(array('restful patch entity:' . $entity_type));
|
||||||
|
@ -103,7 +103,7 @@ class UpdateTest extends RESTTestBase {
|
||||||
// entity types here as well.
|
// entity types here as well.
|
||||||
$entity_type = 'entity_test';
|
$entity_type = 'entity_test';
|
||||||
|
|
||||||
$this->enableService('entity:' . $entity_type);
|
$this->enableService('entity:' . $entity_type, 'PUT');
|
||||||
// Create a user account that has the required permissions to create
|
// Create a user account that has the required permissions to create
|
||||||
// resources via the web API.
|
// resources via the web API.
|
||||||
$account = $this->drupalCreateUser(array('restful put entity:' . $entity_type));
|
$account = $this->drupalCreateUser(array('restful put entity:' . $entity_type));
|
||||||
|
|
|
@ -34,7 +34,10 @@ function rest_admin_form($form, &$form_state) {
|
||||||
}
|
}
|
||||||
asort($entity_resources);
|
asort($entity_resources);
|
||||||
asort($other_resources);
|
asort($other_resources);
|
||||||
$enabled_resources = config('rest.settings')->get('resources') ?: array();
|
$config = config('rest.settings')->get('resources') ?: array();
|
||||||
|
// Strip out the nested method configuration, we are only interested in the
|
||||||
|
// plugin IDs of the resources.
|
||||||
|
$enabled_resources = drupal_map_assoc(array_keys($config));
|
||||||
|
|
||||||
// Render the output using table_select().
|
// Render the output using table_select().
|
||||||
$header = array(
|
$header = array(
|
||||||
|
@ -79,9 +82,19 @@ function rest_admin_form($form, &$form_state) {
|
||||||
* Form submission handler for rest_admin_form().
|
* Form submission handler for rest_admin_form().
|
||||||
*/
|
*/
|
||||||
function rest_admin_form_submit($form, &$form_state) {
|
function rest_admin_form_submit($form, &$form_state) {
|
||||||
$resources = array_filter($form_state['values']['entity_resources']);
|
$enabled_resources = array_filter($form_state['values']['entity_resources']);
|
||||||
if (!empty($form_state['values']['other_resources'])) {
|
if (!empty($form_state['values']['other_resources'])) {
|
||||||
$resources += array_filter($form_state['values']['other_resources']);
|
$enabled_resources += array_filter($form_state['values']['other_resources']);
|
||||||
|
}
|
||||||
|
$resources = array();
|
||||||
|
$plugin_manager = drupal_container()->get('plugin.manager.rest');
|
||||||
|
|
||||||
|
// Enable all methods and all formats for each selected resource.
|
||||||
|
foreach ($enabled_resources as $resource) {
|
||||||
|
$plugin = $plugin_manager->getInstance(array('id' => $resource));
|
||||||
|
$methods = $plugin->availableMethods();
|
||||||
|
// An empty array means all formats are allowed for a method.
|
||||||
|
$resources[$resource] = array_fill_keys($methods, array());
|
||||||
}
|
}
|
||||||
|
|
||||||
$config = config('rest.settings');
|
$config = config('rest.settings');
|
||||||
|
|
Loading…
Reference in New Issue