Skip to content
Snippets Groups Projects
StartTaskCreateArchiveAction.php 4.69 KiB
<?php

/*
 * This file is part of Anis Server.
 *
 * (c) Laboratoire d'Astrophysique de Marseille / CNRS
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
declare(strict_types=1);

namespace App\Action;

use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Slim\Exception\HttpBadRequestException;
use Slim\Exception\HttpNotFoundException;
use Doctrine\ORM\EntityManagerInterface;
use PhpAmqpLib\Connection\AbstractConnection;
use PhpAmqpLib\Message\AMQPMessage;

/**
 * @author François Agneray <francois.agneray@lam.fr>
 * @package App\Action
 */
final class StartTaskCreateArchiveAction extends AbstractAction
{
    /**
     * Contains RabbitMQ connection socket
     *
     * @var AbstractConnection
     */
    private $rmq;

    /**
     * Contains settings to handle Json Web Token (app/settings.php)
     *
     * @var array
     */
    private $settings;

    /**
     * Create the classe before call __invoke to execute the action
     *
     * @param EntityManagerInterface $em       Doctrine Entity Manager Interface
     * @param AbstractConnection     $rmq      RabbitMQ connection socket
     * @param array                  $settings Settings about token
     */
    public function __construct(
        EntityManagerInterface $em,
        AbstractConnection $rmq,
        array $settings
    ) {
        parent::__construct($em);
        $this->rmq = $rmq;
        $this->settings = $settings;
    }

    /**
     * `GET` Starts an asynchronous task, through rabbitmq, to build an archive
     *
     * @param  ServerRequestInterface $request  PSR-7 This object represents the HTTP request
     * @param  ResponseInterface      $response PSR-7 This object represents the HTTP response
     * @param  string[]               $args     This table contains information transmitted in the URL (see routes.php)
     *
     * @return ResponseInterface
     */
    public function __invoke(
        ServerRequestInterface $request,
        ResponseInterface $response,
        array $args
    ): ResponseInterface {
        if ($request->getMethod() === OPTIONS) {
            return $response->withHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
        }

        // Search the correct dataset with primary key
        $datasetName = $args['dname'];
        $dataset = $this->em->find('App\Entity\Dataset', $datasetName);

        // If dataset is not found 404
        if (is_null($dataset)) {
            throw new HttpNotFoundException(
                $request,
                'Dataset with name ' . $datasetName . ' is not found'
            );
        }

        $token = '';

        // If instance is private and authorization enabled
        $instance = $dataset->getDatasetFamily()->getInstance();
        if (!$instance->getPublic() && boolval($this->settings['enabled'])) {
            $this->verifyInstanceAuthorization(
                $request,
                $instance->getName(),
                explode(',', $this->settings['admin_roles'])
            );
            $token = $request->getHeader('Authorization')[0];
        }

        // If dataset is private and authorization enabled
        if (!$dataset->getPublic() && boolval($this->settings['enabled'])) {
            $this->verifyDatasetAuthorization(
                $request,
                $dataset->getName(),
                explode(',', $this->settings['admin_roles'])
            );
            $token = $request->getHeader('Authorization')[0];
        }

        $queryParams = $request->getQueryParams();

        // The parameter "a" is mandatory
        if (!array_key_exists('a', $queryParams)) {
            throw new HttpBadRequestException(
                $request,
                'Param a is required for this request'
            );
        }

        // Create the name of the future archive
        $archiveName = 'archive_' . $dataset->getName() . '_' . (new \DateTime())->format('Y-m-d\TH:i:s') . '.zip';
        $archiveId = uniqid();

        // Publish message in the archive queue
        $channel = $this->rmq->channel();
        $channel->queue_declare('archive', false, false, false, false);
        $msg = new AMQPMessage(json_encode(array(
            'archive_id' => $archiveId,
            'dataset_name' => $datasetName,
            'query' => $request->getUri()->getQuery(),
            'param_a' => $queryParams['a'],
            'token' => $token
        )));
        $channel->basic_publish($msg, '', 'archive');

        // Just returns the future archive name
        $payload = json_encode(array('archive_name' => $archiveName, 'archive_id' => $archiveId));
        $response->getBody()->write($payload);
        return $response;
    }
}