New version managing API

This commit is contained in:
Unknown 2018-05-07 18:14:12 +02:00 committed by Mascaro Lucas
parent 367d3c5988
commit c6b46e1b88
10 changed files with 1835 additions and 1910 deletions

View File

@ -26,7 +26,7 @@
if( !isset($_SESSION['CAS']) || !is_array($_SESSION['CAS']) ) $_SESSION['CAS'] = []; if( !isset($_SESSION['CAS']) || !is_array($_SESSION['CAS']) ) $_SESSION['CAS'] = [];
if( !isset($_SESSION['AUTH']) || !is_array($_SESSION['AUTH']) ) $_SESSION['AUTH'] = []; if( !isset($_SESSION['AUTH']) || !is_array($_SESSION['AUTH']) ) $_SESSION['AUTH'] = [];
if( !isset($_SESSION['AvailableDepartments']) || !is_array($_SESSION['AvailableDepartments']) ) $_SESSION['AvailableDepartments'] = []; if( !isset($_SESSION['AvailableDepartments']) || !is_array($_SESSION['AvailableDepartments']) ) $_SESSION['AvailableDepartments'] = [];
if( !isset($_SESSION['VERSION']) || !is_string($_SESSION['VERSION']) ) $_SESSION['VERSION'] = null; if( !isset($_SESSION['VERSION']) || !is_int($_SESSION['VERSION']) ) $_SESSION['VERSION'] = null;
if( !isset($_SESSION['CurrentDepartmentId']) || !is_int($_SESSION['CurrentDepartmentId']) ) $_SESSION['CurrentDepartmentId'] = null; if( !isset($_SESSION['CurrentDepartmentId']) || !is_int($_SESSION['CurrentDepartmentId']) ) $_SESSION['CurrentDepartmentId'] = null;

View File

@ -146,7 +146,7 @@ class casController{
$_SESSION['AvailableDepartments'] = $departments; $_SESSION['AvailableDepartments'] = $departments;
/* (4) Choose first department by default */ /* (4) Choose first department by default */
$_SESSION['CurrentDatabase'] = $departments[0]['dbName']; $_SESSION['CurrentDatabase'] = $departments[0]['versions'][0]['dbName'];
$_SESSION['CurrentDepartmentId'] = $departments[0]['idDep']; $_SESSION['CurrentDepartmentId'] = $departments[0]['idDep'];
/* (5) Use this department's database */ /* (5) Use this department's database */

View File

@ -0,0 +1,28 @@
<?php
/**
* Created by PhpStorm.
* User: lucas
* Date: 07/05/18
* Time: 17:59
*/
namespace api\module\department\version;
use database\core\Repo;
class switchController
{
public function get($args){
$version = null;
extract($args);
$versionData = Repo::getRepo("meta")->getVersionById($version);
$_SESSION['CurrentDatabase'] = $versionData["dbName"];
$_SESSION['VERSION'] = $version;
return ["success" => true];
}
}

View File

@ -10,6 +10,7 @@ namespace api\module\department;
use database\core\Repo; use database\core\Repo;
use database\repo\meta;
use error\core\Error; use error\core\Error;
use error\core\Err; use error\core\Err;
use database\repo\department; use database\repo\department;
@ -18,95 +19,6 @@ use Ifsnop\Mysqldump\Mysqldump;
class versionController{ class versionController{
/* Attributes
---------------------------------------------------------*/
private $backup_path;
private $origin_dbname;
/* (1) Initialize object
*
---------------------------------------------------------*/
public function __construct(){
$this->backup_path = null;
$this->origin_dbname = null;
}
/* (2) Initializes a directory
*
* @db_name<String> The database name
*
* @return outName<outType> outDesc
*
--------------------------------------------------------*/
private function initDir(string $db_name){
/* (1) Manage 'BACKUP' root directory
---------------------------------------------------------*/
/* (1) If not exists -> try to create it */
if( !is_dir(__BACKUP__) )
mkdir(__BACKUP__);
/* (2) Try to override permissions */
chmod(__BACKUP__, 0775);
/* (2) Manage sub-directory
---------------------------------------------------------*/
/* (1) If @db_name is 'preview_@database_hash' -> extract @database */
if( preg_match('/^preview_(\w+)_[a-f0-9]+$/', $db_name, $m) )
$db_name = $m[1];
/* (2) Store @db_name as origin */
$this->origin_dbname = $db_name;
/* (3) Store backup path */
$this->backup_path =__BACKUP__."/$db_name";
/* (4) If dir. already exists -> do nothing more */
if( is_dir($this->backup_path) )
return;
/* (4) Else -> try to create + set permissions */
mkdir($this->backup_path);
chmod($this->backup_path, 0775);
}
/* (3) Scan directory files
*
* @path<String> Directory path
*
* @return files<array> Array containing file names
*
---------------------------------------------------------*/
private function scandir(string $path) : array {
/* (1) Return [] if not a directory */
if( !is_dir($path) )
return [];
/* (2) Scan the directory */
$files = scandir($path);
/* (3) Remove '.' and '..' (2 first elements) */
$files = array_slice($files, 2);
/* (4) Return file list */
return $files;
}
/* (4) List available versions for this department /* (4) List available versions for this department
* *
* @return versions<array> Version list * @return versions<array> Version list
@ -114,17 +26,12 @@ class versionController{
---------------------------------------------------------*/ ---------------------------------------------------------*/
public function get($args){ public function get($args){
/* (1) Initialize directory for current database (department) */ //search for the current department in the session and return its versions
$this->initDir( $_SESSION['CurrentDatabase'] ); /** @var meta $depRepo */
$depRepo = Repo::getRepo("meta");
/* (2) Strip extensions */ //if no department found, return an empty array
$versions = array_map( return ['versions' => $depRepo->getAllVersions($_SESSION['CurrentDepartmentId']) ];
function($e){ return pathinfo($e, PATHINFO_FILENAME); },
$this->scandir($this->backup_path)
);
/* (3) Return versions */
return ['versions' => $versions ];
} }
@ -132,7 +39,7 @@ class versionController{
/* (5) Remove an existing version for this department /* (5) Remove an existing version for this department
* *
* @version<String> Version name (typically snapshot date) * @version<int> Version name (typically snapshot date)
* *
* @return deleted<bool> Whether the version has been deleted * @return deleted<bool> Whether the version has been deleted
* *
@ -141,11 +48,8 @@ class versionController{
$version = null; $version = null;
extract($args); extract($args);
/* (1) Initialize directory for current database (department) */ /* (1) Dispatch 'deleteVersion' result */
$this->initDir( $_SESSION['CurrentDatabase'] ); return [ 'deleted' => Repo::getRepo("meta")->deleteVersion($version) ];
/* (2) Dispatch 'unlink' result */
return [ 'deleted' => unlink($this->backup_path."/$version.sql") ];
} }
@ -156,10 +60,8 @@ class versionController{
* *
---------------------------------------------------------*/ ---------------------------------------------------------*/
public function post($args){ public function post($args){
$label = null;
extract($args);
/* (1) Initialize directory for current database (department) */
$this->initDir( $_SESSION['CurrentDatabase'] );
/* (2) Try to create the snapshot */ /* (2) Try to create the snapshot */
try{ try{
@ -168,21 +70,33 @@ class versionController{
$conf = Repo::getDBConfig(); $conf = Repo::getDBConfig();
/* (2.2) Try to dump the database */ /* (2.2) Try to dump the database */
/** @var Mysqldump*/
$dump = new Mysqldump( $dump = new Mysqldump(
'mysql:host='.$conf['host'].';dbname='.$conf['dbname'], 'mysql:host='.$conf['host'].';dbname='.$_SESSION['CurrentDatabase'],
$conf['username'], $conf['username'],
$conf['password'], $conf['password']
[ "compress" => Mysqldump::GZIP ]
); );
/* (2.3) Get current date (for naming the version) */ //create temporary file;
$current_date = date('d-m-Y'); $file = tmpfile();
//get URI
$metaDatas = stream_get_meta_data($file);
$tmpFilename = $metaDatas['uri'];
//close file pointer
fclose($file);
//fill the file with sql dump
$dump->start($tmpFilename);
/* (2.4) Store the version */ /** @var department $depRepo */
$dump->start($this->backup_path."/$current_date.sql"); $depRepo = Repo::getRepo("department");
$dbName = $depRepo->createVersionDatabase($_SESSION['CurrentDepartmentId'],file_get_contents($tmpFilename));
/** @var meta $metaRep */
$metaRep = Repo::getRepo("meta");
$versionId = $metaRep->createVersion($label, $dbName,$_SESSION['CurrentDepartmentId'] ,false);
/* (2.5) Return status */ /* (2.5) Return status */
return ['created_id' => $current_date ]; return ['created_id' => $versionId ];
/* (3) On error -> dispatch error */ /* (3) On error -> dispatch error */
}catch(\Exception $e){ }catch(\Exception $e){
@ -194,106 +108,24 @@ class versionController{
} }
/* (6) ( Switches to preview || Applies on prod ) for a version /* (6) Modify a version and use it
* *
* @apply<int> If 0 -> preview version
* If 1 -> apply version into prod database
* @version<String> [OPT] Version name to use (if ommited, switch back to prod database)
* *
* @return created_id<String> The created version id (date) * @return bool success
* *
---------------------------------------------------------*/ ---------------------------------------------------------*/
public function put($args){ public function put($args){
$apply = null; $label = null;
$version = null; $version = null;
$default = null;
extract($args); extract($args);
/** @var meta $metaRepo */
/* (1) Initialisation $metaRepo = Repo::getRepo("meta");
---------------------------------------------------------*/
/* (1) Initialize directory for current database (department) */
$this->initDir( $_SESSION['CurrentDatabase'] );
/* (2) Get department repository */
/** @var department $dept_repo */
$dept_repo = Repo::getRepo('department');
/* (3) Check whether we have to [apply OR preview] */
$apply = ( $apply === '1' );
/* (2) If back to 'prod' database
---------------------------------------------------------*/
if( is_null($version) ){
/* (1) Reset database to 'prod' */
$_SESSION['CurrentDatabase'] = $this->origin_dbname;
$_SESSION['VERSION'] = null;
/* (2) Return success */
return [ 'updated' => true ];
}
/* (3) Read the backup version
---------------------------------------------------------*/
/* (1) Start buffer */
ob_start();
/* (2) Read backup file */
readgzfile($this->backup_path."/$version.sql");
/* (3) Store & Flush buffer into variable */
$snapshot = ob_get_clean();
/* (4) Manage error */
if( strlen($snapshot) === 0 )
return ['error' => new Error(Err::RepoError)];
/* (4) APPLY into 'prod' database
---------------------------------------------------------*/
if( $apply ){
/* (1) Restore from this version */
$restored = $dept_repo->restore($this->origin_dbname, $snapshot);
$_SESSION['VERSION'] = null;
/* (2) Dispatch repo execution status */
return [ 'updated' => $restored ];
}
/* (5) PREVIEW version database
---------------------------------------------------------*/
/* (1) Try to get 'preview' database name */
$preview_dbname = $dept_repo->previewExists($this->origin_dbname, $version);
/* (2) If does not exist -> create it */
if( is_null($preview_dbname) ){
/* 1. Try to create preview */
$preview_dbname = $dept_repo->createPreview($this->origin_dbname, $version);
/* 2. Switch to preview */
$dept_repo->restore($preview_dbname, $snapshot);
}
/* (3) Store 'preview' database in session */
$_SESSION['CurrentDatabase'] = $preview_dbname;
$_SESSION['VERSION'] = $version;
/* (4) Return status */ /* (4) Return status */
return [ 'updated' => true ]; return [ 'updated' => $metaRepo->updateVersion($version,$label,$default) ];
} }
} }

View File

@ -167,36 +167,13 @@ class department extends Repo_i
} }
public function restore(string $dbName, string $SQL) : bool{ public function createVersionDatabase(String $depId, String $SQL) : String{
$dbName = uniqid($depId)."";
//get the list of command to execute $this->pdo->exec("CREATE DATABASE $dbName; USE $dbName;".$SQL."USE {$_SESSION["CurrentDatabase"]};SET autocommit=1;");
$this->pdo->exec("DROP DATABASE $dbName;"); $this->pdo->setAttribute(\PDO::ATTR_AUTOCOMMIT,1);
$this->pdo->exec("CREATE DATABASE $dbName;");
$this->pdo->exec("USE $dbName;");
return $this->pdo->exec($SQL); return $dbName;
} }
private static function generatePreviewDBName(string $bdName, string $backupName) : string{
return "preview_{$bdName}_".sha1($bdName.$backupName);
}
public function previewExists(string $dbName, string $backupname) : ?string {
$previewDBName = static::generatePreviewDBName($dbName,$backupname);
$st = $this->pdo->prepare("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = :dbName");
$st->execute(["dbName" => $previewDBName]);
if($st->fetch() === false) return null;
else return $previewDBName;
}
public function createPreview(string $dbName, string $backupname) : string {
$previewDBName = static::generatePreviewDBName($dbName,$backupname);
$this->pdo->exec("CREATE OR REPLACE DATABASE $previewDBName");
return $previewDBName;
}
} }

View File

@ -88,7 +88,7 @@ class meta extends Repo_i {
public function get_prof_departments(String $casLogin) : array{ public function get_prof_departments(String $casLogin) : array{
/* (1) Prepare Statement */ /* (1) Prepare Statement */
$st = $this->pdo->prepare("SELECT d2.iddepartement idDep, d2.label labelDep, d2.databaseName dbName $st = $this->pdo->prepare("SELECT d2.iddepartement idDep, d2.label labelDep
FROM meta_vhost.casUser FROM meta_vhost.casUser
JOIN meta_vhost.linkedDep D ON casUser.casLogin = D.casUser_casLogin JOIN meta_vhost.linkedDep D ON casUser.casLogin = D.casUser_casLogin
JOIN meta_vhost.departement d2 ON D.departement_iddepartement = d2.iddepartement JOIN meta_vhost.departement d2 ON D.departement_iddepartement = d2.iddepartement
@ -108,11 +108,21 @@ class meta extends Repo_i {
/* (5) Get data */ /* (5) Get data */
$fetched = $st->fetchAll(); $fetched = $st->fetchAll();
/* (6) Return [] on no result */ /* (6) Return [] on no result */
if( $fetched === false ) if( $fetched === false )
return []; return [];
/* (7) Return data */ /* (7) Add all possible databases to the department */
foreach ($fetched as &$dep){
$st = $this->pdo->prepare("SELECT *
FROM meta_vhost.`databases`
WHERE departement_iddepartement = :idDep
ORDER BY meta_vhost.`databases`.`default` DESC ");
$st->execute(["idDep" => $dep["idDep"]]);
$dep["versions"] = $st->fetchAll();
}
/* (8) Return data */
return $fetched; return $fetched;
} }
@ -145,15 +155,6 @@ class meta extends Repo_i {
} }
/* (5) Check if a link exists /* (5) Check if a link exists
* *
* @casLogin<String> The professor's cas login * @casLogin<String> The professor's cas login
@ -254,4 +255,75 @@ class meta extends Repo_i {
} }
public function deleteVersion(int $version) : bool{
//get version data
$st = $this->pdo->prepare("SELECT * FROM meta_vhost.`databases` WHERE iddatabase = :id");
$st->execute(["id" => $version]);
$versionData = $st->fetch();
if($versionData == false){
return false;
}
//delete database
$this->pdo->exec("DROP DATABASE {$versionData["dbName"]}");
//remove from meta
$st = $this->pdo->prepare("DELETE FROM meta_vhost.`databases` WHERE iddatabase = :id");
return $st->execute(["id" => $version]);
}
public function createVersion(String $label, String $dbName, int $depId, bool $isDefault = false) : int{
$st = $this->pdo->prepare("INSERT INTO meta_vhost.`databases`(label, dbName, `default`, departement_iddepartement)
VALUE (:label,:dbName,:default,:depId)");
$st->execute([
"label" => $label,
"dbName" => $dbName,
"default" => $isDefault? 1 : 0,
"depId" => $depId
]);
return $this->pdo->lastInsertId();
}
public function getVersionById(int $id) : array{
$st = $this->pdo->prepare("SELECT * FROM meta_vhost.`databases` WHERE iddatabase = :idVersion");
$st->execute(["idVersion" => $id]);
$fetched = $st->fetch();
return $fetched == false ? [] : $fetched;
}
public function getAllVersions(int $idDep) : array {
$st = $this->pdo->prepare("SELECT * FROM meta_vhost.`databases` WHERE departement_iddepartement = :idDep");
$st->execute(["idDep" => $idDep]);
return $st->fetchAll();
}
public function updateVersion(int $version, ?String $label, ?bool $default) : bool{
$set = "";
$execute = [];
if($label != null){
$set .= "label=:label,";
$execute["label"] = $label;
}
if($default != null){
$set .= "default=:default,";
$execute["default"] = $default?1:0;
}
$set = rtrim($set,",");
$execute["idVersion"] = $version;
$st = $this->pdo->prepare("UPDATE meta_vhost.`databases` SET $set WHERE iddatabase=:idVersion");
return $st->execute($execute);
}
} }

View File

@ -29,6 +29,7 @@
"phpstan/phpstan": "^0.9.2", "phpstan/phpstan": "^0.9.2",
"ifsnop/mysqldump-php": "2.*", "ifsnop/mysqldump-php": "2.*",
"setasign/fpdf": "1.8.1", "setasign/fpdf": "1.8.1",
"mpdf/mpdf": "^7.0" "mpdf/mpdf": "^7.0",
"php" : ">= 7.1"
} }
} }

BIN
composer.phar Normal file

Binary file not shown.

View File

@ -110,6 +110,18 @@
} }
}, },
"version":{ "version":{
"switch":{
"GET": {
"des": "Switch database version for the current session",
"per": [["cas_admin"]],
"par": {
"URL0": { "des": "The version id", "typ": "id", "ren": "version" }
},
"output": {
"sucess": { "des": "success of the operation", "typ": "bool" }
}
}
},
"GET": { "GET": {
"des": "Get the list of the versions of the department", "des": "Get the list of the versions of the department",
"per": [["cas_admin"]], "per": [["cas_admin"]],
@ -121,17 +133,20 @@
"POST": { "POST": {
"des": "Create a backup if the name is empty, execute the backup if the name is set", "des": "Create a backup if the name is empty, execute the backup if the name is set",
"per": [["cas_admin"]], "per": [["cas_admin"]],
"par": {}, "par": {
"label": { "des": "Label of the version","typ": "text"}
},
"output": { "output": {
"created_id": { "des": "The id of the created version", "typ": "varchar(10,10,alphanumeric)" } "created_id": { "des": "The id of the created version", "typ": "varchar(10,10,alphanumeric)" }
} }
}, },
"PUT": { "PUT": {
"des": "Switches to a older version or to current state", "des": "Update a version and switch to this version",
"per": [["cas_admin"]], "per": [["cas_admin"]],
"par": { "par": {
"URL0": { "des": "0 to preview, 1 to apply", "typ": "id", "ren": "apply" }, "URL0": { "des": "id of the version","typ": "id","ren": "version"},
"URL1": { "des": "The version name, current state if ommited", "typ": "varchar(10,10,alphanumeric)", "ren": "version", "opt": true } "label" : { "des": "Label de la version", "typ": "text", "opt":true},
"default": { "des": "true if this version is the one that should be used by default on login", "typ": "bool","opt":true}
}, },
"output": { "output": {
"updated": { "des": "Whether the version has been switched|applied", "typ": "bool" } "updated": { "des": "Whether the version has been switched|applied", "typ": "bool" }
@ -141,7 +156,7 @@
"des": "Delete a backup", "des": "Delete a backup",
"per": [["cas_admin"]], "per": [["cas_admin"]],
"par": { "par": {
"URL0": { "des": "The version name", "typ": "varchar(10,10,alphanumeric)", "ren": "version" } "URL0": { "des": "The version id", "typ": "id", "ren": "version" }
}, },
"output": { "output": {
"deleted": { "des": "Whether the version has been deleted", "typ": "bool" } "deleted": { "des": "Whether the version has been deleted", "typ": "bool" }

3306
package-lock.json generated

File diff suppressed because it is too large Load Diff