Commit accfb59e authored by François Agneray's avatar François Agneray
Browse files

#73 => done

parent 46d7b3d8
Pipeline #5345 passed with stages
in 2 minutes and 39 seconds
......@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- In progress...
### Added
- #73: Create route for retrieve archive (all files for a search)
- #70: Add endpoint to add or delete an attribute
- #69: Add endpoint to list all columns for a table
- #66: Export all attributes (a=all)
......
......@@ -196,6 +196,24 @@ $container->set('App\Action\SearchAction', function (ContainerInterface $c) {
);
});
$container->set('App\Action\ArchiveAction', function (ContainerInterface $c) {
$anisQueryBuilder = (new App\Search\Query\AnisQueryBuilder())
->addQueryPart(new App\Search\Query\From())
->addQueryPart(new App\Search\Query\SelectFile())
->addQueryPart(new App\Search\Query\ConeSearch())
->addQueryPart(new App\Search\Query\Where(new App\Search\Query\Operator\OperatorFactory()))
->addQueryPart(new App\Search\Query\Order())
->addQueryPart(new App\Search\Query\Limit());
return new App\Action\ArchiveAction(
$c->get('em'),
$c->get('settings')['data_path'],
new App\Search\DBALConnectionFactory(),
$anisQueryBuilder,
$c->get(SETTINGS)['token']
);
});
$container->set('App\Action\DatasetFileExplorerAction', function (ContainerInterface $c) {
return new App\Action\DatasetFileExplorerAction($c->get('em'), $c->get('settings')['data_path'], $c->get(SETTINGS)['token']);
});
......
......@@ -67,5 +67,6 @@ $app->group('', function (RouteCollectorProxy $group) {
));
$app->get('/search/{dname}', App\Action\SearchAction::class);
$app->get('/archive/{dname}', App\Action\ArchiveAction::class);
$app->get('/dataset-file-explorer/{dname}[{fpath:.*}]', App\Action\DatasetFileExplorerAction::class);
$app->get('/download-file/{dname}/[{fpath:.*}]', App\Action\DownloadFileAction::class);
<?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 as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Slim\Exception\HttpBadRequestException;
use Slim\Exception\HttpNotFoundException;
use Slim\Exception\HttpInternalServerErrorException;
use Doctrine\ORM\EntityManagerInterface;
use Nyholm\Psr7\Factory\Psr17Factory;
use App\Search\DBALConnectionFactory;
use App\Search\Query\AnisQueryBuilder;
use App\Search\Response\IResponseFactory;
use App\Search\SearchException;
/**
* @author François Agneray <francois.agneray@lam.fr>
* @package App\Action
*/
final class ArchiveAction extends AbstractAction
{
/**
* Contains anis-server data path
*
* @var string
*/
private $dataPath;
/**
* @var DBALConnectionFactory
*/
private $connectionFactory;
/**
* @var AnisQueryBuilder
*/
private $anisQueryBuilder;
/**
* 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 DBALConnectionFactory $connectionFactory Factory used to construct connection to business database
* @param AnisQueryBuilder $anisQueryBuilder Object used to wrap the Doctrine DBAL Query Builder
* @param IResponseFactory $responseFactory Contains the factory used to return formatted response
* @param array $settings Settings about token
*/
public function __construct(
EntityManagerInterface $em,
string $dataPath,
DBALConnectionFactory $connectionFactory,
AnisQueryBuilder $anisQueryBuilder,
array $settings
) {
parent::__construct($em);
$this->dataPath = $dataPath;
$this->connectionFactory = $connectionFactory;
$this->anisQueryBuilder = $anisQueryBuilder;
$this->settings = $settings;
}
/**
* `GET` Returns the file found
*
* @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(Request $request, Response $response, array $args): Response
{
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'
);
}
// If dataset is private and authorization enabled
if (!$dataset->getPublic() && boolval($this->settings['enabled'])) {
$this->verifyDatasetAuthorization($request, $dataset->getName(), $this->settings['admin_role']);
}
$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'
);
}
try {
// Configure the Anis Query Builder
$connection = $this->connectionFactory->create($dataset->getSurvey()->getDatabase());
$this->anisQueryBuilder->setDoctrineQueryBuilder($connection->createQueryBuilder());
$this->anisQueryBuilder->setDatasetSelected($dataset);
// Build the SQL request
$this->anisQueryBuilder->build($queryParams);
} catch (SearchException $e) {
throw new HttpBadRequestException(
$request,
$e->getMessage()
);
}
$zipFile = '/tmp/archive_' . $dataset->getName() . '_' . (new \DateTime())->format('Y-m-d\TH:i:s') . '.zip';
$zip = new \ZipArchive();
if ($zip->open($zipFile, \ZipArchive::CREATE) !== true) {
throw new HttpInternalServerErrorException(
$request,
'Unable to open the file ' . $zipFile
);
}
// Attributes with search_flag = File
$attributesSelected = $this->anisQueryBuilder->getAttributesSelected();
$stmt = $this->anisQueryBuilder->getDoctrineQueryBuilder()->execute();
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
foreach ($attributesSelected as $attribute) {
$attributeLabel = $attribute->getLabel();
$filePath = $this->dataPath . $dataset->getDataPath() . DIRECTORY_SEPARATOR . $row[$attributeLabel];
if (file_exists($filePath)) {
$zip->addFile($filePath, $row[$attributeLabel]);
}
}
}
$zip->close();
// Stream created ZIP file
$psr17Factory = new Psr17Factory();
$stream = $psr17Factory->createStreamFromFile($zipFile, 'r');
return $response->withBody($stream)
->withHeader('Content-Disposition', 'attachment; filename=' . basename($zipFile) . ';')
->withHeader('Content-Type', mime_content_type($zipFile))
->withHeader('Content-Length', filesize($zipFile));
}
}
<?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\Search\Query;
use App\Entity\Dataset;
/**
* Represents the Anis Select File attributes Query Part
*
* @author François Agneray <francois.agneray@lam.fr>
* @package App\Search\Query
*/
class SelectFile extends AbstractQueryPart
{
/**
* Adds the select clause to the request and set only attribute with search_flag = FILE
*
* @param AnisQueryBuilder $anisQueryBuilder Represents the query being built
* @param Dataset $dataset Represents the requested dataset
* @param string[] $queryParams The query params of the url (after ?)
*/
public function __invoke(AnisQueryBuilder $anisQueryBuilder, Dataset $dataset, array $queryParams): void
{
if ($queryParams['a'] === 'all') {
$listOfIds = array_map(fn($attribute) => $attribute->getId(), $dataset->getAttributes());
} else {
$listOfIds = explode(';', $queryParams['a']);
}
$columns = array();
$attributes = array();
foreach ($listOfIds as $id) {
$attribute = $this->getAttribute($dataset, (int) $id);
if ($attribute->getSearchFlag() === 'FILE') {
$columns[] = $dataset->getTableRef() . '.' . $attribute->getName() . ' as ' . $attribute->getLabel();
$attributes[] = $attribute;
}
}
$anisQueryBuilder->getDoctrineQueryBuilder()->select($columns);
$anisQueryBuilder->setAttributesSelected($attributes);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment