Commit afd149f0 authored by Bertrand Gauthier's avatar Bertrand Gauthier
Browse files

Ajout d'une V2 à l'API pour permettre l'import avec unicaen/db-import par...

Ajout d'une V2 à l'API pour permettre l'import avec unicaen/db-import par ESUP-SyGAL ; la V1 demeure.
parent 965f9316
Pipeline #12932 passed with stages
in 25 seconds
Journal des modifications
=========================
2.0.0
-----
- Ajout d'une V2 à l'API pour permettre l'import avec unicaen/db-import par ESUP-SyGAL ; la V1 demeure.
1.3.7
-----
- Rétablissement de la possibilité d'importer les origines de financement.
......
......@@ -7,7 +7,7 @@
et les met à disposition pour leur lecture via des requêtes GET.
## Technologies employées
## Outils employés
*sygal-import-ws* repose sur l'utilisation de :
- [Apigility](https://apigility.org/) pour la fourniture du ws ;
......@@ -53,24 +53,34 @@ Par exemple, `0.0.0.0:443->8443/tcp` indique que le ws est accessible sur la mac
## Les services fournis
Chaque vue en base de données peut être interrogée via un service dédié :
* `/structure`
* `/etablissement`
* `/ecole-doctorale`
* `/unite-recherche`
* `/individu`
* `/doctorant`
* `/these`
* `/these-annee-univ`
* `/role`
* `/acteur`
* `/origine-financement`
* `/financement`
* `/titre-acces`
* `/variable`
- `/structure`
- `/etablissement`
- `/ecole-doctorale`
- `/unite-recherche`
- `/individu`
- `/doctorant`
- `/these`
- `/these-annee-univ`
- `/role`
- `/acteur`
- `/origine-financement`
- `/financement`
- `/titre-acces`
- `/variable`
## Versions
L'API existe en plusieurs versions, veillez à spécifier la version correcte dans l'URL.
Exemple pour la version 1 : `https://localhost:8443/v1/variable`.
Exemple pour la version 2 : `https://localhost:8443/v2/variable`.
## Interrogation avec `curl`
Exemple :
```bash
curl --insecure --header "Accept: application/json" --header "Authorization: Basic xxxx" https://localhost:8443/variable
curl --insecure --header "Accept: application/json" --header "Authorization: Basic xxxx" https://localhost:8443/v1/variable
```
Remplacer `xxxx` par le token généré grâce à la commande suivante
......@@ -88,12 +98,12 @@ L'interrogation d'un service sans paramètre va retourner l'intégralité des do
Afin d'obtenir les informations spécifiques à une donnée, il est possible d'ajouter son identifiant, exemple :
```bash
curl --insecure --header "Accept: application/json" --header "Authorization: Basic xxxx" https://localhost:8443/variable/ETB_LIB_NOM_RESP
curl --insecure --header "Accept: application/json" --header "Authorization: Basic xxxx" https://localhost:8443/v1/variable/ETB_LIB_NOM_RESP
```
Pour mettre en forme le JSON retourné et faciliter la lecture, une solution est d'utiliser `python -m json.tool`:
```bash
curl --insecure --header "Accept: application/json" --header "Authorization: Basic xxxx" https://localhost:8443/variable | python -m json.tool
curl --insecure --header "Accept: application/json" --header "Authorization: Basic xxxx" https://localhost:8443/v1/variable | python -m json.tool
```
......@@ -103,11 +113,24 @@ curl --insecure --header "Accept: application/json" --header "Authorization: Bas
* Le web service retourne du json seulement.
## Service Acteur
## Services acceptant un paramètre
Aucun service n'accepte de paramètre, sauf ceux qui suivent.
### `/acteur`
Ce service accepte un paramètre supplémentaire : un identifiant de thèse (source code) peut être spécifié pour obtenir
les acteurs de cette seule thèse.
Exemple :
```bash
curl --insecure --header "Accept: application/json" --header "Authorization: Basic xxxxx" https://localhost:8443/acteur?these_id=13111
curl --insecure --header "Accept: application/json" --header "Authorization: Basic xxxxx" https://localhost:8443/v1/acteur?these_id=13111
```
### `/doctorant`
Ce service accepte un paramètre supplémentaire : un identifiant de thèse (source code) peut être spécifié pour obtenir
le doctorant de cette thèse.
Exemple :
```bash
curl --insecure --header "Accept: application/json" --header "Authorization: Basic xxxxx" https://localhost:8443/v1/doctorant?these_id=13111
```
This diff is collapsed.
This diff is collapsed.
Version 2.0.0
=============
## Sources PHP
Sur le serveur, placez-vous dans le répertoire du web service (sans doute `/var/www/sygal-import-ws`)
puis lancez les commandes suivantes pour installer la nouvelle version :
```bash
git fetch && git fetch --tags && git checkout --force 2.0.0 && bash install.sh
```
Selon le moteur PHP que vous avez installé, rechargez le service, exemple :
- php7.0-fpm : `service php7.0-fpm reload`
- apache2-mod-php7.0 : `service apache2 reload`
## Base de données
De nouvelles vues et tables *_V2 doivent être créées dans la base Apogée ou Physalis.
### Apogée
- Cf. [2.0.0/apogee-v2.sql](2.0.0/apogee-v2.sql).
### Physalis
- Cf. [2.0.0/physalis-v2.sql](2.0.0/physalis-v2.sql).
......@@ -6,20 +6,6 @@ use Application\Controller\ConsoleController;
use Application\Service\TableService;
use Application\Service\TableServiceFactory;
use Doctrine\DBAL\Logging\EchoSQLLogger;
use ImportData\V1\Entity\Db\Acteur;
use ImportData\V1\Entity\Db\Doctorant;
use ImportData\V1\Entity\Db\EcoleDoctorale;
use ImportData\V1\Entity\Db\Etablissement;
use ImportData\V1\Entity\Db\Financement;
use ImportData\V1\Entity\Db\Individu;
use ImportData\V1\Entity\Db\OrigineFinancement;
use ImportData\V1\Entity\Db\Role;
use ImportData\V1\Entity\Db\Structure;
use ImportData\V1\Entity\Db\These;
use ImportData\V1\Entity\Db\TheseAnneeUniv;
use ImportData\V1\Entity\Db\TitreAcces;
use ImportData\V1\Entity\Db\UniteRecherche;
use ImportData\V1\Entity\Db\Variable;
use Zend\ServiceManager\Factory\InvokableFactory;
return [
......@@ -37,20 +23,62 @@ return [
],
],
'services_to_entity_classes' => [
'structure' => Structure::class,
'etablissement' => Etablissement::class,
'ecole-doctorale' => EcoleDoctorale::class,
'unite-recherche' => UniteRecherche::class,
'individu' => Individu::class,
'doctorant' => Doctorant::class,
'these' => These::class,
'these-annee-univ' => TheseAnneeUniv::class,
'role' => Role::class,
'acteur' => Acteur::class,
'variable' => Variable::class,
'origine-financement' => OrigineFinancement::class,
'financement' => Financement::class,
'titre-acces' => TitreAcces::class,
'structure' => [
\ImportData\V1\Entity\Db\Structure::class,
\ImportData\V2\Entity\Db\Structure::class,
],
'etablissement' => [
\ImportData\V1\Entity\Db\Etablissement::class,
\ImportData\V2\Entity\Db\Etablissement::class,
],
'ecole-doctorale' => [
\ImportData\V1\Entity\Db\EcoleDoctorale::class,
\ImportData\V2\Entity\Db\EcoleDoctorale::class,
],
'unite-recherche' => [
\ImportData\V1\Entity\Db\UniteRecherche::class,
\ImportData\V2\Entity\Db\UniteRecherche::class,
],
'individu' => [
\ImportData\V1\Entity\Db\Individu::class,
\ImportData\V2\Entity\Db\Individu::class,
],
'doctorant' => [
\ImportData\V1\Entity\Db\Doctorant::class,
\ImportData\V2\Entity\Db\Doctorant::class,
],
'these' => [
\ImportData\V1\Entity\Db\These::class,
\ImportData\V2\Entity\Db\These::class,
],
'these-annee-univ' => [
\ImportData\V1\Entity\Db\TheseAnneeUniv::class,
\ImportData\V2\Entity\Db\TheseAnneeUniv::class,
],
'role' => [
\ImportData\V1\Entity\Db\Role::class,
\ImportData\V2\Entity\Db\Role::class,
],
'acteur' => [
\ImportData\V1\Entity\Db\Acteur::class,
\ImportData\V2\Entity\Db\Acteur::class,
],
'variable' => [
\ImportData\V1\Entity\Db\Variable::class,
\ImportData\V2\Entity\Db\Variable::class,
],
'origine-financement' => [
\ImportData\V1\Entity\Db\OrigineFinancement::class,
\ImportData\V2\Entity\Db\OrigineFinancement::class,
],
'financement' => [
\ImportData\V1\Entity\Db\Financement::class,
\ImportData\V2\Entity\Db\Financement::class,
],
'titre-acces' => [
\ImportData\V1\Entity\Db\TitreAcces::class,
\ImportData\V2\Entity\Db\TitreAcces::class,
],
],
'doctrine' => [
'sql_logger_collector' => [
......
......@@ -34,20 +34,23 @@ class ConsoleController extends AbstractConsoleController
public function updateServiceTablesAction()
{
$request = $this->getRequest();
$services = $request->getParam('services');
$services = $services ? explode(',', $services) : null;
$verbose = $request->getParam('verbose');
$priority = $verbose ? Logger::DEBUG : Logger::INFO;
foreach ($this->logger->getWriters()->toArray() as $writer) {
/** @var AbstractWriter $writer */
$writer->addFilter(new Priority($priority));
}
$this->tableService->setLogger($this->logger);
$this->tableService->updateTablesForServices($services);
if ($services !== null) {
$services = explode(',', $services);
$this->tableService->updateTablesForServices($services);
} else {
$this->tableService->updateTablesForAllServices();
}
$this->logger->info("Terminé.");
}
}
\ No newline at end of file
}
......@@ -12,6 +12,9 @@ class TableService
{
use LoggerAwareTrait;
const DELETE_TEMPLATE = "delete from %s ;";
const INSERT_TEMPLATE = "insert into %s (%s) select %s from V_%s ;";
/**
* @var EntityManager
*/
......@@ -61,17 +64,25 @@ class TableService
}
/**
* Lance la mise à jour des tables sources.
* Lance la mise à jour des tables sources pour tous les services.
*/
public function updateTablesForAllServices()
{
$services = array_keys($this->servicesToEntityClassesConfig);
$this->updateTablesForServices($services);
}
/**
* Lance la mise à jour des tables sources pour les services spécifiés.
*
* @param array|string|null $services Liste éventuelle des noms de services dont on veut mettre à jour
* les tables sources.
* Exemple : ['structure','etablissement','unite-recherche','ecole-doctorale']
* @param array $services Liste des noms de services dont on veut mettre à jour les tables sources.
* Exemple : ['structure', 'etablissement']
*/
public function updateTablesForServices($services = null)
public function updateTablesForServices(array $services)
{
$conn = $this->entityManager->getConnection();
$services = (array)$services ?: array_keys($this->servicesToEntityClassesConfig);
$sql = $this->generateTablesUpdateSQLForServices($services);
$sql = $this->generateSQLUpdatesForServices($services);
$this->logger->debug("SQL généré : " . $sql);
......@@ -94,40 +105,70 @@ class TableService
$this->logger->info(
"Les tables sources des services suivants ont été mises à jour avec succès :" . PHP_EOL .
implode(PHP_EOL, $services));
implode(PHP_EOL, $services)
);
}
/**
* Génère le SQL permettant de mettre à jour le contenu des tables sources.
* Génère le SQL permettant de mettre à jour le contenu des tables sources pour les services spécifiés.
*
* @param array $services Liste des noms de services dont on veut mettre à jour les tables sources.
* Exemple : ['structure', 'etablissement']
*
* @param array|string $services Liste éventuelle des noms de services dont on veut mettre à jour
* les tables sources.
* Exemple : ['structure','etablissement','unite-recherche','ecole-doctorale']
* @return string SQL généré.
* Exemple :
* @return string SQL généré, exemple :
* <pre>
* begin
* delete from SYGAL_STRUCTURE ;
* insert into SYGAL_STRUCTURE (SOURCE_ID, TYPE_STRUCTURE_ID, SIGLE, LIBELLE, CODE_PAYS, LIBELLE_PAYS, ID) select SOURCE_ID, TYPE_STRUCTURE_ID, SIGLE, LIBELLE, CODE_PAYS, LIBELLE_PAYS, ID from V_SYGAL_STRUCTURE ;
* delete from SYGAL_ETABLISSEMENT ;
* insert into SYGAL_ETABLISSEMENT (SOURCE_ID, STRUCTURE_ID, CODE, ID) select SOURCE_ID, STRUCTURE_ID, CODE, ID from V_SYGAL_ETABLISSEMENT ;
* delete from SYGAL_UNITE_RECH ;
* insert into SYGAL_UNITE_RECH (SOURCE_ID, STRUCTURE_ID, ID) select SOURCE_ID, STRUCTURE_ID, ID from V_SYGAL_UNITE_RECH ;
* delete from SYGAL_ECOLE_DOCT ;
* insert into SYGAL_ECOLE_DOCT (SOURCE_ID, STRUCTURE_ID, ID) select SOURCE_ID, STRUCTURE_ID, ID from V_SYGAL_ECOLE_DOCT ;
* insert into SYGAL_STRUCTURE (SOURCE_ID, ...) select SOURCE_ID, ... from V_SYGAL_STRUCTURE ;
* delete from SYGAL_STRUCTURE_V2 ;
* insert into SYGAL_STRUCTURE_V2 (SOURCE_CODE, ...) select SOURCE_CODE, ... from V_SYGAL_STRUCTURE_V2 ;
* delete from SYGAL_ETABLISSEMENT
* insert into SYGAL_ETABLISSEMENT (SOURCE_ID, ...) select SOURCE_ID, ... from V_SYGAL_ETABLISSEMENT ;
* delete from SYGAL_ETABLISSEMENT_V2 ;
* insert into SYGAL_ETABLISSEMENT_V2 (SOURCE_CODE, ...) select SOURCE_CODE, ... from V_SYGAL_ETABLISSEMENT_V2 ;
* end;
* </pre>
*/
private function generateTablesUpdateSQLForServices(array $services)
private function generateSQLUpdatesForServices(array $services): string
{
$deleteTemplate = "delete from %s ;";
$updateTemplate = "insert into %s (%s) select %s from V_%s ;";
$sqlParts = [];
$sqlParts[] = 'begin';
foreach ($services as $service) {
$className = $this->getEntityClassNameForService($service);
$sqlParts[] = $this->generateSQLUpdatesForService($service);
}
$sqlParts[] = 'end;';
return implode(PHP_EOL, $sqlParts);
}
/**
* Génère le SQL permettant de mettre à jour le contenu des tables sources pour un service.
*
* @param string $service Nom du service dont on veut mettre à jour les tables sources.
* Exemple : 'structure'
*
* @return string SQL généré, exemple :
* <pre>
* delete from SYGAL_STRUCTURE ;
* insert into SYGAL_STRUCTURE (SOURCE_ID, ...) select SOURCE_ID, ... from V_SYGAL_STRUCTURE ;
* delete from SYGAL_STRUCTURE_V2 ;
* insert into SYGAL_STRUCTURE_V2 (SOURCE_CODE, ...) select SOURCE_CODE, ... from V_SYGAL_STRUCTURE_V2 ;
* </pre>
*/
private function generateSQLUpdatesForService(string $service): string
{
$sqlParts = [];
// SQL à exécuter au préalable ?
if (isset($this->servicesPreSql[$service])) {
$sqlParts[] = $this->servicesPreSql[$service];
}
$classNames = $this->getEntityClassNamesForService($service);
// NB : Il y a une classe d'entité Doctrine par version de l'API.
foreach ($classNames as $className) {
$metadata = $this->entityManager->getClassMetadata($className);
$tableName = $metadata->getTableName();
$columnNames = $metadata->getColumnNames();
......@@ -135,31 +176,27 @@ class TableService
// exclusion éventuelle de certaines colonnes
$columnNames = array_diff($columnNames, $this->excludedColumnNames);
// SQL à exécuter au préalable ?
if (isset($this->servicesPreSql[$service])) {
$sqlParts[] = $this->servicesPreSql[$service];
}
$sqlParts[] = sprintf($deleteTemplate, $tableName);
$sqlParts[] = sprintf($updateTemplate, $tableName, $cols = implode(', ', $columnNames), $cols, $tableName);
$cols = implode(', ', $columnNames);
$sqlParts[] = sprintf(self::DELETE_TEMPLATE, $tableName);
$sqlParts[] = sprintf(self::INSERT_TEMPLATE, $tableName, $cols, $cols, $tableName);
}
$sqlParts[] = 'end;';
return implode(PHP_EOL, $sqlParts);
}
/**
* Retourne les classes d'entité Doctrine correspondant au service spécifié.
* NB : Il y a une classe par version de l'API.
*
* @param string $service
* @return string
* @return array
*/
private function getEntityClassNameForService($service)
private function getEntityClassNamesForService(string $service): array
{
$className = $this->servicesToEntityClassesConfig[$service] ?? null;
if ($className === null) {
throw new \LogicException("Service inconnu : '$service''");
if (! isset($this->servicesToEntityClassesConfig[$service])) {
throw new \LogicException("Service inconnu : '$service'");
}
return $className;
return $this->servicesToEntityClassesConfig[$service];
}
}
\ No newline at end of file
}
This diff is collapsed.
<?php
namespace ImportData\V2\Entity\Db;
/**
* Acteur
*
* @codeCoverageIgnore
*/
class Acteur
{
private $id;
private $sourceCode;
private $sourceId;
private $theseId;
private $roleId;
private $individuId;
private $acteurEtablissementId;
private $codeRoleJury;
private $libRoleJury;
private $codeQualite;
private $libQualite;
private $temoinHDR;
private $temoinRapport;
private $sourceInsertDate;
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @return mixed
*/
public function getSourceCode()
{
return $this->sourceCode;
}
/**
* @return mixed
*/
public function getSourceId()
{
return $this->sourceId;
}
/**
* @return mixed
*/
public function getTheseId()
{
return $this->theseId;
}
/**
* @return mixed
*/
public function getRoleId()
{
return $this->roleId;
}
/**
* @return mixed
*/
public function getIndividuId()
{
return $this->individuId;
}
/**
* @return mixed
*/
public function getActeurEtablissementId()
{
return $this->acteurEtablissementId;
}
/**
* return $this;
* }
*
* /**
* @return mixed
*/
public function getCodeRoleJury()
{
return $this->codeRoleJury;
}
/**
* @return mixed
*/
public function getLibRoleJury()
{
return $this->libRoleJury;
}
/**
* @return mixed
*/
public function getCodeQualite()
{
return $this->codeQualite;
}
/**
* @return mixed
*/
public function getLibQualite()
{
return $this->libQualite;
}
/**
* @return mixed
*/
public function getTemoinHDR()
{
return $this->temoinHDR;
}
/**
* @return mixed
*/
public function getTemoinRapport()
{
return $this->temoinRapport;
}
/**
* @return mixed
*/
public function getSourceInsertDate()
{
return $this->sourceInsertDate;
}
}
<?php
namespace ImportData\V2\Entity\Db;
/**
* Doctorant
*
* @codeCoverageIgnore
*/
class Doctorant
{
private $id;
private $sourceCode;
private $sourceId;
private $individuId;
private $sourceInsertDate;
private $ine;