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

#69 => done

parent a7075da2
Pipeline #5190 passed with stages
in 2 minutes and 2 seconds
......@@ -8,11 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- In progress...
### Added
- #69: Add endpoint to list all columns for a table
- #66: Export all attributes (a=all)
- #48: Export to votable format
- #64: Data management system
### Changed
- #68: Deleting property table_name from attribute entity
- #67: Body Parsing and Content Length slim's middlewares
- #61: Docker images are stored on the gitlab internal registry
- #63: Update composer.json dependencies and using php version 8
......
......@@ -84,6 +84,10 @@ $container->set('App\Action\TableListAction', function (ContainerInterface $c) {
return new App\Action\TableListAction($c->get('em'), new App\Search\DBALConnectionFactory());
});
$container->set('App\Action\ColumnListAction', function (ContainerInterface $c) {
return new App\Action\ColumnListAction($c->get('em'), new App\Search\DBALConnectionFactory());
});
$container->set('App\Action\AdminFileExplorerAction', function (ContainerInterface $c) {
return new App\Action\AdminFileExplorerAction($c->get('settings')['data_path']);
});
......
......@@ -22,6 +22,7 @@ $app->group('', function (RouteCollectorProxy $group) {
$group->map([OPTIONS, GET, POST], '/database', App\Action\DatabaseListAction::class);
$group->map([OPTIONS, GET, PUT, DELETE], '/database/{id}', App\Action\DatabaseAction::class);
$group->map([OPTIONS, GET], '/database/{id}/table', App\Action\TableListAction::class);
$group->map([OPTIONS, GET], '/database/{id}/table/{tname}/column', App\Action\ColumnListAction::class);
$group->map([OPTIONS, GET], '/file-explorer[{fpath:.*}]', App\Action\AdminFileExplorerAction::class);
})->add(new App\Middleware\RouteGuardMiddleware(
boolval($container->get(SETTINGS)['token']['enabled']),
......
<?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\HttpNotFoundException;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use App\Search\DBALConnectionFactory;
/**
* @author François Agneray <francois.agneray@lam.fr>
* @package App\Action
*/
final class ColumnListAction extends AbstractAction
{
/**
* @var DBALConnectionFactory
*/
private $connectionFactory;
/**
* 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
*/
public function __construct(EntityManagerInterface $em, DBALConnectionFactory $connectionFactory)
{
parent::__construct($em);
$this->connectionFactory = $connectionFactory;
}
/**
* `GET` Returns a list of all columns available in a table from a database
*
* @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 database with primary key
$database = $this->em->find('App\Entity\Database', $args['id']);
// If database is not found 404
if (is_null($database)) {
throw new HttpNotFoundException(
$request,
'Database with id ' . $args['id'] . ' is not found'
);
}
if ($request->getMethod() === GET) {
$connection = $this->connectionFactory->create($database);
$sm = $connection->getSchemaManager();
$columns = $this->getColumns($sm, $args['tname']);
$payload = json_encode($columns);
}
$response->getBody()->write($payload);
return $response;
}
/**
* @param AbstractSchemaManager $sm Doctrine schema manager for the selected database
*
* @return string[]
*/
private function getColumns(AbstractSchemaManager $sm, string $tableName): array
{
$columns = array();
foreach ($sm->listTableColumns($tableName) as $column) {
$columns[] = $column->getName();
}
return $columns;
}
}
......@@ -16,7 +16,7 @@ use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Slim\Exception\HttpNotFoundException;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\DBAL\Schema\View;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use App\Search\DBALConnectionFactory;
/**
......@@ -71,10 +71,7 @@ final class TableListAction extends AbstractAction
if ($request->getMethod() === GET) {
$connection = $this->connectionFactory->create($database);
$sm = $connection->getSchemaManager();
$f = function ($o) {
return $o->getName();
};
$tables = array_merge(array_map($f, $sm->listTables()), $this->getViews($sm->listViews()));
$tables = array(...$this->getTables($sm), ...$this->getViews($sm));
$payload = json_encode($tables);
}
......@@ -83,15 +80,29 @@ final class TableListAction extends AbstractAction
}
/**
* @param View[] $listViews List of views availables for the selected database
* @param AbstractSchemaManager $sm Doctrine schema manager for the selected database
*
* @return string[]
*/
private function getViews(array $listViews): array
private function getTables(AbstractSchemaManager $sm): array
{
$tables = array();
foreach ($sm->listTables() as $table) {
$tables[] = $table->getName();
}
return $tables;
}
/**
* @param AbstractSchemaManager $sm Doctrine schema manager for the selected database
*
* @return string[]
*/
private function getViews(AbstractSchemaManager $sm): array
{
$views = array();
foreach ($listViews as $v) {
$views[] = $v->getName();
foreach ($sm->listViews() as $view) {
$views[] = $view->getName();
}
return $views;
}
......
<?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\Tests\Action;
use PHPUnit\Framework\TestCase;
use Nyholm\Psr7\ServerRequest;
use Nyholm\Psr7\Response;
use Slim\Exception\HttpNotFoundException;
use Doctrine\ORM\EntityManager;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\Column;
use App\Entity\Database;
use App\Search\DBALConnectionFactory;
final class ColumnListActionTest extends TestCase
{
private $action;
private $entityManager;
private $connectionFactory;
protected function setUp(): void
{
$this->entityManager = $this->createMock(EntityManager::class);
$this->connectionFactory = $this->createMock(DBALConnectionFactory::class);
$this->action = new \App\Action\ColumnListAction($this->entityManager, $this->connectionFactory);
}
public function testOptionsHttpMethod(): void
{
$request = $this->getRequest('OPTIONS');
$response = ($this->action)($request, new Response(), array());
$this->assertSame($response->getHeaderLine('Access-Control-Allow-Methods'), 'GET, OPTIONS');
}
public function testDatabaseIsNotFound(): void
{
$this->entityManager->method('find')->willReturn(null);
$this->expectException(HttpNotFoundException::class);
$this->expectExceptionMessage('Database with id 1 is not found');
$request = $this->getRequest('GET');
$response = ($this->action)($request, new Response(), array('id' => 1));
$this->assertEquals(404, (int) $response->getStatusCode());
}
public function testGetColumnsList(): void
{
$database = $this->createMock(Database::class);
$this->entityManager->method('find')->willReturn($database);
$columnId = $this->createMock(Column::class);
$columnId->method('getName')->willReturn('id');
$columnRa = $this->createMock(Column::class);
$columnRa->method('getName')->willReturn('ra');
$columnDec = $this->createMock(Column::class);
$columnDec->method('getName')->willReturn('dec');
$sm = $this->createMock(AbstractSchemaManager::class);
$sm->method('listTableColumns')->willReturn(array($columnId, $columnRa, $columnDec));
$connection = $this->createMock(Connection::class);
$connection->method('getSchemaManager')->willReturn($sm);
$this->connectionFactory->method('create')->willReturn($connection);
$request = $this->getRequest('GET');
$response = ($this->action)($request, new Response(), array('id' => 1, 'tname' => 'observation'));
$this->assertSame(json_encode(array('id', 'ra', 'dec')), (string) $response->getBody());
}
private function getRequest(string $method): ServerRequest
{
return new ServerRequest($method, '/database/1/table/observation/column', array(
'Content-Type' => 'application/json'
));
}
}
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