diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/ResourceBase.php b/core/modules/rest/lib/Drupal/rest/Plugin/ResourceBase.php index 962af71..1da1b34 100644 --- a/core/modules/rest/lib/Drupal/rest/Plugin/ResourceBase.php +++ b/core/modules/rest/lib/Drupal/rest/Plugin/ResourceBase.php @@ -17,6 +17,13 @@ abstract class ResourceBase extends PluginBase { /** + * Holds all predefined HTTP methods and their configuration. + * + * @var array + */ + protected $reuqestMethods; + + /** * Provides an array of permissions suitable for hook_permission(). * * Every plugin operation method gets its own user permission. Example: @@ -29,7 +36,7 @@ public function permissions() { $permissions = array(); $definition = $this->getDefinition(); - foreach ($this->requestMethods() as $method) { + foreach ($this->requestMethods() as $method => $settings) { $lowered_method = strtolower($method); // Only expose permissions where the HTTP request method exists on the // plugin. @@ -55,7 +62,7 @@ public function routes() { $collection = new RouteCollection(); $methods = $this->requestMethods(); - foreach ($methods as $method) { + foreach ($methods as $method => $settings) { // Only expose routes where the HTTP request method exists on the plugin. if (method_exists($this, strtolower($method))) { $prefix = strtr($this->plugin_id, ':', '/'); @@ -83,22 +90,73 @@ public function routes() { * Provides predefined HTTP request methods. * * Plugins can override this method to provide additional custom request - * methods. + * methods or to alter the semantics. + * + * @param string $method + * Optional: a specific request method to return setting information for. * * @return array - * The list of allowed HTTP request method strings. + * An array with HTTP methods as keys and value arrays with the following + * key/value pairs: + * - success_code: an integer used for the HTTP response code on success. + * - incoming_data: a boolean to indicate that incoming data has to be + * de-serialized. + * - outgoing_data: a boolean to indicate that outgoing data has to be + * serialized. */ - protected function requestMethods() { - return drupal_map_assoc(array( - 'HEAD', - 'GET', - 'POST', - 'PUT', - 'DELETE', - 'TRACE', - 'OPTIONS', - 'CONNECT', - 'PATCH', - )); + public function requestMethods($method = NULL) { + if ($this->reuqestMethods == NULL) { + $this->reuqestMethods = array( + 'HEAD' => array( + 'success_code' => 200, + 'incoming_data' => FALSE, + 'outgoing_data' => FALSE, + ), + 'GET' => array( + 'success_code' => 200, + 'incoming_data' => FALSE, + 'outgoing_data' => TRUE, + ), + 'POST' => array( + 'success_code' => 201, + 'incoming_data' => TRUE, + 'outgoing_data' => FALSE, + ), + 'PUT' => array( + 'success_code' => 200, + 'incoming_data' => TRUE, + 'outgoing_data' => FALSE, + ), + 'DELETE' => array( + 'success_code' => 204, + 'incoming_data' => FALSE, + 'outgoing_data' => FALSE, + ), + 'TRACE' => array( + 'success_code' => 200, + 'incoming_data' => FALSE, + 'outgoing_data' => FALSE, + ), + 'OPTIONS' => array( + 'success_code' => 200, + 'incoming_data' => FALSE, + 'outgoing_data' => FALSE, + ), + 'CONNECT' => array( + 'success_code' => 200, + 'incoming_data' => FALSE, + 'outgoing_data' => FALSE, + ), + 'PATCH' => array( + 'success_code' => 200, + 'incoming_data' => TRUE, + 'outgoing_data' => FALSE, + ), + ); + } + if ($method) { + return $this->reuqestMethods[$method]; + } + return $this->reuqestMethods; } } diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/DBLogResource.php b/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/DBLogResource.php index b4ea4d1..71c3d1c 100644 --- a/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/DBLogResource.php +++ b/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/DBLogResource.php @@ -9,15 +9,15 @@ use Drupal\rest\Plugin\ResourceBase; use Drupal\Core\Annotation\Plugin; -use Symfony\Component\HttpFoundation\Response; +use Drupal\Core\Annotation\Translation; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Provides a resource for database watchdog log entries. * * @Plugin( - * id = "dblog", - * label = "Watchdog database log" + * id = "dblog", + * label = @Translation("Watchdog database log") * ) */ class DBLogResource extends ResourceBase { @@ -38,8 +38,8 @@ public function routes() { * * Returns a watchdog log entry for the specified ID. * - * @return \Symfony\Component\HttpFoundation\Response - * The response object. + * @return object + * The databse row loaded as stdClass object. * * @throws \Symfony\Component\HttpKernel\Exception\HttpException */ diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php b/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php index 8416371..a28ae07 100644 --- a/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php +++ b/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php @@ -8,6 +8,7 @@ namespace Drupal\rest\Plugin\rest\resource; use Drupal\Core\Annotation\Plugin; +use Drupal\Core\Annotation\Translation; use Drupal\Core\Entity\EntityStorageException; use Drupal\rest\Plugin\ResourceBase; use Symfony\Component\HttpFoundation\Response; @@ -18,9 +19,9 @@ * Represents entities as resources. * * @Plugin( - * id = "entity", - * label = "Entity", - * derivative = "Drupal\rest\Plugin\Derivative\EntityDerivative" + * id = "entity", + * label = @Translation("Entity"), + * derivative = "Drupal\rest\Plugin\Derivative\EntityDerivative" * ) */ class EntityResource extends ResourceBase { @@ -31,8 +32,8 @@ class EntityResource extends ResourceBase { * @param mixed $id * The entity ID. * - * @return \Symfony\Component\HttpFoundation\Response - * The response object. + * @return \Drupal\Core\Entity\EntityInterface + * The loaded entity. * * @throws \Symfony\Component\HttpKernel\Exception\HttpException */ @@ -51,9 +52,6 @@ public function get($id) { * @param mixed $id * The entity ID. * - * @return \Symfony\Component\HttpFoundation\Response - * The response object. - * * @throws \Symfony\Component\HttpKernel\Exception\HttpException */ public function delete($id) { @@ -62,13 +60,13 @@ public function delete($id) { if ($entity) { try { $entity->delete(); - // Delete responses have an empty body. - return new Response('', 204); } catch (EntityStorageException $e) { throw new HttpException(500, 'Internal Server Error', $e); } } - throw new NotFoundHttpException(t('Entity with ID @id not found', array('@id' => $id))); + else { + throw new NotFoundHttpException(t('Entity with ID @id not found', array('@id' => $id))); + } } } diff --git a/core/modules/rest/lib/Drupal/rest/RequestHandler.php b/core/modules/rest/lib/Drupal/rest/RequestHandler.php index 16af1d5..1dbd81a 100644 --- a/core/modules/rest/lib/Drupal/rest/RequestHandler.php +++ b/core/modules/rest/lib/Drupal/rest/RequestHandler.php @@ -34,32 +34,41 @@ class RequestHandler extends ContainerAware { * '_plugin' property of route object. */ public function handle($plugin, Request $request, $id = NULL) { - $method = strtolower($request->getMethod()); - if (user_access("restful $method $plugin")) { + $method = $request->getMethod(); + $lower_method = strtolower($method); + if (user_access("restful $lower_method $plugin")) { $resource = $this->container ->get('plugin.manager.rest') ->getInstance(array('id' => $plugin)); + // @todo De-serialization should happen here if the request is supposed + // to carry incoming data. + $settings = $resource->requestMethods($method); try { - $data = $resource->{$method}($id); - // If the plugin returns response objects itself we pass them through. - if ($data instanceof Response) { - return $data; - } - // Otherwise we serialize the data. - $serializer = $this->container->get('serializer'); - // Convert object to array as normalizer does not support - // non EntityNG objects. - if (is_object($data) && !$data instanceof EntityNG) { - $data = (array) $data; - } - // @todo Replace the format here with something we get from the HTTP - // Accept headers. See http://drupal.org/node/1833440 - $output = $serializer->serialize($data, 'drupal_jsonld'); - return new Response($output, 200, array('Content-Type' => 'application/ld+json')); + $data = $resource->{$lower_method}($id); } catch (HttpException $e) { return new Response($e->getMessage(), $e->getStatusCode(), $e->getHeaders()); } + // If the plugin returns response objects itself we pass them through. + if ($data instanceof Response) { + return $data; + } + // If there is no response body data to return we can send a response + // immediately. + if (empty($settings['outgoing_data'])) { + return new Response('', $settings['success_code']); + } + // Otherwise we serialize the data. + $serializer = $this->container->get('serializer'); + // Convert object to array as normalizer does not support + // non EntityNG objects. + if (is_object($data) && !$data instanceof EntityNG) { + $data = (array) $data; + } + // @todo Replace the format here with something we get from the HTTP + // Accept headers. See http://drupal.org/node/1833440 + $output = $serializer->serialize($data, 'drupal_jsonld'); + return new Response($output, $settings['success_code'], array('Content-Type' => 'application/ld+json')); } return new Response('Access Denied', 403); } diff --git a/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php b/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php index 3d995d7..66b3457 100644 --- a/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php +++ b/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php @@ -32,17 +32,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); // Enable web API for the watchdog resource. - $config = config('rest'); - $config->set('resources', array( - 'dblog' => 'dblog', - )); - $config->save(); - - // Rebuild routing cache, so that the web API paths are available. - drupal_container()->get('router.builder')->rebuild(); - // Reset the Simpletest permission cache, so that the new resource - // permissions get picked up. - drupal_static_reset('checkPermissions'); + $this->enableService('dblog'); } /** diff --git a/core/modules/rest/lib/Drupal/rest/Tests/DeleteTest.php b/core/modules/rest/lib/Drupal/rest/Tests/DeleteTest.php index 8e27864..4c039c2 100644 --- a/core/modules/rest/lib/Drupal/rest/Tests/DeleteTest.php +++ b/core/modules/rest/lib/Drupal/rest/Tests/DeleteTest.php @@ -34,7 +34,7 @@ public static function getInfo() { */ public function testDelete() { foreach (entity_get_info() as $entity_type => $info) { - $this->enableService($entity_type); + $this->enableService('entity:' . $entity_type); // Create a user account that has the required permissions to delete // resources via the web API. $account = $this->drupalCreateUser(array('restful delete entity:' . $entity_type)); diff --git a/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php b/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php index 1dbc796..eed04fb 100644 --- a/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php +++ b/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php @@ -67,7 +67,9 @@ protected function httpRequest($url, $method, $body = NULL, $format = 'applicati break; } - $this->verboseHttpRequest($url, $method, $body); + $this->verbose($method . ' request to: ' . $url . + '
Code: ' . curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE) . + '
Response: ' . $response); return $response; } @@ -100,16 +102,16 @@ protected function entityCreate($entity_type) { /** * Enables the web service interface for a specific entity type. * - * @param string|FALSE $entity_type - * The entity type that should get web API enabled or FALSE to disable all - * entity types. + * @param string|FALSE $resource_type + * The resource type that should get web API enabled or FALSE to disable all + * resource types. */ - protected function enableService($entity_type) { + protected function enableService($resource_type) { // Enable web API for this entity type. $config = config('rest'); - if ($entity_type) { + if ($resource_type) { $config->set('resources', array( - 'entity:' . $entity_type => 'entity:' . $entity_type, + $resource_type => $resource_type, )); } else { @@ -123,15 +125,4 @@ protected function enableService($entity_type) { // permissions get picked up. drupal_static_reset('checkPermissions'); } - - /** - * Verbose the response of the REST call. - */ - protected function verboseHttpRequest($url, $method, $body) { - $response_code = curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE); - $response_content = $this->drupalGetContent(); - $this->verbose($method . ' request to: ' . $url . - '
Code: ' . $response_code . - '
Response: ' . $response_content); - } } diff --git a/core/modules/rest/rest.info b/core/modules/rest/rest.info index 8513e15..c0bf4bd 100644 --- a/core/modules/rest/rest.info +++ b/core/modules/rest/rest.info @@ -3,5 +3,6 @@ description = Exposes entities and other resources as RESTful web API package = Core version = VERSION core = 8.x +; @todo Remove this dependency once hard coding to JSON-LD is gone. dependencies[] = jsonld configure = admin/config/services/rest