Script player
Description
Le Script player correspond au module d’import Voozanoo3. Il a été ré-écrit pour se baser sur les class Zend et Voozanoo4.
L’objectif est d’étendre les possibilité de cette class pour qu’elle ne serve pas uniquement à faire des imports, d’où son nom plus générique de script player.
Intégration
<?php
// Instancier un objet de type Core_Library_Resource_Xml et lire le script
$oScript = new Core_Library_Resource_XML();
$oScript->LoadFromFile( APPLICATION_PATH . '/resources/xml/script_import_test.xml' );
$oScriptPlayer = Core_Library_ScriptPlayer::GetInstance();
$oScriptPlayer->Play( array(
'script' => $oScript,
'settings' => array(
'real_path' => APPLICATION_PATH . '/resources/csv/test_import.csv'
)
));
Exemple de script
<?xml version="1.0" encoding="UTF-8"?>
<script>
<actions>
<load_file type="csv" label="Chargement" file="$real_path" delimiter=";" start_position="1">
<field position="2" target="ddn" type="date"/>
<field position="1" target="prenom"/>
<field position="0" target="nom"/>
</load_file>
<fill_tmp_table table="import_table" label="Transfert" temporary_table="false" />
</actions>
</script>
Actions
void
Action vide, utile pour faire des tests.
<void label="Void action" />
load_file
Chargement dans le datasource du script player d’un fichier.
<load_file type="csv" label="Chargement" file="$real_path" delimiter=";" start_position="1">
<field position="2" target="ddn" type="date"/>
<field position="1" target="prenom"/>
<field position="0" target="nom"/>
</load_file>
<load_file type="fixed_len" label="Chargement" file="$real_path" start_position="1">
<field position="2" target="ddn" type="date" length="12"/>
<field position="1" target="prenom" length="16"/>
<field position="0" target="nom" length="10"/>
</load_file>
fill_tmp_table
Remplissage d’une table temporaire à partir des données présente dans le datasource du scriptplayer.
<fill_tmp_table table="import_table" label="Transfert" temporary_table="false" />
exec_code
Cette action est utilisée pour excuter du code de l’application. Cela permet d’exploiter les class métier développées pour une application.
<exec_code label="[LABEL]" class="[CLASS NAME]" method="[METHO NAME]" [PARAM1]="[VALUE]" [PARAM2]="[VALUE]" ... />
Les paramètres sont passés sous la forme d’un tableau d’option dans le constructeur de la class.
Exemple:
<exec_code label="Appel SAGA" class="Saga_Lib_Save_Manager" method="ImportSaveFile" save_file="$file1" />
<?php
class Saga_Lib_Save_Manager extends Core_Library_Project_BaseObject
{
/**
*
*/
public function SetSaveFile( $sSaveFile ) {
}
/**
*
*/
public function ImportSaveFile( ) {
}
}
import_users
Importe les utilisateurs qui se trouvent dans le datasource.
Dans le cas où le script est exécuté sur un Provider (OAuth), il est possible de spécifier des consumers, pour leur rattacher automatiquement les utilisateurs importés. Si le mode de synchronisation automatique est activé sur les consumers, les utilisateurs seront aussi créés sur les consumers.
<import_users label="[Label]" default_group="[Group name]" default_role="[Role Name]" >
<consumers>
<consumer>[Consumer A name]</consumer>
<consumer>[Consumer B name]</consumer>
</consumers>
</import_users>
Champs attendus dans le datasource:
- user_name (obligatoire)
- firstname
- lastname
- status
- locale
- password (obligatoire)
- cps_id
Exemple complet:
<load_file label="Load file" file="$file1" type="csv" delimiter=";" start_position="0" zipped="true">
<field position="0" target="user_name"/>
<field position="2" target="lastname"/>
<field position="3" target="firstname"/>
<field position="11" target="email"/>
<field position="12" target="password"/>
<field position="13" target="group"/>
<field position="14" target="role"/>
</load_file>
<import_users label="Insert users" default_group="main" default_role="admin" >
<consumers>
<consumer>Neonetidf</consumer>
</consumers>
</import_users>
Créer une action propre à l’application
Créer une class dans la librairie de l’application de la forme qui étend Core_Library_Resource_XML_ScriptPlayer_Action :
[ app lib prefixe ]_Resource_XML_ScriptPlayer_Action[ nom de l’action ]
Exemple:
<hello label="Hello world" param1="value" />
<?php
class Saga_Lib_Resource_XML_ScriptPlayer_Action_Hello extends Core_Library_Resource_XML_ScriptPlayer_Action
{
public function Exec() {
error_log( __CLASS__ . ' ' . $this->GetAttribute( 'param1' ) );
}
}
Pilotage d’une application
Voozanoo4 dispose d’un mécanisme permettant à une application de transmettre des fichiers à une autre application pour qu’elle les traite. Le traitement est fait par le script player (l’application source transmet le script qui sera executé par le script player).
Ce mécanisme est utile pour faire des imports de données par exemple. Il a été mis en place dans le cadre du projet BUDI (Bulk Dispatcher), et il repose sur un webservice.
Intégration
Le controller est présent uniquement dans le répertoire library/Controller/Ws/Script.php, il n’y a pas de module. Pour l’utiliser, il faut donc créer un module dans l’application:
Créer un module ws
avec un controller Script
dans l’application
cible ( APPLICATION_PATH/modules/ws/controllers/ScriptController.php ),
et hériter le controller Ws_ScriptController
de
Core_Library_Controller_Ws_Script
.
Les fichiers à traiter sont transmis par POST au webservice.
Le temps d’attente de la réponse du POST
est limité par un timeout, il
est possible de l’augmenter si nécessaire.
<?php
// 120s
$oClient->getHttpClient()->setConfig( array( 'timeout' => 120 ) );
Paramètres attendus:
zip_file_content
: fichier zip contenant le ou les fichiers à traiter, ainsi que le script destiné à être utilisé par le script playersignature_file
: une signature garantissant que les fichiers transmis l’ont bien été par une source reconnuescript_filename
: nom du script à l’intérieur du zip, cela permet de le retrouver et de le différencier des fichiers à traiterid_task
: identifiant unique, à faire varier s’il faut exécuter plusieurs tâches sur l’application
<?php
$oClient = new Zend_Rest_Client( $this->GetUrlWithAuth() );
$oClient->zip_file_content( base64_encode( file_get_contents( '/path/to/file.zip' ) ) );
$oClient->signature_file( base64_encode( file_get_contents( '/path/to/signature' ) ) );
$oClient->script_filename( 'script.xml' );
$oClient->id_task( 42 );
$oClient->post();
le fichier zip est transmis via la requête HTTP, ce qui est inutile au
cas où les deux applications communiquantes sont sur la même machine.
Dans ce cas, il est possible de remplacer ce paramètre par
zip_file_path
, le système fera une recopie du zip dans le répertoire
storage
de l’application.
<?php
: \$oClient->zip\_file\_path( \$zipFile );
Le webservice décompresse le fichier zip dans le répertoire storage de
l’application, dans un sous répertoire qui porte comme nom la valeur de
id_task
. Ce répertoire est automatiquement supprimé par le WS en
fonction du contexte (temps réel, ou processus parallèle).
La création du fichier Zip et de la signature se fait en utilisant la
class Core_Library_File_Manager
<?php
$aFiles[] = '/path/to/file1';
$aFiles[] = '/path/to/file2';
$aFiles[] = '/path/to/script.xml';
// Compression des fichiers récupérés
$oFileManager->ZipFiles( $aFiles, '/path/to/file.zip' );
// Création de la signature
$oFileManager->CreateFilesSignature(
$aFiles,
'/path/to/signature',
$destinationProjectKey
);
Réponses du webservice
idle
: l’application est disponibledone
: la tâche est terminéefailed
: la tâche est terminée mais a généré une erreurrunning
: la tâche est en cours de traitement
<?php
$oResult = $oClient->post();
/* @var $oResult Zend_Rest_Client_Result */
if ( false === $oResult->isSuccess() ) {
throw new Core_Library_Exception( sprintf( 'Consumer webservice return error %s', $oResult->message ) );
}
// $sStatus = 'idle' | 'done' | 'failed' | 'running'
$sStatus = (string) $oResult->response->status;
// En cas d'erreur, le message est présent dans la réponse
$oResult->response->error_message
Il est possible de récupérer le statut d’une tâche en interrogeant le
Webservice
via GET
, et en lui indiquant l’identifiant de la tâche.
La réponse est la même que pour un POST
, c’est à dire le statut de la
tâche, et selon, un message d’erreur.
<?php
$oClient = new Zend_Rest_Client( $sWsUrl . '/id_task/42' );
$oResult = $oClient->get();
Execution du traitement en parallèle
fork (compatible windows)
Le code ci-dessous permet de créer un nouveau processus.
<?php
protected function _execInBackground( $sCmd )
{
if ( substr( php_uname(), 0, 7 ) == "Windows" ) {
pclose( popen( "start /B " . $sCmd, "r" ) );
}
else {
exec( $sCmd . " > /dev/null &" );
}
}
Source: PHP.net
Dans le controller Ws_ScriptController
de
Core_Library_Controller_Ws_Script
, surcharger la méthode
_executeScript
qui lance le processus parallèle, et répond en
transmettant le statut running
. Le nouveau processus correspond au
script [application]_cli.php
, qui redispatche vers la class
[Application]CliApi
. (ajouter ces scripts s’il n’existent pas).
<?php
protected function _executeScript()
{
$sScriptPath =
APPLICATION_PATH . DIRECTORY_SEPARATOR .
'resources' . DIRECTORY_SEPARATOR .
'cron' . DIRECTORY_SEPARATOR .
'saga_cli.php';
if ( ! file_exists( $sScriptPath ) ) {
throw new Core_Library_Exception( sprintf( "Script not found (%s)", $sScriptPath ) );
}
$sCmd = sprintf(
"php \"%s\" mtd=execTask script_file=%s id=%d",
$sScriptPath,
escapeshellarg( $this->_getScriptWsManager()->GetScriptFile() ),
$this->_getScriptWsManager()->GetId()
);
$this->_execInBackground( $sCmd );
return array( 'status' => Core_Library_ScriptPlayer_Ws_Manager::STATUS_RUNNING );
}
Dans la class [Application]CliApi
, déclarer la méthode execTask
dans
laquelle sera fait le traitement en utilisant le script player ( le
traitement avec le script player est encapsulé dans la class
Core_Library_ScriptPlayer_Ws_Manager
).
<?php
public function execTask()
{
$oScriptManager = new Core_Library_ScriptPlayer_Ws_Manager( array(
'project' => Core_Library_Account::GetInstance()->GetCurrentProject(),
'script_file' => $this->getParamValue( 'script_file' ),
'id' => $this->getParamValue( 'id' )
));
try {
$aResult = $oScriptManager->ExecuteScript();
$oScriptManager->SaveStatusInStorage(
$aResult[ 'status' ],
( isset( $aResult[ 'error_message'] ) ? $aResult[ 'error_message'] : '' )
);
} catch ( Exception $e ) {
$oScriptManager->SaveStatusInStorage(
Core_Library_ScriptPlayer_Ws_Manager::STATUS_FAILED,
$e->getMessage()
);
}
}
Système de plugin (uniquement sur les serveurs d’Epiconcept)
Ce système se base sur le système de proxy
/plugin
: l’application
demande au proxy
de charger le plugin
de l’application, et de lui
transmettre une action à executer.
<?php
protected function _executeScript()
{
$oClient = new Core_Library_Zmq_Client();
$oClient->connect();
$sMSG = $oClient->loadPlugin(
'pexec1',
array( APPLICATION_PATH . "/resources/cron/pexec_cli.php", "mtd=loadPlugin" )
);
if ( 'OK' == $sMSG ) {
$sRep = $oClient->sendWork(
'pexec1',
'QOFF',
array(
'script_file' => $this->_getScriptWsManager()->GetScriptFile(),
'id' => $this->_getScriptWsManager()->GetId()
)
);
return array( ‘'status' => Core_Library_ScriptPlayer_Ws_Manager::STATUS_RUNNING );
}
return array( 'status' => Core_Library_ScriptPlayer_Ws_Manager::STATUS_FAILED );
}
Comme précédemment, il faut utiliser l’API CLI pour executer le code qui
charge le plugin
.
<?php
public function loadPlugin()
{
$oPlugin = new Core_Library_Zmq_Plugin( 'pexec1' );
$oPlugin->registerPlugin();
$oPlugin->waitWork( array( $this, '_exec' ) );
}
Une fois le plugin
chargé, la callback _exec
est appelée, c’est ici
que se trouve le code de traitement des fichiers.
<?php
public function _exec( $sJson )
{
$aData = Zend_Json::decode( $sJson );
$oScriptManager = new Core_Library_ScriptPlayer_Ws_Manager( array(
'project' => Core_Library_Account::GetInstance()->GetCurrentProject(),
'script_file' => $aData[ 'script_file' ],
'id' => $aData[ 'id' ]
));
try {
$aResult = $oScriptManager->ExecuteScript();
$oScriptManager->SaveStatusInStorage(
$aResult[ 'status' ],
( isset( $aResult[ 'error_message'] ) ? $aResult[ 'error_message'] : '' )
);
} catch ( Exception $e ) {
$oScriptManager->SaveStatusInStorage(
Core_Library_ScriptPlayer_Ws_Manager::STATUS_FAILED,
$e->getMessage()
);
}
}