<?php /** * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; under version 2 * of the License (non-upgradable). * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ namespace oat\taoDeliveryRdf\controller; use common_exception_RestApi; use core_kernel_classes_Class; use oat\generis\model\kernel\persistence\smoothsql\search\ComplexSearchService; use oat\oatbox\event\EventManager; use oat\search\base\exception\SearchGateWayExeption; use oat\tao\model\taskQueue\QueueDispatcher; use oat\tao\model\taskQueue\TaskLog\Broker\TaskLogBrokerInterface; use oat\tao\model\taskQueue\TaskLog\Entity\EntityInterface; use oat\tao\model\taskQueue\TaskLog\TaskLogFilter; use oat\tao\model\taskQueue\TaskLogActionTrait; use oat\tao\model\taskQueue\TaskLogInterface; use oat\taoDeliveryRdf\model\Delete\DeliveryDeleteTask; use oat\taoDeliveryRdf\model\DeliveryAssemblyService; use oat\generis\model\OntologyRdfs; use oat\taoDeliveryRdf\model\DeliveryFactory; use oat\taoDeliveryRdf\model\tasks\CompileDelivery; use oat\taoDeliveryRdf\model\tasks\UpdateDelivery; class RestDelivery extends \tao_actions_RestController { use TaskLogActionTrait; const REST_DELIVERY_TEST_ID = 'test'; const REST_DELIVERY_SEARCH_PARAMS = 'searchParams'; const REST_DELIVERY_ID = 'delivery'; const REST_DELIVERY_CLASS_URI = 'delivery-uri'; const REST_DELIVERY_CLASS_LABEL = 'delivery-label'; const REST_DELIVERY_CLASS_PARENT = 'delivery-parent'; const REST_DELIVERY_CLASS_COMMENT = 'delivery-comment'; const TASK_ID_PARAM = 'id'; const CLASS_LABEL_PARAM = 'delivery-label'; const CLASS_COMMENT_PARAM = 'delivery-comment'; const PARENT_CLASS_URI_PARAM = 'delivery-parent'; /** * @return EventManager */ protected function getEventManager() { return $this->getServiceLocator()->get(EventManager::SERVICE_ID); } /** * Generate a delivery from test uri * Test uri has to be set and existing */ public function generate() { try { if (!$this->hasRequestParameter(self::REST_DELIVERY_TEST_ID)) { throw new \common_exception_MissingParameter(self::REST_DELIVERY_TEST_ID, $this->getRequestURI()); } $test = $this->getResource($this->getRequestParameter(self::REST_DELIVERY_TEST_ID)); if (!$test->exists()) { throw new common_exception_RestApi('Unable to find a test associated to the given uri.'); } $label = 'Delivery of ' . $test->getLabel(); $deliveryClass = $this->getDeliveryClassByParameters(); $deliveryFactory = $this->getServiceManager()->get(DeliveryFactory::SERVICE_ID); /** @var \common_report_Report $report */ $report = $deliveryFactory->create($deliveryClass, $test, $label); if ($report->getType() == \common_report_Report::TYPE_ERROR) { $this->logInfo('Unable to generate delivery execution ' . 'into taoDeliveryRdf::RestDelivery for test uri ' . $test->getUri()); throw new \common_Exception('Unable to generate delivery execution.'); } $delivery = $report->getData(); /** @var DeliveryFactory $deliveryFactoryService */ $deliveryFactoryService = $this->getServiceManager()->get(DeliveryFactory::SERVICE_ID); $initialProperties = $deliveryFactoryService->getInitialPropertiesFromRequest($this->getRequest()); $delivery = $deliveryFactoryService->setInitialProperties($initialProperties, $delivery); $this->returnSuccess(['delivery' => $delivery->getUri()]); } catch (\Exception $e) { $this->returnFailure($e); } } /** * Put task to generate a delivery from test uri to the task queue * Test uri has to be set and existing */ public function generateDeferred() { try { if (! $this->hasRequestParameter(self::REST_DELIVERY_TEST_ID)) { throw new \common_exception_MissingParameter(self::REST_DELIVERY_TEST_ID, $this->getRequestURI()); } $test = $this->getResource($this->getRequestParameter(self::REST_DELIVERY_TEST_ID)); if (! $test->exists()) { throw new common_exception_RestApi('Unable to find a test associated to the given uri.'); } $deliveryClass = $this->getDeliveryClassByParameters(); /** @var DeliveryFactory $deliveryFactoryService */ $deliveryFactoryService = $this->getServiceManager()->get(DeliveryFactory::SERVICE_ID); $initialProperties = $deliveryFactoryService->getInitialPropertiesFromRequest($this->getRequest()); $task = CompileDelivery::createTask($test, $deliveryClass, $initialProperties); $result = [ 'reference_id' => $task->getId() ]; /** @var TaskLogInterface $taskLog */ $taskLog = $this->getServiceManager()->get(TaskLogInterface::SERVICE_ID); $report = $taskLog->getReport($task->getId()); if (!empty($report)) { if ($report instanceof \common_report_Report) { //serialize report to array $report = json_decode($report); } $result['common_report_Report'] = $report; } return $this->returnSuccess($result); } catch (\Exception $e) { $this->returnFailure($e); } } /** * Update delivery by parameters */ public function update() { try { if ($this->getRequestMethod() !== \Request::HTTP_POST) { throw new \common_exception_NotImplemented('Only post method is accepted to updating delivery'); } if (! $this->hasRequestParameter(self::REST_DELIVERY_SEARCH_PARAMS)) { throw new \common_exception_MissingParameter(self::REST_DELIVERY_SEARCH_PARAMS, $this->getRequestURI()); } $where = json_decode(html_entity_decode($this->getRequestParameter(self::REST_DELIVERY_SEARCH_PARAMS)), true); $propertyValues = $this->getRequestParameters(); unset($propertyValues[self::REST_DELIVERY_SEARCH_PARAMS]); $deliveryModelClass = $this->getDeliveryRootClass(); $deliveries = $deliveryModelClass->searchInstances($where, ['like' => false, 'recursive' => true]); $response = []; /** @var \core_kernel_classes_Resource $delivery */ foreach ($deliveries as $key => $delivery) { foreach ($propertyValues as $rdfKey => $rdfValue) { $rdfKey = \tao_helpers_Uri::decode($rdfKey); $property = $this->getProperty($rdfKey); $delivery->editPropertyValues($property, $rdfValue); } $response[] = ['delivery' => $delivery->getUri()]; } $this->returnSuccess($response); } catch (\Exception $e) { $this->returnFailure($e); } } /** * Update delivery by parameters */ public function updateDeferred() { try { if ($this->getRequestMethod() !== \Request::HTTP_POST) { throw new \common_exception_NotImplemented('Only post method is accepted to updating delivery'); } if (! $this->hasRequestParameter(self::REST_DELIVERY_SEARCH_PARAMS)) { throw new \common_exception_MissingParameter(self::REST_DELIVERY_SEARCH_PARAMS, $this->getRequestURI()); } $where = json_decode(html_entity_decode($this->getRequestParameter(self::REST_DELIVERY_SEARCH_PARAMS)), true); $propertyValues = $this->getRequestParameters(); unset($propertyValues[self::REST_DELIVERY_SEARCH_PARAMS]); $task = UpdateDelivery::createTask($where, $propertyValues); $result = [ 'reference_id' => $task->getId() ]; /** @var TaskLogInterface $taskLog */ $taskLog = $this->getServiceManager()->get(TaskLogInterface::SERVICE_ID); $report = $taskLog->getReport($task->getId()); if (!empty($report)) { if ($report instanceof \common_report_Report) { //serialize report to array $report = json_decode($report); } $result['common_report_Report'] = $report; } return $this->returnSuccess($result); } catch (\Exception $e) { $this->returnFailure($e); } } /** * Delete delivery by URI */ public function deleteDeferred() { try { if ($this->getRequestMethod() !== \Request::HTTP_DELETE) { throw new \common_exception_NotImplemented('Only delete method is accepted to deleting delivery'); } if (!$this->hasRequestParameter('uri')) { throw new \common_exception_MissingParameter('uri', $this->getRequestURI()); } $uri = $this->getRequestParameter('uri'); $delivery = $this->getResource($uri); if (!$delivery->exists()) { $this->returnFailure(new \common_exception_NotFound('Delivery has not been found')); } /** @var QueueDispatcher $queueDispatcher */ $queueDispatcher = $this->getServiceManager()->get(QueueDispatcher::SERVICE_ID); $task = new DeliveryDeleteTask(); $task->setServiceLocator($this->getServiceLocator()); $taskParameters = ['deliveryId' => $uri]; $task = $queueDispatcher->createTask($task, $taskParameters, __('Deleting of "%s"', $delivery->getLabel()), null, true); $data = $this->getTaskLogReturnData( $task->getId(), DeliveryDeleteTask::class ); $this->returnSuccess($data); } catch (\Exception $e) { $this->returnFailure($e); } } /** * List all deliveries or paginated range */ public function get() { try { if ($this->getRequestMethod() !== \Request::HTTP_GET) { throw new \common_exception_NotImplemented('Only get method is accepted to getting deliveries'); } $limit = 0; if ($this->hasRequestParameter('limit')) { $limit = $this->getRequestParameter('limit'); if (!is_numeric($limit) || (int)$limit != $limit || $limit < 0) { throw new \common_exception_ValidationFailed('limit', '\'Limit\' should be a positive integer'); } } $offset = 0; if ($this->hasRequestParameter('offset')) { $offset = $this->getRequestParameter('offset'); if (!is_numeric($offset) || (int)$offset != $offset || $offset < 0) { throw new \common_exception_ValidationFailed('offset', '\'Offset\' should be a positive integer'); } } $service = DeliveryAssemblyService::singleton(); /** @var \core_kernel_classes_Resource[] $deliveries */ $deliveries = $service->getAllAssemblies(); $overallCount = count($deliveries); if ($offset || $limit) { if ($overallCount <= $offset) { throw new \common_exception_ValidationFailed('offset', '\'Offset\' is too large'); } $deliveries = array_slice($deliveries, $offset, $limit); } $mappedDeliveries = []; foreach ($deliveries as $delivery) { $mappedDeliveries[] = [ 'uri' => $delivery->getUri(), 'label' => $delivery->getLabel(), ]; } $response = [ 'items' => $mappedDeliveries, 'overallCount' => $overallCount, ]; $this->returnSuccess($response); } catch (\Exception $e) { $this->returnFailure($e); } } /** * Action to retrieve test compilation task status from queue */ public function getStatus() { try { if (!$this->hasRequestParameter(self::TASK_ID_PARAM)) { throw new \common_exception_MissingParameter(self::TASK_ID_PARAM, $this->getRequestURI()); } $data = $this->getTaskLogReturnData( $this->getRequestParameter(self::TASK_ID_PARAM), CompileDelivery::class ); $children = $this->getStatusesForChildren($this->getRequestParameter(self::TASK_ID_PARAM)); $data['children'] = $children; $this->returnSuccess($data); } catch (\Exception $e) { $this->returnFailure($e); } } /** * @param $taskId * @return array */ protected function getStatusesForChildren($taskId) { /** @var TaskLogInterface $taskLog */ $taskLog = $this->getServiceManager()->get(TaskLogInterface::SERVICE_ID); $filter = (new TaskLogFilter()) ->eq(TaskLogBrokerInterface::COLUMN_PARENT_ID, $taskId); $collection = $taskLog->search($filter); $response = []; if ($collection->isEmpty()) { return $response; } /** @var EntityInterface $item */ foreach ($collection as $item) { $response[] = [ 'id' => $this->getTaskId($item), 'label' => $item->getLabel(), 'status' => $this->getTaskStatus($item), 'report' => $item->getReport() ? $this->getTaskReport($item) : [] ]; } return $response; } /** * Return 'Success' instead of 'Completed', required by the specified API. * * @param EntityInterface $taskLogEntity * @return string */ protected function getTaskStatus(EntityInterface $taskLogEntity) { if ($taskLogEntity->getStatus()->isCreated()) { return 'In Progress'; } elseif ($taskLogEntity->getStatus()->isCompleted()) { return 'Success'; } return $taskLogEntity->getStatus()->getLabel(); } /** * @param EntityInterface $taskLogEntity * @return array */ protected function addExtraReturnData(EntityInterface $taskLogEntity) { $data = []; if ($taskLogEntity->getReport()) { $plainReport = $this->getPlainReport($taskLogEntity->getReport()); //the second report is the report of the compilation test if (isset($plainReport[1]) && isset($plainReport[1]->getData()['uriResource'])) { $data['delivery'] = $plainReport[1]->getData()['uriResource']; } } return $data; } /** * Create a Delivery Class * * Label parameter is mandatory * If parent class parameter is an uri of valid delivery class, new class will be created under it * If not parent class parameter is provided, class will be created under root class * Comment parameter is not mandatory, used to describe new created class * * @return core_kernel_classes_Class */ public function createClass() { try { $class = $this->createSubClass($this->getDeliveryRootClass()); $result = [ 'message' => __('Class successfully created.'), 'delivery-uri' => $class->getUri(), ]; $this->returnSuccess($result); } catch (\common_exception_ClassAlreadyExists $e) { $result = [ 'message' => $e->getMessage(), 'delivery-uri' => $e->getClass()->getUri(), ]; $this->returnSuccess($result); } catch (\Exception $e) { $this->returnFailure($e); } } /** * Get a delivery class based on parameters * * If an uri parameter is provided, and it is a delivery class, this delivery class is returned * If a label parameter is provided, and only one delivery class has this label, this delivery class is returned * * @return core_kernel_classes_Class * @throws SearchGateWayExeption * @throws common_exception_RestApi */ protected function getDeliveryClassByParameters() { $rootDeliveryClass = $this->getDeliveryRootClass(); // If an uri is provided, check if it's an existing delivery class if ($this->hasRequestParameter(self::REST_DELIVERY_CLASS_URI)) { $deliveryClass = $this->getClass($this->getRequestParameter(self::REST_DELIVERY_CLASS_URI)); if ( $deliveryClass == $rootDeliveryClass || ($deliveryClass->exists() && $deliveryClass->isSubClassOf($rootDeliveryClass)) ) { return $deliveryClass; } throw new common_exception_RestApi(__('Delivery class uri provided is not a valid delivery class.')); } if ($this->hasRequestParameter(self::REST_DELIVERY_CLASS_LABEL)) { $label = $this->getRequestParameter(self::REST_DELIVERY_CLASS_LABEL); $deliveryClasses = $rootDeliveryClass->getSubClasses(true); $classes = [$rootDeliveryClass->getUri()]; foreach ($deliveryClasses as $class) { $classes[] = $class->getUri(); } /** @var ComplexSearchService $search */ $search = $this->getServiceManager()->get(ComplexSearchService::SERVICE_ID); $queryBuilder = $search->query(); $criteria = $queryBuilder->newQuery() ->add(OntologyRdfs::RDFS_LABEL)->equals($label) ->add(OntologyRdfs::RDFS_SUBCLASSOF)->in($classes) ; $queryBuilder->setCriteria($criteria); $result = $search->getGateway()->search($queryBuilder); switch ($result->count()) { case 0: throw new common_exception_RestApi(__('Delivery with label "%s" not found', $label)); case 1: return $this->getClass($result->current()->getUri()); default: $availableClasses = []; foreach ($result as $raw) { $availableClasses[] = $raw->getUri(); } throw new common_exception_RestApi(__( 'Multiple delivery class found for label "%s": %s', $label, implode(',', $availableClasses) )); } } return $rootDeliveryClass; } /** * Get the delivery root class * * @return core_kernel_classes_Class */ protected function getDeliveryRootClass() { return $this->getClass(DeliveryAssemblyService::CLASS_URI); } }