From 5a0194932b1ede47623045c8ce17aec855fe9f58 Mon Sep 17 00:00:00 2001 From: Jim Safley Date: Tue, 20 Feb 2024 14:17:09 -0500 Subject: [PATCH] Remove unneeded abstract class --- .../src/Controller/AbstractApiController.php | 297 ------------------ application/src/Controller/ApiController.php | 267 +++++++++++++++- .../src/Controller/ApiLocalController.php | 23 +- 3 files changed, 288 insertions(+), 299 deletions(-) delete mode 100644 application/src/Controller/AbstractApiController.php diff --git a/application/src/Controller/AbstractApiController.php b/application/src/Controller/AbstractApiController.php deleted file mode 100644 index 29d4351c6..000000000 --- a/application/src/Controller/AbstractApiController.php +++ /dev/null @@ -1,297 +0,0 @@ -paginator = $paginator; - $this->api = $api; - } - - /** - * Fetch all contexts and render a JSON-LD context object. - */ - public function contextAction() - { - $eventManager = $this->getEventManager(); - $args = $eventManager->prepareArgs(['context' => []]); - $eventManager->triggerEvent(new MvcEvent('api.context', null, $args)); - return new ApiJsonModel(['@context' => $args['context']], $this->getViewOptions()); - } - - public function get($id) - { - $resource = $this->params()->fromRoute('resource'); - $response = $this->api->read($resource, $id); - return new ApiJsonModel($response, $this->getViewOptions()); - } - - public function getList() - { - $this->setBrowseDefaults('id', 'asc'); - $resource = $this->params()->fromRoute('resource'); - - if (null === $resource) { - $view = new ViewModel; - $view->setTerminal(true); - $view->setTemplate('omeka/api/root'); - $apiResources = $this->api->search('api_resources')->getContent(); - usort($apiResources, function ($a, $b) { - return strcmp($a->id(), $b->id()); - }); - $view->setVariable('apiResources', $apiResources); - return $view; - } - - $query = $this->params()->fromQuery(); - $response = $this->api->search($resource, $query); - - $this->paginator->setCurrentPage($query['page']); - if (isset($query['per_page'])) { - $this->paginator->setPerPage($query['per_page']); - } - $this->paginator->setTotalCount($response->getTotalResults()); - - // Add Link header for pagination. - $links = []; - $pages = [ - 'first' => 1, - 'prev' => $this->paginator->getPreviousPage(), - 'next' => $this->paginator->getNextPage(), - 'last' => $this->paginator->getPageCount(), - ]; - foreach ($pages as $rel => $page) { - if ($page) { - $query['page'] = $page; - $url = $this->url()->fromRoute(null, [], - ['query' => $query, 'force_canonical' => true], true); - $links[] = sprintf('<%s>; rel="%s"', $url, $rel); - } - } - - $this->getResponse()->getHeaders() - ->addHeaderLine('Link', implode(', ', $links)); - - $totalResults = $response->getTotalResults(); - if ($totalResults !== null) { - $this->getResponse()->getHeaders() - ->addHeaderLine('Omeka-S-Total-Results', $totalResults); - } - return new ApiJsonModel($response, $this->getViewOptions()); - } - - /** - * {@inheritDoc} - * - * @param array $fileData PHP file upload data - */ - public function create($data, $fileData = []) - { - throw new Exception\NotFoundException('The create operation is not supported.'); - } - - public function update($id, $data) - { - throw new Exception\NotFoundException('The update operation is not supported.'); - } - - public function patch($id, $data) - { - throw new Exception\NotFoundException('The patch operation is not supported.'); - } - - public function delete($id) - { - throw new Exception\NotFoundException('The delete operation is not supported.'); - } - - /** - * Validate the API request and set global options. - * - * @param MvcEvent $event - */ - public function onDispatch(MvcEvent $event) - { - $request = $this->getRequest(); - - // Set the output format. - $this->setViewOption('format', $request->getQuery('format')); - - // Set the Accept header. - $this->setViewOption('accept_header', $request->getHeader('Accept')); - - // Set pretty print. - $prettyPrint = $request->getQuery('pretty_print'); - if (null !== $prettyPrint) { - $this->setViewOption('pretty_print', true); - } - - // Set the JSONP callback. - $callback = $request->getQuery('callback'); - if (null !== $callback) { - $this->setViewOption('callback', $callback); - } - - try { - // Finish dispatching the request. - $this->checkContentType($request); - parent::onDispatch($event); - } catch (\Exception $e) { - $this->logger()->err((string) $e); - return $this->getErrorResult($event, $e); - } - } - - /** - * Process post data and call create - * - * This method is overridden from the AbstractRestfulController to allow - * processing of multipart POSTs. - * - * @param Request $request - * @return mixed - */ - public function processPostData(Request $request) - { - $contentType = $request->getHeader('content-type'); - if ($contentType->match('multipart/form-data')) { - $content = $request->getPost('data'); - $fileData = $request->getFiles()->toArray(); - } else { - $content = $request->getContent(); - $fileData = []; - } - $data = $this->jsonDecode($content); - return $this->create($data, $fileData); - } - - /** - * Set a view model option. - * - * @param string $key - * @param mixed $value - */ - public function setViewOption($key, $value) - { - $this->viewOptions[$key] = $value; - } - - /** - * Get all view options. - * - * return array - */ - public function getViewOptions() - { - return $this->viewOptions; - } - - /** - * Check request content-type header to require JSON for methods with payloads. - * - * @param Request $request - * @throws Exception\UnsupportedMediaTypeException - */ - protected function checkContentType(Request $request) - { - // Require application/json Content-Type for certain methods. - $method = strtolower($request->getMethod()); - $contentType = $request->getHeader('content-type'); - if (in_array($method, ['post', 'put', 'patch']) - && ( - !$contentType - || !$contentType->match(['application/json', 'multipart/form-data']) - ) - ) { - $contentType = $request->getHeader('Content-Type'); - $errorMessage = sprintf( - 'Invalid Content-Type header. Expecting "application/json", got "%s".', - $contentType ? $contentType->getMediaType() : 'none' - ); - - throw new Exception\UnsupportedMediaTypeException($errorMessage); - } - } - - /** - * Check if the request contains an identifier. - * - * This method overrides parent in order to allow to query on one or - * multiple ids. - * - * {@inheritDoc} - * @see \Laminas\Mvc\Controller\AbstractRestfulController::getIdentifier() - */ - protected function getIdentifier($routeMatch, $request) - { - $identifier = $this->getIdentifierName(); - return $routeMatch->getParam($identifier, false); - } - - /** - * Set an error result to the MvcEvent and return the result. - * - * @param MvcEvent $event - * @param \Exception $error - */ - protected function getErrorResult(MvcEvent $event, \Exception $error) - { - $result = new ApiJsonModel(null, $this->getViewOptions()); - $result->setException($error); - - $event->setResult($result); - return $result; - } - - /** - * Decode a JSON string. - * - * Override ZF's default to always use json_decode and to add error checking.' - * - * @param string - * @return mixed - * @throws Exception\InvalidJsonException on JSON decoding errors or if the - * content is a scalar. - */ - protected function jsonDecode($string) - { - $content = json_decode($string, (bool) $this->jsonDecodeType); - - if (json_last_error() !== JSON_ERROR_NONE) { - throw new Exception\InvalidJsonException('JSON: ' . json_last_error_msg()); - } - - if (!is_array($content)) { - throw new Exception\InvalidJsonException('JSON: Content must be an object or array.'); - } - return $content; - } -} diff --git a/application/src/Controller/ApiController.php b/application/src/Controller/ApiController.php index 1763c7b8d..0f5c9d489 100644 --- a/application/src/Controller/ApiController.php +++ b/application/src/Controller/ApiController.php @@ -1,10 +1,41 @@ paginator = $paginator; + $this->api = $api; + } + public function create($data, $fileData = []) { $resource = $this->params()->fromRoute('resource'); @@ -32,4 +63,238 @@ public function delete($id) $response = $this->api->delete($resource, $id); return new ApiJsonModel($response, $this->getViewOptions()); } + + /** + * Fetch all contexts and render a JSON-LD context object. + */ + public function contextAction() + { + $eventManager = $this->getEventManager(); + $args = $eventManager->prepareArgs(['context' => []]); + $eventManager->triggerEvent(new MvcEvent('api.context', null, $args)); + return new ApiJsonModel(['@context' => $args['context']], $this->getViewOptions()); + } + + public function get($id) + { + $resource = $this->params()->fromRoute('resource'); + $response = $this->api->read($resource, $id); + return new ApiJsonModel($response, $this->getViewOptions()); + } + + public function getList() + { + $this->setBrowseDefaults('id', 'asc'); + $resource = $this->params()->fromRoute('resource'); + + if (null === $resource) { + $view = new ViewModel; + $view->setTerminal(true); + $view->setTemplate('omeka/api/root'); + $apiResources = $this->api->search('api_resources')->getContent(); + usort($apiResources, function ($a, $b) { + return strcmp($a->id(), $b->id()); + }); + $view->setVariable('apiResources', $apiResources); + return $view; + } + + $query = $this->params()->fromQuery(); + $response = $this->api->search($resource, $query); + + $this->paginator->setCurrentPage($query['page']); + if (isset($query['per_page'])) { + $this->paginator->setPerPage($query['per_page']); + } + $this->paginator->setTotalCount($response->getTotalResults()); + + // Add Link header for pagination. + $links = []; + $pages = [ + 'first' => 1, + 'prev' => $this->paginator->getPreviousPage(), + 'next' => $this->paginator->getNextPage(), + 'last' => $this->paginator->getPageCount(), + ]; + foreach ($pages as $rel => $page) { + if ($page) { + $query['page'] = $page; + $url = $this->url()->fromRoute(null, [], + ['query' => $query, 'force_canonical' => true], true); + $links[] = sprintf('<%s>; rel="%s"', $url, $rel); + } + } + + $this->getResponse()->getHeaders() + ->addHeaderLine('Link', implode(', ', $links)); + + $totalResults = $response->getTotalResults(); + if ($totalResults !== null) { + $this->getResponse()->getHeaders() + ->addHeaderLine('Omeka-S-Total-Results', $totalResults); + } + return new ApiJsonModel($response, $this->getViewOptions()); + } + + /** + * Validate the API request and set global options. + * + * @param MvcEvent $event + */ + public function onDispatch(MvcEvent $event) + { + $request = $this->getRequest(); + + // Set the output format. + $this->setViewOption('format', $request->getQuery('format')); + + // Set the Accept header. + $this->setViewOption('accept_header', $request->getHeader('Accept')); + + // Set pretty print. + $prettyPrint = $request->getQuery('pretty_print'); + if (null !== $prettyPrint) { + $this->setViewOption('pretty_print', true); + } + + // Set the JSONP callback. + $callback = $request->getQuery('callback'); + if (null !== $callback) { + $this->setViewOption('callback', $callback); + } + + try { + // Finish dispatching the request. + $this->checkContentType($request); + parent::onDispatch($event); + } catch (\Exception $e) { + $this->logger()->err((string) $e); + return $this->getErrorResult($event, $e); + } + } + + /** + * Process post data and call create + * + * This method is overridden from the AbstractRestfulController to allow + * processing of multipart POSTs. + * + * @param Request $request + * @return mixed + */ + public function processPostData(Request $request) + { + $contentType = $request->getHeader('content-type'); + if ($contentType->match('multipart/form-data')) { + $content = $request->getPost('data'); + $fileData = $request->getFiles()->toArray(); + } else { + $content = $request->getContent(); + $fileData = []; + } + $data = $this->jsonDecode($content); + return $this->create($data, $fileData); + } + + /** + * Set a view model option. + * + * @param string $key + * @param mixed $value + */ + public function setViewOption($key, $value) + { + $this->viewOptions[$key] = $value; + } + + /** + * Get all view options. + * + * return array + */ + public function getViewOptions() + { + return $this->viewOptions; + } + + /** + * Check request content-type header to require JSON for methods with payloads. + * + * @param Request $request + * @throws Exception\UnsupportedMediaTypeException + */ + protected function checkContentType(Request $request) + { + // Require application/json Content-Type for certain methods. + $method = strtolower($request->getMethod()); + $contentType = $request->getHeader('content-type'); + if (in_array($method, ['post', 'put', 'patch']) + && ( + !$contentType + || !$contentType->match(['application/json', 'multipart/form-data']) + ) + ) { + $contentType = $request->getHeader('Content-Type'); + $errorMessage = sprintf( + 'Invalid Content-Type header. Expecting "application/json", got "%s".', + $contentType ? $contentType->getMediaType() : 'none' + ); + + throw new Exception\UnsupportedMediaTypeException($errorMessage); + } + } + + /** + * Check if the request contains an identifier. + * + * This method overrides parent in order to allow to query on one or + * multiple ids. + * + * {@inheritDoc} + * @see \Laminas\Mvc\Controller\AbstractRestfulController::getIdentifier() + */ + protected function getIdentifier($routeMatch, $request) + { + $identifier = $this->getIdentifierName(); + return $routeMatch->getParam($identifier, false); + } + + /** + * Set an error result to the MvcEvent and return the result. + * + * @param MvcEvent $event + * @param \Exception $error + */ + protected function getErrorResult(MvcEvent $event, \Exception $error) + { + $result = new ApiJsonModel(null, $this->getViewOptions()); + $result->setException($error); + + $event->setResult($result); + return $result; + } + + /** + * Decode a JSON string. + * + * Override ZF's default to always use json_decode and to add error checking.' + * + * @param string + * @return mixed + * @throws Exception\InvalidJsonException on JSON decoding errors or if the + * content is a scalar. + */ + protected function jsonDecode($string) + { + $content = json_decode($string, (bool) $this->jsonDecodeType); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw new Exception\InvalidJsonException('JSON: ' . json_last_error_msg()); + } + + if (!is_array($content)) { + throw new Exception\InvalidJsonException('JSON: Content must be an object or array.'); + } + return $content; + } } diff --git a/application/src/Controller/ApiLocalController.php b/application/src/Controller/ApiLocalController.php index c0dd97cb8..3ac7a013b 100644 --- a/application/src/Controller/ApiLocalController.php +++ b/application/src/Controller/ApiLocalController.php @@ -1,6 +1,27 @@