Comparer les révisions

..

Pas de révisions en commun. "f67a7470ad1b50fd23de4a4fccf1523c785e88df" et "efcc4d312aed09f04967bab2b6774c7479ab109f" ont des historiques entièrement différents.

7 fichiers modifiés avec 380 ajouts et 17 suppressions

Voir le fichier

@ -34,21 +34,21 @@ ## Installation
``` ```
## Routes disponibles ## Routes disponibles
- ```baseURL/webroutes``` : rend une documentation des endpoints configurés. - ```baseURL```/webroutes : rend une documentation des endpoints configurés
- ```baseURL/logs``` : rend une interface de lecture des logs. - ```baseURL```/logs : rend une interface de lecture des logs
## Philosophie de développement ## Philosophie de développement
### Bridge Pattern ### Bridge Pattern
Afin de permettre une abstraction et une réusabilité accrue, l'applicatif est entendu de manière modulaire. Afin de permettre une hermécité, et une réusabilité accrue, l'applicatif est entendu de manière modulaire.
Chaque module peut disposer d'une classe publique et dispose nécessairement d'une classe privée. Chaque module peut disposer d'une classe publique et dispose nécessairement d'une classe privée.
La classe publique, qui étend la classe privée de son module, est la seule à pouvoir discuter avec le routeur, elle fait toutes les vérifications d'intégrité des données en entrée, gère la transaction avec la base donnée (commit/rollback), capture les levées d'exception; elle est la seule à communiquer en sortie avec le client. La classe publique, qui étend la classe privée de son module, est la seule à pouvoir discuter avec le routeur, elle fait toutes les vérifications d'intégrité des données en entrée, gère la transaction avec la base donnée (commit/rollback), elle est la seule à communiquer en sortie avec l'utilisateur.
__Une classe publique ne peut pas instancier une autre classe privée : elle dépend de sa classe mère.__ Totalement arbitraire, mais évite le spaghetti code, cela peut amener de la redondance sur les noms de méthodes, mais cela garantit une seule manière de faire transiter l'information. __Une classe publique ne peut pas instancier une autre classe privée : elle dépend de sa classe mère.__ Totalement arbitraire, mais évite le spaghetti code, cela peut amener de la redondance sur les noms de méthodes, mais ça garantit une seule manière de faire transiter l'information.
Chaque classe privée est abstraite de la classe publique, les opérations de calcul et de communication avec le SGBD y résident. Chaque classe privée est abstraite de la classe publique, les opérations de calcul sont fait dans la classe privée alors que la classe publique ne sert de contrôle des données.
Une classe privée peut utiliser une autre classe privée d'un autre module, ou d'une autre classe située en dehors du dossier ```app/Http/Controllers/Modules```. Dès lors la classe privée doit aussi comprendre les contrôles des arguments de méthodes mais ne capture pas les levées d'exception, elle ne fait qu'en émettre. Une classe privée peut utiliser une autre classe privée d'un autre module, ou d'une autre classe située en dehors du dossier ```app/Http/Controllers/Modules```
### Gestion des levées d'exceptions ### Gestion des levées d'exceptions
Afin de ne pas multiplier les gestions des exceptions personnalisées des différents modules et vendors, la classe publique se bornera à ne capter que deux types d'exceptions : ```Exception``` et ```QueryException```. Afin de ne pas multiplier les gestions des exceptions personnalisées des différents modules et vendors, la classe publique se bornera à ne capter que deux types d'exceptions : ```Exception``` et ```QueryException```.
@ -72,9 +72,9 @@ ### Architecture API
Pour une même route, avec la même syntaxe, en fonction de la méthode utilisée, l'action ne sera pas la même. Pour une même route, avec la même syntaxe, en fonction de la méthode utilisée, l'action ne sera pas la même.
Le retour de chaque route comporte au moins une clé : ```status```. Le retour de chaque route comporte au moins une clé : ```status```.
Cette clé prend en valeur 0 ou 1 sous la forme d'un entier. Cette clé peut prendre en valeur 0 ou 1 sous la forme d'un entier.
Quand la valeur est 1, l'opération, quelle qu'elle soit, est réussie, si la valeur est 0, une erreur est survenue. Quand la valeur est 1, l'opération, quelle qu'elle soit est réussie, si la valeur est 0, une erreur est survenue.
Si ```status``` est égal à 0, alors une autre clé est nécessairement disponible : ```msg```. Si status est égal à 0, alors une autre clé doit être nécessairement disponible : ```msg```.
La clé ```msg``` doit contenir le retour d'erreur pour le client, dans la langue voulue. La clé ```msg``` doit nécessairement contenir le retour pour l'utilisateur, dans sa langue.
On s'affranchit donc des codes HTTP de retour : ça fonctionne ou ça ne fonctionne pas. On s'affranchit donc des codes HTTP de retour : ça fonctionne ou ça ne fonctionne pas.

Voir le fichier

@ -0,0 +1,16 @@
<?php
namespace App\Http\Controllers\Core;
use App\Http\Controllers\Core\Controller;
class CommonController extends Controller{
/**
* Constructeur
*/
public function __construct() {
}
}

Voir le fichier

@ -2,6 +2,11 @@
namespace App\Http\Controllers\Core; namespace App\Http\Controllers\Core;
// use App\Http\Utility\arrayUtility;
// use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
// use Illuminate\Foundation\Bus\DispatchesJobs;
// use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController; use Illuminate\Routing\Controller as BaseController;
// Surcharge du controller principal // Surcharge du controller principal

Voir le fichier

@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers\Core;
use App\Http\Utility\debug;
class apiException extends \Exception{
public function __construct($message = null, $method = '', $code = 0, Throwable $previous = null) {
$trace = $this->getTrace();
if(is_array($trace)){
//$last_call = $trace[1]['class'].'\\'.$trace[1]['function'];
$last_call = $trace[1];
}else{
$last_call = $trace;
}
//$method = ($method!='') ? ( (is_array($method)) ? print_r($method,true) : $method ) : $last_call;
$messageLog = ($method!='') ? $method . ' : '. $message : $message;
debug::consign($messageLog,'Api_Log','ERROR');
debug::consign($last_call,'Api_Log','ERROR');
parent::__construct($message, $code, $previous);
}
// public function __toString() {
// return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
// }
}

Voir le fichier

@ -18,8 +18,7 @@ public function up()
$table->string('login')->unique(); $table->string('login')->unique();
$table->string('email')->unique(); $table->string('email')->unique();
$table->string('password'); $table->string('password');
$table->string('token')->nullable(); $table->string('token')->nullable();;
$table->string('ip')->nullable();
$table->integer('isadmin'); $table->integer('isadmin');
$table->integer('creation_date'); $table->integer('creation_date');
$table->integer('modification_date'); $table->integer('modification_date');

Voir le fichier

@ -1,6 +1,6 @@
<?php <?php
// Pourquoi pas Blade ? Parce que cette documentation doit rester fonctionnelle quoi qu'il en coûte. // Pourquoi pas Blade ? Parce que cette documentation doit rester fonctionnelle quoi qu'il en coûte.
// Si elle s'affiche, alors vos fichiers de routage sont correctement écris : elle sert aussi de validation du routing. // Si elle s'affiche, alors vos fichiers de routage sont correctement écris : ça sert aussi de validation du routing.
// Pourquoi pas de dépendances lié à l'architecture et du CSS en ligne par exemple : exactement pour les mêmes raisons : on s'affranchit totalement du moteur de Laravel en cas de souci, on ne respecte que le routeur. // Pourquoi pas de dépendances lié à l'architecture et du CSS en ligne par exemple : exactement pour les mêmes raisons : on s'affranchit totalement du moteur de Laravel en cas de souci, on ne respecte que le routeur.
use App\Http\Utility\routeCollector; use App\Http\Utility\routeCollector;
@ -434,7 +434,7 @@ function GenerateAnApiDoc($key,$apiArray){
<body> <body>
<div id="menu"> <div id="menu">
<div class="pure-menu"> <div class="pure-menu">
<a class="pure-menu-heading home" href="#home">🏠</a> <a class="pure-menu-heading home" href="#home">🏠</a>
'.$menu.' '.$menu.'
</div> </div>
@ -454,8 +454,8 @@ function GenerateAnApiDoc($key,$apiArray){
<pre><code class=\"lang-php\">return [ <pre><code class=\"lang-php\">return [
// nom de l'API et préfixe de l'URL, est unique au système // nom de l'API et préfixe de l'URL, est unique au système
'api'=>'apiname' 'api'=>'apiname'
// namespace de la classe publique // la classe publique LARAVEL connecté à cette route, relativement à /app/Http/Controllers/..
,'class'=>'public\\class\\namespace' ,'class'=>'entityOrModules\\path\\to\\publicfunction'
// Les possibles restrictions d'accès au groupe de route (FACULTATIF). Tous les routes de tous les types seront restreint par les accès utilisateur // Les possibles restrictions d'accès au groupe de route (FACULTATIF). Tous les routes de tous les types seront restreint par les accès utilisateur
// Restrictions possibles : // Restrictions possibles :
// - sys_admin : administrateur système // - sys_admin : administrateur système
@ -615,6 +615,301 @@ function GenerateAnApiDoc($key,$apiArray){
] ]
];</code></pre> ];</code></pre>
</div>"; </div>";
$html.="
<div class='deploybox'>
<div class='deploybtn'>Voir un exemple</div>
<pre><code class=\"lang-php\">return [
'api'=>'articlemanager'
,'class'=>'Modules\ArticleManager\ArticleManagerPublic'
,'get'=>[
[
'desc'=>'Récupère les prix de l\'article par la quantité',
'url'=>'{idtask}/getpricewithqty'
,'method'=>'getPriceWithQty'
,'params'=>[
'idtask' =>'\d+',
]
,'params_desc'=>[
'idtask' =>'Identifiant tâche'
]
,'get_params'=>[
'thpId' =>'\d+',
'qty' =>'\d+\.?\d*',
'discountId' =>'\d+',
'productUnitId' =>'\d+',
]
,'get_params_desc'=>[
'thpId' =>'Identifiant de ligne de tâche',
'qty' =>'Quantité',
'discountId' =>'Identifant remise',
'productUnitId' =>'Identifiant de l\'unité du produit',
]
],
[
'desc'=>'Récupère les prix de l\'article par un prix donné',
'url'=>'{idtask}/getpricewithprice'
,'method'=>'getPriceWithPrice'
,'params'=>[
'idtask' =>'\d+',
]
,'params_desc'=>[
'idtask' =>'Identifiant tâche'
]
,'get_params'=>[
'thpId' =>'\d+',
'qty' =>'\d+\.?\d*',
'discountId' =>'\d+',
'productUnitId' =>'\d+',
'priceType' =>'unitprice|packprice',
'price' =>'\d+\.?\d*',
]
,'get_params_desc'=>[
'thpId' =>'Identifiant de ligne de tâche',
'qty' =>'Quantité',
'discountId' =>'Identifant remise',
'productUnitId' =>'Identifiant de l\'unité du produit',
'priceType' =>'Le type de price souhaite : prix unitaire, prix conditionnement',
'price' =>'Prix',
]
],
[
'desc'=>'Recherche des articles',
'url'=>'{idtask}/search'
,'method'=>'search'
,'params'=>[
'idtask' =>'\d+',
]
,'params_desc'=>[
'idtask' =>'Identifiant tâche'
]
,'get_params'=>[
'search' =>'\w+',
]
,'get_params_desc'=>[
'search' =>'Terme de la recherche',
]
],
[
'desc'=>'Récupère un objet de grille Vgrid',
'url'=>'{idtask}/list/{gridType}/'
,'method'=>'getProductList'
,'params'=>[
'idtask' =>'\d+',
'gridType' =>'productall|productfromprice|alreadyemployed|compmodels|devices',
]
,'params_desc'=>[
'idtask' =>'Identifiant tâche',
'gridType' =>'Le type de grille souhaité : Tous les produits, Produit des tarifs, Produits déjà utilisés, Composant modèles, Machines',
]
],
]
,'post'=>[
[
'desc'=>'Ajoute un article en fonction de son type',
'url'=>'{idtask}/addarticle/'
,'method'=>'addArticle'
,'params'=>[
'idtask' =>'\d+',
]
,'params_desc'=>[
'idtask' =>'Identifiant tâche'
]
,'post_params'=>[
'pid' =>'\d+',
'type' =>'\w+',
'uid' =>'\d+',
]
,'post_params_desc'=>[
'pid' =>'Identifiant produit',
'type' =>'Type de produit : product, device',
'uid' =>'Identifiant de l\'unité',
]
],
[
'desc'=>'test JSON',
'url'=>'equipment'
,'method'=>'addArticle'
,'params'=>[
// 'idtask' =>'\d+',
]
,'params_desc'=>[
// 'idtask' =>'Identifiant tâche'
]
,'post_params'=>[
'data' =>'JSON',
]
,'post_params_desc'=>[
'data'=>[
'external_id'=>'Id externe (ID SAGE)',
'code'=>'Code de la société [REQUIRED]',
'name'=>'Nom de la société',
'email'=>'Email de la société',
'web_site'=>'Adresse web de la société',
'siret'=>'Siret de la société',
'parent_id'=>'Id de la société mère (centrale d\'achat)',
'parent_code'=>'Code de la société mère (centrale d\'achat)',
'parent_external_id'=>'Id externe de la société mère (centrale d\'achat)',
'contacts'=>[
[
'external_id'=>'Id externe (ID SAGE)',
'code'=>'Code du contact [REQUIRED]',
'name'=>'Nom du contact',
'first_name'=>'Prénom du contact',
'phone'=>'Téléphone du contact',
'email'=>'Email du contact',
'linkedin'=>'Adresse LinkedIn du contact',
],
[
'external_id'=>'Id externe (ID SAGE)',
'code'=>'Code du contact [REQUIRED]',
'name'=>'Nom du contact',
'first_name'=>'Prénom du contact',
'phone'=>'Téléphone du contact',
'email'=>'Email du contact',
'linkedin'=>'Adresse LinkedIn du contact',
]
],
'locations'=>[
'!COMMENT!_1'=>'Code adresse principale, Une seule adresse principale [REQUIRED]',
'main'=>[
'external_id'=>'Id externe (ID SAGE)',
'address1'=>'Zone d\'adresse 1 de l\'adresse principale',
'address2'=>'Zone d\'adresse 2 de l\'adresse principale',
'postal'=>'Code postale de l\'adresse principale',
'city'=>'Ville de l\'adresse principale',
'country'=>'Pays de l\'adresse principale',
'phone'=>'Téléphone de l\'adresse principale',
'email'=>'Email de l\'adresse principale',
],
'!COMMENT!_2'=>'Code adresse de livraison, Peut y en avoir plusieurs ',
'delivery'=>[
'external_id'=>'Id externe (ID SAGE)',
'address1'=>'Zone d\'adresse 1 de l\'adresse principale',
'address2'=>'Zone d\'adresse 2 de l\'adresse principale',
'postal'=>'Code postale de l\'adresse principale',
'city'=>'Ville de l\'adresse principale',
'country'=>'Pays de l\'adresse principale',
'phone'=>'Téléphone de l\'adresse principale',
'email'=>'Email de l\'adresse principale',
]
],
],
]
],
]
,'patch'=>[
[
'desc'=>'Met à jour un article',
'url'=>'{idtask}/updatearticle/'
,'method'=>'updateArticle'
,'params'=>[
'idtask' =>'\d+',
]
,'params_desc'=>[
'idtask' =>'Identifiant tâche'
]
,'post_params'=>[
'thpId'=>'\d+',
'unitId'=>'\d+',
'unitPrice'=>'\d+',
'packPrice'=>'\d+',
'name'=>'\w+',
'qty'=>'\d+\.?\d*',
]
,'opt_post_params'=>[
'deliveryDate'=>'\d{4}\/\d{2}\/\d{2}(?>\s?\d{2}:\d{2}|)',
'discountId'=>'\d+',
]
,'post_params_desc'=>[
'thpId'=>'Identifiant ligne de tâche',
'unitId'=>'Identifiant de l\'unité',
'unitPrice'=>'Prix unitaire',
'packPrice'=>'Prix de conditionnement',
'name'=>'Nom du produit',
'qty'=>'Quantité',
'deliveryDate'=>'Date de livraison',
'discountId'=>'Identifiant de la remise',
]
],
[
'desc'=>'Change l\'ordre d\'une ligne',
'url'=>'{idtask}/moveline/'
,'method'=>'moveLine'
,'params'=>[
'idtask' =>'\d+',
]
,'params_desc'=>[
'idtask' =>'Identifiant tâche'
]
,'post_params'=>[
'type' =>'product|comment',
'index' =>'\d+',
'oid' =>'\d+',
]
,'post_params_desc'=>[
'type' =>'Type de ligne : produit ou commentaire',
'index' =>'Ordre de la ligne dans la liste',
'oid' =>'Identifiant de la ligne',
]
],
[
'desc'=>'Met à jour un commentaire',
'url'=>'{idtask}/updatecomment/'
,'method'=>'changeComment'
,'params'=>[
'idtask' =>'\d+',
]
,'params_desc'=>[
'idtask' =>'Identifiant tâche'
]
,'post_params'=>[
'txt' =>'\w+',
'commentId' =>'\d+',
]
,'post_params_desc'=>[
'txt' =>'Text du commentaire',
'commentId' =>'Identifiant du commentaire',
]
],
]
,'delete'=>[
[
'desc'=>'Supprime un article',
'url'=>'{idtask}/deletearticle/'
,'method'=>'delArticle'
,'params'=>[
'idtask' =>'\d+',
]
,'params_desc'=>[
'idtask' =>'Identifiant tâche'
]
,'post_params'=>[
'thpId' =>'\d+',
]
,'post_params_desc'=>[
'thpId' =>'Identifiant ligne de tâche',
]
],
[
'desc'=>'Supprime un commentaire',
'url'=>'{idtask}/deletecomment/'
,'method'=>'delComment'
,'params'=>[
'idtask' =>'\d+',
]
,'params_desc'=>[
'idtask' =>'Identifiant tâche'
]
,'post_params'=>[
'thcId' =>'\d+',
]
,'post_params_desc'=>[
'thpId' =>'Identifiant ligne de tâche',
]
],
]
];</code></pre>
</div>";
$html.=' </div> $html.=' </div>
<div class="content">'.$body.'</div> <div class="content">'.$body.'</div>

18
tests/Unit/ExampleTest.php Fichier normal
Voir le fichier

@ -0,0 +1,18 @@
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function test_example()
{
$this->assertTrue(true);
}
}