Compare commits
2 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
656120f042 | ||
|
|
e3318d652c |
109 changed files with 21748 additions and 1708 deletions
|
|
@ -1,8 +1,8 @@
|
||||||
APP_NAME=Laravel
|
APP_NAME=Incollector
|
||||||
APP_ENV=local
|
APP_ENV=local
|
||||||
APP_KEY=
|
APP_KEY=
|
||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
APP_URL=http://localhost
|
APP_URL=https://incollector.s-web.test
|
||||||
|
|
||||||
LOG_CHANNEL=stack
|
LOG_CHANNEL=stack
|
||||||
LOG_DEPRECATIONS_CHANNEL=null
|
LOG_DEPRECATIONS_CHANNEL=null
|
||||||
|
|
|
||||||
47
.env.testing.example
Normal file
47
.env.testing.example
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
APP_NAME=Incollector
|
||||||
|
APP_ENV=testing
|
||||||
|
APP_KEY=base64:oRXNjgvWgIcboJN2O0NgaBt3kMUUfgyQ1Wv0S9/7sAA=
|
||||||
|
APP_DEBUG=true
|
||||||
|
APP_URL="http://127.0.0.1:8888"
|
||||||
|
|
||||||
|
LOG_CHANNEL=stack
|
||||||
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
|
DB_CONNECTION=mysql
|
||||||
|
DB_HOST=dev-mariadb
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_DATABASE=testing-incollector
|
||||||
|
DB_USERNAME=
|
||||||
|
DB_PASSWORD=
|
||||||
|
|
||||||
|
BROADCAST_DRIVER=log
|
||||||
|
CACHE_DRIVER=file
|
||||||
|
QUEUE_CONNECTION=database
|
||||||
|
SESSION_DRIVER=file
|
||||||
|
SESSION_LIFETIME=120
|
||||||
|
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PASSWORD=null
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
MAIL_MAILER=smtp
|
||||||
|
MAIL_HOST=mailhog
|
||||||
|
MAIL_PORT=1025
|
||||||
|
MAIL_USERNAME=null
|
||||||
|
MAIL_PASSWORD=null
|
||||||
|
MAIL_ENCRYPTION=null
|
||||||
|
MAIL_FROM_ADDRESS=incollector@ldc.fr
|
||||||
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
|
||||||
|
AWS_ACCESS_KEY_ID=
|
||||||
|
AWS_SECRET_ACCESS_KEY=
|
||||||
|
AWS_DEFAULT_REGION=us-east-1
|
||||||
|
AWS_BUCKET=
|
||||||
|
|
||||||
|
PUSHER_APP_ID=
|
||||||
|
PUSHER_APP_KEY=
|
||||||
|
PUSHER_APP_SECRET=
|
||||||
|
PUSHER_APP_CLUSTER=mt1
|
||||||
|
|
||||||
|
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||||
|
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||||
13
.gitignore
vendored
13
.gitignore
vendored
|
|
@ -1,9 +1,22 @@
|
||||||
/node_modules
|
/node_modules
|
||||||
/public/hot
|
/public/hot
|
||||||
/public/storage
|
/public/storage
|
||||||
|
/public/css
|
||||||
|
/public/js
|
||||||
|
/public/images
|
||||||
|
/public/fonts
|
||||||
|
/public/medias
|
||||||
|
/public/uploads
|
||||||
|
/public/assets
|
||||||
|
/public/mix-manifest.json
|
||||||
|
.vscode
|
||||||
|
|
||||||
/storage/*.key
|
/storage/*.key
|
||||||
|
/storage/inco/*
|
||||||
|
/storage/marque/*
|
||||||
/vendor
|
/vendor
|
||||||
.env
|
.env
|
||||||
|
.env.testing
|
||||||
.env.backup
|
.env.backup
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
docker-compose.override.yml
|
docker-compose.override.yml
|
||||||
|
|
|
||||||
24
.vscode/settings.json
vendored
24
.vscode/settings.json
vendored
|
|
@ -1,23 +1,23 @@
|
||||||
{
|
{
|
||||||
"phpcs.executablePath": "vendor/bin/phpcs",
|
"phpcs.executablePath": "vendor/bin/phpcs",
|
||||||
"workbench.colorCustomizations": {
|
"workbench.colorCustomizations": {
|
||||||
"activityBar.activeBackground": "#a714ac",
|
"activityBar.activeBackground": "#834329",
|
||||||
"activityBar.activeBorder": "#eb549a",
|
"activityBar.activeBorder": "#08180c",
|
||||||
"activityBar.background": "#a714ac",
|
"activityBar.background": "#834329",
|
||||||
"activityBar.foreground": "#e7e7e7",
|
"activityBar.foreground": "#e7e7e7",
|
||||||
"activityBar.inactiveForeground": "#e7e7e799",
|
"activityBar.inactiveForeground": "#e7e7e799",
|
||||||
"activityBarBadge.background": "#eb549a",
|
"activityBarBadge.background": "#08180c",
|
||||||
"activityBarBadge.foreground": "#15202b",
|
"activityBarBadge.foreground": "#e7e7e7",
|
||||||
"sash.hoverBorder": "#a714ac",
|
"sash.hoverBorder": "#834329",
|
||||||
"statusBar.background": "#0f437e",
|
"statusBar.background": "#5c2f1d",
|
||||||
"statusBar.foreground": "#e7e7e7",
|
"statusBar.foreground": "#e7e7e7",
|
||||||
"statusBarItem.hoverBackground": "#a714ac",
|
"statusBarItem.hoverBackground": "#834329",
|
||||||
"statusBarItem.remoteBackground": "#0f437e",
|
"statusBarItem.remoteBackground": "#5c2f1d",
|
||||||
"statusBarItem.remoteForeground": "#e7e7e7",
|
"statusBarItem.remoteForeground": "#e7e7e7",
|
||||||
"titleBar.activeBackground": "#0f437e",
|
"titleBar.activeBackground": "#5c2f1d",
|
||||||
"titleBar.activeForeground": "#e7e7e7",
|
"titleBar.activeForeground": "#e7e7e7",
|
||||||
"titleBar.inactiveBackground": "#0f437e99",
|
"titleBar.inactiveBackground": "#5c2f1d99",
|
||||||
"titleBar.inactiveForeground": "#e7e7e799"
|
"titleBar.inactiveForeground": "#e7e7e799"
|
||||||
},
|
},
|
||||||
"peacock.remoteColor": "#0f437e"
|
"peacock.remoteColor": "#5c2f1d"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
78
app/Console/Commands/LdcWebStatusChecker.php
Normal file
78
app/Console/Commands/LdcWebStatusChecker.php
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command Name: LDC Web Status Checker
|
||||||
|
* Description: Envoi des infos Serveur/Laravel vers LDC Web
|
||||||
|
* Version: 1.1
|
||||||
|
* Author: Vincent BRIDIER
|
||||||
|
* Author URI: vincent.bridier@ldc.fr
|
||||||
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
class LdcWebStatusChecker extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'ldcweb:statuschecker';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Envoi des infos Serveur/Laravel vers LDC Web';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$data = [];
|
||||||
|
$data['uuid'] = config('ldcweb.project_uuid');
|
||||||
|
|
||||||
|
// Sitemap.xml ?
|
||||||
|
if (file_exists(public_path() . '/sitemap.xml')) {
|
||||||
|
$data['sitemap'] = 'true';
|
||||||
|
} else {
|
||||||
|
$data['sitemap'] = 'false';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Robots.txt ?
|
||||||
|
if (file_exists(public_path() . '/robots.txt')) {
|
||||||
|
$data['robots'] = 'true';
|
||||||
|
} else {
|
||||||
|
$data['robots'] = 'false';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check logs.
|
||||||
|
$data['logs'] = 0;
|
||||||
|
$patherror = storage_path('/logs');
|
||||||
|
$files = glob($patherror . '/*.log');
|
||||||
|
if (count($files) > 0) {
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$data['logs'] = $data['logs'] + filesize($file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['code']['version'] = app()->version();
|
||||||
|
|
||||||
|
// Send by cURL.
|
||||||
|
$url = 'https://ldc-web.ldc.fr/api/status-checker';
|
||||||
|
$ch = curl_init($url);
|
||||||
|
$payload = ['token' => 'CE8S52Qq7stf5k8frF4T', 'datas' => json_encode($data)];
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_exec($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
49
app/Console/Commands/UserCreate.php
Normal file
49
app/Console/Commands/UserCreate.php
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
class UserCreate extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'user:create';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Command description';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$firstname = $this->ask('What is the firstname?');
|
||||||
|
$lastname = $this->ask('What is the lastname?');
|
||||||
|
$email = $this->ask('What is the email?');
|
||||||
|
$pwd = $this->secret('What is the password?');
|
||||||
|
|
||||||
|
$user = new User();
|
||||||
|
$user->firstname = $firstname;
|
||||||
|
$user->lastname = $lastname;
|
||||||
|
$user->email = $email;
|
||||||
|
$user->password = $pwd;
|
||||||
|
$user->saveOrFail();
|
||||||
|
|
||||||
|
$this->info('User created.');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
37
app/Console/Commands/XmlBrandReader.php
Normal file
37
app/Console/Commands/XmlBrandReader.php
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Jobs\Arcadia\XmlBrandReaderJob;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class XmlBrandReader extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'xml:brand';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Run the XmlBrandReaderJob manually';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$this->info('Running the Job, this could take some time.');
|
||||||
|
$job = new XmlBrandReaderJob();
|
||||||
|
$job->console = $this;
|
||||||
|
$job->handle();
|
||||||
|
$this->info('Job completed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,12 +2,11 @@
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\Jobs\XmlIncoReader as JobsXmlIncoReader;
|
use App\Jobs\Arcadia\XmlIncoReaderJob;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
class XmlIncoReader extends Command
|
class XmlIncoReader extends Command
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
*
|
*
|
||||||
|
|
@ -30,7 +29,7 @@ class XmlIncoReader extends Command
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->info('Running the Job, this could take some time.');
|
$this->info('Running the Job, this could take some time.');
|
||||||
$job = new JobsXmlIncoReader();
|
$job = new XmlIncoReaderJob();
|
||||||
$job->console = $this;
|
$job->console = $this;
|
||||||
$job->handle();
|
$job->handle();
|
||||||
$this->info('Job completed');
|
$this->info('Job completed');
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Console;
|
namespace App\Console;
|
||||||
|
|
||||||
|
use App\Console\Commands\LdcWebStatusChecker;
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||||
|
|
||||||
|
|
@ -12,9 +13,7 @@ class Kernel extends ConsoleKernel
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $commands = [
|
protected $commands = [];
|
||||||
//
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the application's command schedule.
|
* Define the application's command schedule.
|
||||||
|
|
@ -24,8 +23,7 @@ class Kernel extends ConsoleKernel
|
||||||
*/
|
*/
|
||||||
protected function schedule(Schedule $schedule)
|
protected function schedule(Schedule $schedule)
|
||||||
{
|
{
|
||||||
// $schedule->command('inspire')->hourly();
|
$schedule->command(LdcWebStatusChecker::class)->everyFifteenMinutes();
|
||||||
//$schedule->job(new XmlIncoReader)->weeklyOn(1, '8:00');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -35,8 +33,6 @@ class Kernel extends ConsoleKernel
|
||||||
*/
|
*/
|
||||||
protected function commands()
|
protected function commands()
|
||||||
{
|
{
|
||||||
$this->load(__DIR__.'/Commands');
|
$this->load(__DIR__ . '/Commands');
|
||||||
|
|
||||||
require base_path('routes/console.php');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
84
app/Enums/ApiError.php
Normal file
84
app/Enums/ApiError.php
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
use Kwaadpepper\Enum\BaseEnumRoutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This describes an API error.
|
||||||
|
*
|
||||||
|
* @method static self modelNotFound()
|
||||||
|
* The request model was not found (typically the customer)
|
||||||
|
*
|
||||||
|
* @method static self validationFailed()
|
||||||
|
* This means some data could not be validated,
|
||||||
|
* you may have to look into message to get the details
|
||||||
|
*
|
||||||
|
* @method static self apiAuthFailed()
|
||||||
|
* The authentification to the api failed. You may have to
|
||||||
|
* check into config or .env file for your service credentials
|
||||||
|
*
|
||||||
|
* @method static self methodNotAllowed()
|
||||||
|
* This http method is not allowed
|
||||||
|
*
|
||||||
|
* @method static self wrongTimeStamp()
|
||||||
|
* The timestamp given is wrong. This would happen in case if
|
||||||
|
* a replay attack scenario. Timestamps are provided in headers
|
||||||
|
* and in the http body
|
||||||
|
*
|
||||||
|
* @method static self missingParameter()
|
||||||
|
* The request is missing some parameters
|
||||||
|
*
|
||||||
|
* @method static self throttleRequestsException()
|
||||||
|
* Too many requests have been attempted. The is a throttle security.
|
||||||
|
* Normally you should not trigger this.
|
||||||
|
* If this happens fill a ticket on https://supportweb.ldc.fr
|
||||||
|
* with some technical details
|
||||||
|
*
|
||||||
|
* @method static self authorizationException()
|
||||||
|
* You are maybe trying to access data that is out of your range.
|
||||||
|
* This should not happen normally, it is here to allow you to handle this case.
|
||||||
|
* If this happens fill a ticket on https://supportweb.ldc.fr
|
||||||
|
* with some technical details
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class ApiError extends BaseEnumRoutable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Valeurs des possibilités de cet énumération (value)
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'modelNotFound' => 101,
|
||||||
|
'validationFailed' => 102,
|
||||||
|
'apiAuthFailed' => 119,
|
||||||
|
'methodNotAllowed' => 150,
|
||||||
|
'wrongTimeStamp' => 151,
|
||||||
|
'missingParameter' => 400,
|
||||||
|
'authorizationException' => 403,
|
||||||
|
'throttleRequestsException' => 429
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Libellés des possibilités de cet énumération (label)
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function labels(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'modelNotFound' => '%s introuvable.',
|
||||||
|
'validationFailed' => 'Impossible de valider les données',
|
||||||
|
'apiAuthFailed' => 'Echec de l\'authentification client à l\'Api LDC-CRM pour ce service',
|
||||||
|
'methodNotAllowed' => 'This method is not allowed for this request',
|
||||||
|
'wrongTimeStamp' => 'Your request is malformed, please check timestamp header and parameter',
|
||||||
|
'missingParameter' => 'The request is missing some parameters',
|
||||||
|
'authorizationException' => 'You are not autorized to access this',
|
||||||
|
'throttleRequestsException' => 'Throttle: too many request have been attempted, please retry later'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
16
app/Enums/ApiStatus.php
Normal file
16
app/Enums/ApiStatus.php
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
use Kwaadpepper\Enum\BaseEnumRoutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method static self OK()
|
||||||
|
* @method static self KO()
|
||||||
|
*
|
||||||
|
* @package LDCCrmClient
|
||||||
|
* @subpackage enums
|
||||||
|
*/
|
||||||
|
class ApiStatus extends BaseEnumRoutable
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace App\Exceptions;
|
namespace App\Exceptions;
|
||||||
|
|
||||||
use Exception;
|
use ErrorException;
|
||||||
|
|
||||||
class AbortXmlImportException extends Exception
|
class AbortXmlImportException extends ErrorException
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
101
app/Exceptions/Api/ApiErrorException.php
Normal file
101
app/Exceptions/Api/ApiErrorException.php
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions\Api;
|
||||||
|
|
||||||
|
use App\Enums\ApiError;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This would be thrown if any of the case
|
||||||
|
* referenced in \App\Enums\ApiError would
|
||||||
|
* occur within the crm
|
||||||
|
*
|
||||||
|
* On the client an ApiErrorException would
|
||||||
|
* also be throw with the ApiError corresponding to
|
||||||
|
* the situation
|
||||||
|
*/
|
||||||
|
class ApiErrorException extends Exception
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Api Error
|
||||||
|
* This value makes the error more explicit.
|
||||||
|
* Handling these errors are normal as inputs or
|
||||||
|
* request may be undesired or refused by the crm.
|
||||||
|
*
|
||||||
|
* @var \App\Enums\ApiError
|
||||||
|
*/
|
||||||
|
protected $error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an explicit error message,
|
||||||
|
* ou may print this to the front or not depending
|
||||||
|
* on $error value
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the http code returned by the crm.
|
||||||
|
* This value could be used to handle errors.
|
||||||
|
* But please take in consideration $error first
|
||||||
|
*
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
protected $httpCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new exception base on ApiError enum.
|
||||||
|
*
|
||||||
|
* @param ApiError $error
|
||||||
|
* @param integer $httpCode
|
||||||
|
* @param string $message
|
||||||
|
* @param Throwable|null $previous
|
||||||
|
*/
|
||||||
|
public function __construct(ApiError $error, int $httpCode = 400, string $message = '', Throwable $previous = null)
|
||||||
|
{
|
||||||
|
$this->error = $error;
|
||||||
|
$this->message = $message;
|
||||||
|
$this->httpCode = $httpCode;
|
||||||
|
parent::__construct(
|
||||||
|
strlen($this->message) ? $this->message : $error->label,
|
||||||
|
$error->value,
|
||||||
|
$previous
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Api Error
|
||||||
|
*
|
||||||
|
* @return ApiError
|
||||||
|
*/
|
||||||
|
public function getApiError(): ApiError
|
||||||
|
{
|
||||||
|
return $this->error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the http code
|
||||||
|
*
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
public function getStatusCode(): int
|
||||||
|
{
|
||||||
|
return $this->httpCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used by laravel only.
|
||||||
|
* It uses Log class to print the error.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function report()
|
||||||
|
{
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
Log::debug(sprintf('CRM API: %s', $this->getMessage()));
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
}
|
||||||
25
app/Exceptions/Api/ApplicationException.php
Normal file
25
app/Exceptions/Api/ApplicationException.php
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions\Api;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a critical error.
|
||||||
|
* The request could not be handled. You may dump any value you can
|
||||||
|
* get before submitting a ticket to https://supportweb.ldc.fr
|
||||||
|
*/
|
||||||
|
class ApplicationException extends Exception
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This is used by laravel only.
|
||||||
|
* It uses Log class to print the error.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function report()
|
||||||
|
{
|
||||||
|
Log::critical(sprintf('API: %s', $this->getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,20 @@
|
||||||
|
|
||||||
namespace App\Exceptions;
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use App\Enums\ApiError;
|
||||||
|
use App\Enums\ApiStatus;
|
||||||
|
use App\Exceptions\Api\ApiErrorException;
|
||||||
|
use App\Http\Resources\Api\V1\ApiResponseResource;
|
||||||
|
use App\Lib\Helper;
|
||||||
|
use Illuminate\Auth\Access\AuthorizationException;
|
||||||
|
use Illuminate\Auth\AuthenticationException;
|
||||||
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
|
use Illuminate\Http\Exceptions\ThrottleRequestsException;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
use Kwaadpepper\ExceptionHandler\Exceptions\ExceptionHandler;
|
use Kwaadpepper\ExceptionHandler\Exceptions\ExceptionHandler;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
class Handler extends ExceptionHandler
|
class Handler extends ExceptionHandler
|
||||||
|
|
@ -10,19 +23,18 @@ class Handler extends ExceptionHandler
|
||||||
/**
|
/**
|
||||||
* A list of the exception types that are not reported.
|
* A list of the exception types that are not reported.
|
||||||
*
|
*
|
||||||
* @var string[]
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $dontReport = [
|
protected $dontReport = [
|
||||||
//
|
ApiErrorException::class
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of the inputs that are never flashed for validation exceptions.
|
* A list of the inputs that are never flashed for validation exceptions.
|
||||||
*
|
*
|
||||||
* @var string[]
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $dontFlash = [
|
protected $dontFlash = [
|
||||||
'current_password',
|
|
||||||
'password',
|
'password',
|
||||||
'password_confirmation',
|
'password_confirmation',
|
||||||
];
|
];
|
||||||
|
|
@ -34,8 +46,125 @@ class Handler extends ExceptionHandler
|
||||||
*/
|
*/
|
||||||
public function register()
|
public function register()
|
||||||
{
|
{
|
||||||
$this->reportable(function (Throwable $e) {
|
}
|
||||||
//
|
|
||||||
});
|
/**
|
||||||
|
* Render an exception into an HTTP response.
|
||||||
|
*
|
||||||
|
* @param mixed $request
|
||||||
|
* @param \Throwable $e
|
||||||
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
|
*/
|
||||||
|
public function render($request, Throwable $e)
|
||||||
|
{
|
||||||
|
if (Helper::isRequestForApiV1($request) and $request->isJson()) {
|
||||||
|
return $this->handleApiV1Exception($request, $e);
|
||||||
|
} //end if
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
return parent::render($request, $e);
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a json response for any apiV2 error case
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param \Throwable $e
|
||||||
|
* @return JsonResponse
|
||||||
|
*/
|
||||||
|
private function handleApiV1Exception(Request $request, \Throwable $e): JsonResponse
|
||||||
|
{
|
||||||
|
switch (get_class($e)) {
|
||||||
|
case AuthenticationException::class:
|
||||||
|
/** @var AuthenticationException $e */
|
||||||
|
return $this->getReponse(
|
||||||
|
$request,
|
||||||
|
401,
|
||||||
|
ApiError::apiAuthFailed()->value,
|
||||||
|
sprintf('%s, %s', ApiError::apiAuthFailed()->label, $e->getMessage())
|
||||||
|
);
|
||||||
|
case MethodNotAllowedHttpException::class:
|
||||||
|
/** @var MethodNotAllowedHttpException $e */
|
||||||
|
return $this->getReponse(
|
||||||
|
$request,
|
||||||
|
$e->getStatusCode(),
|
||||||
|
ApiError::methodNotAllowed()->value,
|
||||||
|
sprintf('%s, %s', ApiError::methodNotAllowed()->label, $e->getMessage())
|
||||||
|
);
|
||||||
|
case ValidationException::class:
|
||||||
|
/** @var ValidationException $e */
|
||||||
|
return $this->getReponse(
|
||||||
|
$request,
|
||||||
|
$e->status,
|
||||||
|
ApiError::validationFailed()->value,
|
||||||
|
sprintf(ApiError::validationFailed()->label, $e->getMessage()),
|
||||||
|
['errors' => $e->errors()]
|
||||||
|
);
|
||||||
|
case ModelNotFoundException::class:
|
||||||
|
/** @var ModelNotFoundException $e */
|
||||||
|
return $this->getReponse(
|
||||||
|
$request,
|
||||||
|
404,
|
||||||
|
ApiError::modelNotFound()->value,
|
||||||
|
sprintf(
|
||||||
|
ApiError::modelNotFound()->label,
|
||||||
|
\array_slice(explode('\\', $e->getModel()), -1)[0]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
case ApiErrorException::class:
|
||||||
|
/** @var ApiErrorException $e */
|
||||||
|
return $this->getReponse(
|
||||||
|
$request,
|
||||||
|
$e->getStatusCode(),
|
||||||
|
$e->getCode(),
|
||||||
|
$e->getMessage()
|
||||||
|
);
|
||||||
|
case ThrottleRequestsException::class:
|
||||||
|
/** @var ThrottleRequestsException $e */
|
||||||
|
return $this->getReponse(
|
||||||
|
$request,
|
||||||
|
429,
|
||||||
|
ApiError::throttleRequestsException()->value,
|
||||||
|
ApiError::throttleRequestsException()->label
|
||||||
|
);
|
||||||
|
case AuthorizationException::class:
|
||||||
|
/** @var AuthorizationException $e */
|
||||||
|
return $this->getReponse(
|
||||||
|
$request,
|
||||||
|
403,
|
||||||
|
ApiError::authorizationException()->value,
|
||||||
|
ApiError::authorizationException()->label
|
||||||
|
);
|
||||||
|
} //end switch
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
if (config('app.env') === 'testing') {
|
||||||
|
// phpcs:ignore Squiz.PHP.DiscouragedFunctions.Found
|
||||||
|
print_r((string)$e);
|
||||||
|
}
|
||||||
|
return $this->getReponse($request, 500, $e->getCode(), $e->getMessage());
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate an ApiV2 json response
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param integer $httpCode
|
||||||
|
* @param integer $apiCode
|
||||||
|
* @param string $message
|
||||||
|
* @param array $data
|
||||||
|
* @return JsonResponse
|
||||||
|
*/
|
||||||
|
private static function getReponse(
|
||||||
|
Request $request,
|
||||||
|
int $httpCode,
|
||||||
|
int $apiCode,
|
||||||
|
string $message,
|
||||||
|
array $data = []
|
||||||
|
): JsonResponse {
|
||||||
|
return (new ApiResponseResource(
|
||||||
|
(object)array_merge($data, ['message' => $message, 'code' => $apiCode]),
|
||||||
|
ApiStatus::KO()
|
||||||
|
))->toResponse($request)->setStatusCode($httpCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace App\Exceptions;
|
namespace App\Exceptions;
|
||||||
|
|
||||||
use Exception;
|
use ErrorException;
|
||||||
|
|
||||||
class InvalidXmlException extends Exception
|
class InvalidXmlException extends ErrorException
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
app/Http/Controllers/Api/V1/ApiController.php
Normal file
19
app/Http/Controllers/Api/V1/ApiController.php
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api\V1;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
|
||||||
|
class ApiController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Just returns HTTP 200 response
|
||||||
|
*
|
||||||
|
* @return JsonResponse
|
||||||
|
*/
|
||||||
|
public function ping(): JsonResponse
|
||||||
|
{
|
||||||
|
return response()->json();
|
||||||
|
}
|
||||||
|
}
|
||||||
58
app/Http/Controllers/Api/V1/XmlIncoController.php
Normal file
58
app/Http/Controllers/Api/V1/XmlIncoController.php
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api\V1;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Api\V1\XmlIncosIndexRequest;
|
||||||
|
use App\Http\Resources\Api\V1\XmlIncoResource;
|
||||||
|
use App\Models\XmlInco;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||||
|
|
||||||
|
class XmlIncoController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns xmlIncos.
|
||||||
|
*
|
||||||
|
* @param XmlIncosIndexRequest $request
|
||||||
|
* - autorised is 'brand' with id
|
||||||
|
* @return JsonResource
|
||||||
|
*/
|
||||||
|
public function index(XmlIncosIndexRequest $request): ResourceCollection
|
||||||
|
{
|
||||||
|
/** @var \App\Models\ArcadiaBrand|null $arcadiaBrand */
|
||||||
|
$arcadiaBrand = $request->arcadiaBrand;
|
||||||
|
|
||||||
|
$xmlIncos = XmlInco::query();
|
||||||
|
|
||||||
|
if ($arcadiaBrand) {
|
||||||
|
// * Make sure we are accessing xmlIncos that belongs to our service.
|
||||||
|
$this->authorize('arcadiaBrandBelongsToService', $arcadiaBrand);
|
||||||
|
|
||||||
|
$xmlIncos->whereHas('arcadiaBrands', function ($q) use ($arcadiaBrand) {
|
||||||
|
$q->whereId($arcadiaBrand->id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return XmlIncoResource::collection($xmlIncos->simplePaginate(
|
||||||
|
$request->per_page,
|
||||||
|
['*'],
|
||||||
|
'page',
|
||||||
|
$request->page
|
||||||
|
)->items());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a specific xmlInco.
|
||||||
|
*
|
||||||
|
* @param XmlInco $xmlInco
|
||||||
|
* @return JsonResource
|
||||||
|
*/
|
||||||
|
public function show(XmlInco $xmlInco): JsonResource
|
||||||
|
{
|
||||||
|
// * Make sure we are accessing a xmlInco that belongs to our service.
|
||||||
|
$this->authorize('arcadiaBrandBelongsToService', $xmlInco->arcadiaBrand);
|
||||||
|
|
||||||
|
return new XmlIncoResource($xmlInco);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,5 +9,7 @@ use Illuminate\Routing\Controller as BaseController;
|
||||||
|
|
||||||
class Controller extends BaseController
|
class Controller extends BaseController
|
||||||
{
|
{
|
||||||
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
use AuthorizesRequests;
|
||||||
|
use DispatchesJobs;
|
||||||
|
use ValidatesRequests;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,12 @@ class Kernel extends HttpKernel
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $middleware = [
|
protected $middleware = [
|
||||||
// \App\Http\Middleware\TrustHosts::class,
|
|
||||||
\App\Http\Middleware\TrustProxies::class,
|
\App\Http\Middleware\TrustProxies::class,
|
||||||
\Fruitcake\Cors\HandleCors::class,
|
\Fruitcake\Cors\HandleCors::class,
|
||||||
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||||
\App\Http\Middleware\TrimStrings::class,
|
\App\Http\Middleware\TrimStrings::class,
|
||||||
|
\App\Http\Middleware\XssSanitizer::class,
|
||||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -33,16 +33,18 @@ class Kernel extends HttpKernel
|
||||||
\App\Http\Middleware\EncryptCookies::class,
|
\App\Http\Middleware\EncryptCookies::class,
|
||||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||||
\Illuminate\Session\Middleware\StartSession::class,
|
\Illuminate\Session\Middleware\StartSession::class,
|
||||||
// \Illuminate\Session\Middleware\AuthenticateSession::class,
|
\Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
],
|
],
|
||||||
|
|
||||||
'api' => [
|
'api' => [
|
||||||
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
|
||||||
'throttle:api',
|
'throttle:api',
|
||||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
\App\Http\Middleware\JsonOnly::class,
|
||||||
|
\App\Http\Middleware\ApiResponse::class,
|
||||||
|
\App\Http\Middleware\ApiCheckTimeStamps::class,
|
||||||
|
\Illuminate\Routing\Middleware\SubstituteBindings::class
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
38
app/Http/Middleware/ApiCheckTimeStamps.php
Normal file
38
app/Http/Middleware/ApiCheckTimeStamps.php
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Enums\ApiError;
|
||||||
|
use App\Exceptions\Api\ApiErrorException;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check Http Header Timestamp with request timestamp
|
||||||
|
* to prevent replay attacks
|
||||||
|
* https://restfulapi.net/security-essentials/
|
||||||
|
*/
|
||||||
|
class ApiCheckTimeStamps
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @return mixed
|
||||||
|
* @throws ApiErrorException With ApiError::wrongTimeStamp if timestamp has an incorrect format or is timed out.
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, \Closure $next)
|
||||||
|
{
|
||||||
|
if (!\is_string($request->timestamp) or !Carbon::hasFormat($request->timestamp, 'Y-m-d H:i:s')) {
|
||||||
|
throw new ApiErrorException(ApiError::wrongTimeStamp());
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
Carbon::parse($request->timestamp, 'utc')
|
||||||
|
->isBefore(Carbon::now('UTC')->subSeconds(config('api.timestampTimeout', 10)))
|
||||||
|
) {
|
||||||
|
throw new ApiErrorException(ApiError::wrongTimeStamp());
|
||||||
|
}
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
57
app/Http/Middleware/ApiResponse.php
Normal file
57
app/Http/Middleware/ApiResponse.php
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Enums\ApiStatus;
|
||||||
|
use App\Http\Resources\Api\V1\ApiResponseResource;
|
||||||
|
use App\Models\Service;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class ApiResponse
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @return mixed
|
||||||
|
* @throws \ErrorException If the received response is not a JsonResponse.
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, \Closure $next)
|
||||||
|
{
|
||||||
|
/** @var \Illuminate\Http\JsonResponse $response */
|
||||||
|
$response = $next($request);
|
||||||
|
|
||||||
|
if (\get_class($response) !== JsonResponse::class) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
throw new \ErrorException('All api responses should be json.');
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set no cache header.
|
||||||
|
$response->header('Cache-Control', 'No-Store');
|
||||||
|
|
||||||
|
/** @var \App\Models\Service|null */
|
||||||
|
if (\optional($service = app(Service::class))->id) {
|
||||||
|
$service->incrementStat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var object $data */
|
||||||
|
$data = $response->getData();
|
||||||
|
|
||||||
|
// If reponse is already a V2ApiResponse then just return it. This happens if error handler comes to action.
|
||||||
|
if (
|
||||||
|
isset($data->status) and
|
||||||
|
isset($data->data) and
|
||||||
|
isset($data->version) and
|
||||||
|
in_array($data->status, [ApiStatus::OK()->value, ApiStatus::KO()->value])
|
||||||
|
) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var \App\Enums\ApiStatus $status */
|
||||||
|
$status = intval($response->getStatusCode() / 100) == 2 ? ApiStatus::OK() : ApiStatus::KO();
|
||||||
|
return $response->setData(new ApiResponseResource((object)$response->getData(), $status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,5 @@ class EncryptCookies extends Middleware
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $except = [
|
protected $except = [
|
||||||
//
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
app/Http/Middleware/JsonOnly.php
Normal file
24
app/Http/Middleware/JsonOnly.php
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class JsonOnly
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, \Closure $next)
|
||||||
|
{
|
||||||
|
if (!$request->isJson() or !$request->expectsJson() or is_null(json_decode($request->getContent()))) {
|
||||||
|
abort(400, 'Invalid JSON');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,5 @@ class PreventRequestsDuringMaintenance extends Middleware
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $except = [
|
protected $except = [
|
||||||
//
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class TrustProxies extends Middleware
|
||||||
/**
|
/**
|
||||||
* The headers that should be used to detect proxies.
|
* The headers that should be used to detect proxies.
|
||||||
*
|
*
|
||||||
* @var int
|
* @var integer
|
||||||
*/
|
*/
|
||||||
protected $headers =
|
protected $headers =
|
||||||
Request::HEADER_X_FORWARDED_FOR |
|
Request::HEADER_X_FORWARDED_FOR |
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,5 @@ class VerifyCsrfToken extends Middleware
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $except = [
|
protected $except = [
|
||||||
//
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
app/Http/Middleware/XssSanitizer.php
Normal file
25
app/Http/Middleware/XssSanitizer.php
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
|
||||||
|
class XssSanitizer
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param mixed $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle($request, Closure $next)
|
||||||
|
{
|
||||||
|
$input = $request->all();
|
||||||
|
array_walk_recursive($input, function (&$input) {
|
||||||
|
$input = strip_tags($input);
|
||||||
|
});
|
||||||
|
$request->merge($input);
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
31
app/Http/Requests/Api/V1/PaginatorRequest.php
Normal file
31
app/Http/Requests/Api/V1/PaginatorRequest.php
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Api\V1;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class PaginatorRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return auth()->check();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'page' => 'sometimes|numeric|min:1',
|
||||||
|
'per_page' => 'sometimes|numeric|min:1|max:100',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
36
app/Http/Requests/Api/V1/XmlIncosIndexRequest.php
Normal file
36
app/Http/Requests/Api/V1/XmlIncosIndexRequest.php
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Api\V1;
|
||||||
|
|
||||||
|
use App\Models\ArcadiaBrand;
|
||||||
|
|
||||||
|
class XmlIncosIndexRequest extends PaginatorRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
$this->merge([
|
||||||
|
'brand' => $this->brand ? ArcadiaBrand::whereId($this->brand)->firstOrFail() : null
|
||||||
|
]);
|
||||||
|
return auth()->check();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return \array_merge(
|
||||||
|
parent::rules(),
|
||||||
|
[
|
||||||
|
'brand' => 'nullable'
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
app/Http/Resources/Api/V1/ApiResponseResource.php
Normal file
42
app/Http/Resources/Api/V1/ApiResponseResource.php
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources\Api\V1;
|
||||||
|
|
||||||
|
use App\Enums\ApiStatus;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
class ApiResponseResource extends JsonResource
|
||||||
|
{
|
||||||
|
/** @var stdClass */
|
||||||
|
protected $data;
|
||||||
|
|
||||||
|
/** @var \App\Enums\ApiStatus */
|
||||||
|
protected $status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param stdClass $data
|
||||||
|
* @param ApiStatus $status
|
||||||
|
*/
|
||||||
|
public function __construct(stdClass $data = null, ApiStatus $status)
|
||||||
|
{
|
||||||
|
$this->status = $status;
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the resource into an array.
|
||||||
|
*
|
||||||
|
* @param mixed $request
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
|
||||||
|
public function toArray($request): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'status' => $this->status->value,
|
||||||
|
'data' => (array)$this->data,
|
||||||
|
'version' => config('api.version')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
66
app/Http/Resources/Api/V1/XmlIncoResource.php
Normal file
66
app/Http/Resources/Api/V1/XmlIncoResource.php
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources\Api\V1;
|
||||||
|
|
||||||
|
use App\Models\XmlInco;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class XmlIncoResource extends JsonResource
|
||||||
|
{
|
||||||
|
/** @var array */
|
||||||
|
private static $skeleton = [
|
||||||
|
'id' => '',
|
||||||
|
'no_art' => '',
|
||||||
|
'prod_saison' => '',
|
||||||
|
'allegations' => '',
|
||||||
|
'conseil_prepa' => '',
|
||||||
|
'astuces' => '',
|
||||||
|
'descriptif' => '',
|
||||||
|
'arg_vente' => '',
|
||||||
|
'nb_portion' => '',
|
||||||
|
'typ_portion' => '',
|
||||||
|
'pds_moy_theo' => '',
|
||||||
|
'lib_conso' => '',
|
||||||
|
'lst_ing' => '',
|
||||||
|
'lib_allergene' => '',
|
||||||
|
'nutritional_values' => '',
|
||||||
|
'pictures' => '',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XmlIncoResource
|
||||||
|
*
|
||||||
|
* @param XmlInco $xmlInco
|
||||||
|
*/
|
||||||
|
public function __construct(XmlInco $xmlInco)
|
||||||
|
{
|
||||||
|
$this->xmlInco = $xmlInco;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the resource into an array.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request|null $request
|
||||||
|
* @return array
|
||||||
|
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||||
|
*/
|
||||||
|
public function toArray($request = null)
|
||||||
|
{
|
||||||
|
$data = array_intersect_key(
|
||||||
|
$this->xmlInco->toArray(),
|
||||||
|
$this->getSkeleton()
|
||||||
|
);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the resource skeletton
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function getSkeleton(): array
|
||||||
|
{
|
||||||
|
return self::$skeleton;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs\Arcadia;
|
||||||
|
|
||||||
use App\Enums\LogType;
|
use App\Enums\LogType;
|
||||||
use Exception;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Filesystem\Filesystem;
|
use Illuminate\Contracts\Filesystem\Filesystem;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
|
@ -30,9 +29,23 @@ abstract class XArcadiaFlux implements ShouldQueue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is for Laravel Console usage
|
* This is for Laravel Console usage
|
||||||
|
* @var \Illuminate\Console\Command|null
|
||||||
*/
|
*/
|
||||||
public $console = null;
|
public $console = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is for Laravel Console usage
|
||||||
|
* @var \Symfony\Component\Console\Helper\ProgressBar|null
|
||||||
|
*/
|
||||||
|
public $progressBar = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* invalid xml folder
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $invalidFolderName = 'invalid';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The relative folder where to archive data
|
* The relative folder where to archive data
|
||||||
*
|
*
|
||||||
|
|
@ -53,6 +66,7 @@ abstract class XArcadiaFlux implements ShouldQueue
|
||||||
* Check if the file extension
|
* Check if the file extension
|
||||||
*
|
*
|
||||||
* @param string $filename
|
* @param string $filename
|
||||||
|
* @param string $extension
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
protected function checkExtension(string $filename, string $extension): bool
|
protected function checkExtension(string $filename, string $extension): bool
|
||||||
|
|
@ -69,6 +83,81 @@ abstract class XArcadiaFlux implements ShouldQueue
|
||||||
return $isExtension;
|
return $isExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move the file to invalid folder
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function moveToInvalid(string $filename): void
|
||||||
|
{
|
||||||
|
$newFileName = $filename;
|
||||||
|
if (!$this->getDisk()->exists($filename)) {
|
||||||
|
$this->log(
|
||||||
|
sprintf(
|
||||||
|
'File %s could not be moved to invalid (not found)',
|
||||||
|
$filename
|
||||||
|
),
|
||||||
|
LogType::warning()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($this->getDisk()->exists(sprintf('%s/%s', $this->invalidFolderName, $filename))) {
|
||||||
|
$ext = \pathinfo($filename, \PATHINFO_EXTENSION);
|
||||||
|
$newFileName = \sprintf(
|
||||||
|
'%s_%d.%s',
|
||||||
|
\substr($filename, 0, 0 - (strlen($ext) + 1)),
|
||||||
|
\time(),
|
||||||
|
$ext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (!$this->getDisk()->move($filename, sprintf('%s/%s', $this->invalidFolderName, $newFileName))) {
|
||||||
|
$this->log(
|
||||||
|
sprintf(
|
||||||
|
'File %s could not be moved to invalid',
|
||||||
|
$filename
|
||||||
|
),
|
||||||
|
LogType::warning()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->log(
|
||||||
|
sprintf(
|
||||||
|
'File %s could not be moved to invalid',
|
||||||
|
$filename
|
||||||
|
),
|
||||||
|
LogType::warning()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suppression des fichiers dans
|
||||||
|
* invalid qui ont plus de 6 mois
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function clearOldInvalids(): void
|
||||||
|
{
|
||||||
|
$disk = $this->getDisk();
|
||||||
|
$invalids = $disk->files($this->invalidFolderName);
|
||||||
|
|
||||||
|
// /** @var File */
|
||||||
|
foreach ($invalids as $invalid) {
|
||||||
|
$pi = \pathinfo($invalid, \PATHINFO_BASENAME);
|
||||||
|
if (in_array($pi, $this->ignoreFiles)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($disk->lastModified($invalid) < strtotime(Carbon::now()->subMonth(6))) {
|
||||||
|
if (!$disk->delete($invalid)) {
|
||||||
|
Log::critical(sprintf('%s : Could not delete file %s', get_class($this), $invalid));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move the file to archives
|
* Move the file to archives
|
||||||
*
|
*
|
||||||
|
|
@ -107,7 +196,7 @@ abstract class XArcadiaFlux implements ShouldQueue
|
||||||
LogType::warning()
|
LogType::warning()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->log(
|
$this->log(
|
||||||
sprintf(
|
sprintf(
|
||||||
'File %s could not be moved to archives',
|
'File %s could not be moved to archives',
|
||||||
|
|
@ -160,6 +249,7 @@ abstract class XArcadiaFlux implements ShouldQueue
|
||||||
* @param string $message
|
* @param string $message
|
||||||
* @param LogType $logtype
|
* @param LogType $logtype
|
||||||
* @return void
|
* @return void
|
||||||
|
* @throws \ErrorException If logtype is not handled.
|
||||||
*/
|
*/
|
||||||
protected function log(string $message, LogType $logtype = null): void
|
protected function log(string $message, LogType $logtype = null): void
|
||||||
{
|
{
|
||||||
|
|
@ -187,8 +277,8 @@ abstract class XArcadiaFlux implements ShouldQueue
|
||||||
Log::critical($message);
|
Log::critical($message);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception(sprintf('%s : unhandled log type %s', get_class($this), $logtype->label));
|
throw new \ErrorException(sprintf('%s : unhandled log type %s', get_class($this), $logtype->label));
|
||||||
}//end switch
|
} //end switch
|
||||||
$this->consolePrint($message, $logtype);
|
$this->consolePrint($message, $logtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,9 +288,11 @@ abstract class XArcadiaFlux implements ShouldQueue
|
||||||
* @param string $message
|
* @param string $message
|
||||||
* @param LogType $logtype
|
* @param LogType $logtype
|
||||||
* @return void
|
* @return void
|
||||||
|
* @throws \ErrorException If logtype is not handled.
|
||||||
*/
|
*/
|
||||||
protected function consolePrint(string $message, LogType $logtype): void
|
protected function consolePrint(string $message, LogType $logtype): void
|
||||||
{
|
{
|
||||||
|
$this->clearProgressBarFromLine();
|
||||||
if ($this->console) {
|
if ($this->console) {
|
||||||
switch ($logtype->value) {
|
switch ($logtype->value) {
|
||||||
case LogType::debug()->value:
|
case LogType::debug()->value:
|
||||||
|
|
@ -216,8 +308,59 @@ abstract class XArcadiaFlux implements ShouldQueue
|
||||||
$this->console->error($message);
|
$this->console->error($message);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception(sprintf('%s : unhandled log type %s', get_class($this), $logtype->label));
|
throw new \ErrorException(sprintf('%s : unhandled log type %s', get_class($this), $logtype->label));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start ProgressBar
|
||||||
|
*
|
||||||
|
* @param integer $total
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function startProgressBar(int $total)
|
||||||
|
{
|
||||||
|
if ($this->console) {
|
||||||
|
$this->progressBar = $this->console->getOutput()
|
||||||
|
->createProgressBar($total - 1);
|
||||||
|
$this->progressBar->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the line to write someting
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function clearProgressBarFromLine()
|
||||||
|
{
|
||||||
|
if ($this->progressBar) {
|
||||||
|
$this->progressBar->clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment 1 on progress bar
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function incrementProgressBar()
|
||||||
|
{
|
||||||
|
if ($this->progressBar) {
|
||||||
|
$this->progressBar->advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End the progress bar
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function endProgressBar()
|
||||||
|
{
|
||||||
|
if ($this->progressBar) {
|
||||||
|
$this->progressBar->finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
55
app/Jobs/Arcadia/XmlBrandReaderJob.php
Normal file
55
app/Jobs/Arcadia/XmlBrandReaderJob.php
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs\Arcadia;
|
||||||
|
|
||||||
|
use App\Models\ArcadiaBrand;
|
||||||
|
|
||||||
|
class XmlBrandReaderJob extends XmlJobs
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The xsd file path used to validate files
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
protected $xsdSchemaPath = 'resources/documents/arcadia/marque.xsd';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disk name from config/filesystems.php
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $disk = 'arcadia_brand';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array Columns to take from the xml.
|
||||||
|
*/
|
||||||
|
protected $takeColumns = [
|
||||||
|
'codMarq' => 'arcadia_id',
|
||||||
|
'libMarq' => 'libelle',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fields that are allowed to be null
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $nullableFields = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique keys for updating rows
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $uniqueKeyField = ['arcadia_id'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->parseData('dam', ArcadiaBrand::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,20 +1,36 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs\Arcadia;
|
||||||
|
|
||||||
use App\Exceptions\AbortXmlImportException;
|
use App\Exceptions\AbortXmlImportException;
|
||||||
use App\Models\XmlInco;
|
use App\Models\XmlInco;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
use SimpleXMLElement;
|
use SimpleXMLElement;
|
||||||
|
|
||||||
class XmlIncoReader extends XmlJobs
|
class XmlIncoReaderJob extends XmlJobs
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* The xsd file path used to validate files
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
protected $xsdSchemaPath = 'resources/documents/inco/incollector.xsd';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disk name from config/filesystems.php
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
protected $disk = 'inco';
|
protected $disk = 'inco';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage path
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
protected $imagesPath = 'images';
|
protected $imagesPath = 'images';
|
||||||
|
|
||||||
protected $xsdFile = 'xsd/incollector.xsd';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* éléments à la racine : nom du noeud
|
* éléments à la racine : nom du noeud
|
||||||
* sous éléments d'un noeud PARENT :
|
* sous éléments d'un noeud PARENT :
|
||||||
|
|
@ -29,31 +45,35 @@ class XmlIncoReader extends XmlJobs
|
||||||
* 'EXPLOITANT' => 'exploitant', // Consider this sub array
|
* 'EXPLOITANT' => 'exploitant', // Consider this sub array
|
||||||
* 'EXPLOITANT.ID_SOCIETE' => 'id_societe',
|
* 'EXPLOITANT.ID_SOCIETE' => 'id_societe',
|
||||||
* 'EXPLOITANT.NOM_SOCIETE' => 'nom_societe',
|
* 'EXPLOITANT.NOM_SOCIETE' => 'nom_societe',
|
||||||
|
* @var array Columns to take from the xml.
|
||||||
*/
|
*/
|
||||||
protected $takeColumns = [
|
protected $takeColumns = [
|
||||||
'NO_ART' => 'id', // primary key on model
|
'NO_ART' => 'id',
|
||||||
|
// primary key on model
|
||||||
//this wil not return anything cf doc above
|
// this wil not return anything cf doc above
|
||||||
'ID_SOCIETE' => 'id_societe',
|
'ID_SOCIETE' => 'id_societe',
|
||||||
'NOM_SOCIETE' => 'nom_societe',
|
'NOM_SOCIETE' => 'nom_societe',
|
||||||
|
|
||||||
'PROD_SAISON' => 'prod_saison',
|
'PROD_SAISON' => 'prod_saison',
|
||||||
'NOM' => 'nom',
|
'NOM' => 'nom',
|
||||||
'NUMERO' => 'numero',
|
'NUMERO' => 'numero',
|
||||||
'TAILLE_REF' => 'taille_ref', //$poidsRef censé être dans INCO_COMPO (et généralement 100g)
|
'TAILLE_REF' => 'taille_ref',
|
||||||
|
// $poidsRef censé être dans INCO_COMPO (et généralement 100g)
|
||||||
'ALLEGATIONS' => 'allegations',
|
'ALLEGATIONS' => 'allegations',
|
||||||
'CONSEIL_PREPA' => 'conseil_prepa',
|
'CONSEIL_PREPA' => 'conseil_prepa',
|
||||||
'ASTUCES' => 'astuces',
|
'ASTUCES' => 'astuces',
|
||||||
'DESCRIPTIF' => 'descriptif', //$descriptif
|
'DESCRIPTIF' => 'descriptif',
|
||||||
|
// $descriptif
|
||||||
'ARG_VENTE' => 'arg_vente',
|
'ARG_VENTE' => 'arg_vente',
|
||||||
'NB_PORTION' => 'nb_portion',
|
'NB_PORTION' => 'nb_portion',
|
||||||
'TYP_PORTION' => 'typ_portion',
|
'TYP_PORTION' => 'typ_portion',
|
||||||
'PDS_MOY_THEO' => 'pds_moy_theo',
|
'PDS_MOY_THEO' => 'pds_moy_theo',
|
||||||
'LIB_CONSO' => 'lib_conso',
|
'LIB_CONSO' => 'lib_conso',
|
||||||
'LST_ING' => 'lst_ing', //$ingredients
|
'LST_ING' => 'lst_ing',
|
||||||
|
// $ingredients
|
||||||
// list alergene
|
// list alergene
|
||||||
'LST_ALLERGENE' => 'lst_allergene', // Consider this sub array
|
'LST_ALLERGENE' => 'lst_allergene',
|
||||||
|
// Consider this sub array
|
||||||
'LST_ALLERGENE.LIB_ALLERGENE' => 'lib_allergene',
|
'LST_ALLERGENE.LIB_ALLERGENE' => 'lib_allergene',
|
||||||
|
|
||||||
// Allegations
|
// Allegations
|
||||||
|
|
@ -62,11 +82,13 @@ class XmlIncoReader extends XmlJobs
|
||||||
'ALLEGAT_AUTRE' => 'allegat_autre',
|
'ALLEGAT_AUTRE' => 'allegat_autre',
|
||||||
|
|
||||||
|
|
||||||
'INCO_COMPO' => 'inco_compo', // Consider this sub array
|
'INCO_COMPO' => 'inco_compo',
|
||||||
|
// Consider this sub array
|
||||||
'INCO_COMPO.NOM' => 'nom',
|
'INCO_COMPO.NOM' => 'nom',
|
||||||
'INCO_COMPO.TAILLE_REF' => 'taille_ref',
|
'INCO_COMPO.TAILLE_REF' => 'taille_ref',
|
||||||
'INCO_COMPO.VAL_ENERG_KJ_NUT' => 'val_energ_kj_nut',
|
'INCO_COMPO.VAL_ENERG_KJ_NUT' => 'val_energ_kj_nut',
|
||||||
'INCO_COMPO.VAL_ENERG_KJ_R' => 'val_energ_kj_r', //_R apports de la taille_ref
|
'INCO_COMPO.VAL_ENERG_KJ_R' => 'val_energ_kj_r',
|
||||||
|
// _R apports de la taille_ref
|
||||||
'INCO_COMPO.VAL_ENERG_KCAL_R' => 'val_energ_kcal_r',
|
'INCO_COMPO.VAL_ENERG_KCAL_R' => 'val_energ_kcal_r',
|
||||||
'INCO_COMPO.VAL_ENERG_KCAL_NUT' => 'val_energ_kcal_nut',
|
'INCO_COMPO.VAL_ENERG_KCAL_NUT' => 'val_energ_kcal_nut',
|
||||||
'INCO_COMPO.MAT_GRASS_R' => 'mat_grass_r',
|
'INCO_COMPO.MAT_GRASS_R' => 'mat_grass_r',
|
||||||
|
|
@ -87,7 +109,8 @@ class XmlIncoReader extends XmlJobs
|
||||||
'INCO_COMPO.ENER_E14_R' => 'ener_e14_r',
|
'INCO_COMPO.ENER_E14_R' => 'ener_e14_r',
|
||||||
'INCO_COMPO.FRUITS_R' => 'fruits_r',
|
'INCO_COMPO.FRUITS_R' => 'fruits_r',
|
||||||
'INCO_COMPO.FRUITS_COQUE_SECH_R' => 'fruits_coque_sech_r',
|
'INCO_COMPO.FRUITS_COQUE_SECH_R' => 'fruits_coque_sech_r',
|
||||||
'INCO_COMPO.POURCENT_APPORT_REF_E14_R' => 'pourcent_apport_ref_e14_r', //POURCENT_*_P apports journaliers par portion
|
'INCO_COMPO.POURCENT_APPORT_REF_E14_R' => 'pourcent_apport_ref_e14_r',
|
||||||
|
// POURCENT_*_P apports journaliers par portion
|
||||||
'INCO_COMPO.ENER_KJO_R' => 'ener_kjo_r',
|
'INCO_COMPO.ENER_KJO_R' => 'ener_kjo_r',
|
||||||
'INCO_COMPO.POURCENT_APPORT_REF_KJO_R' => 'pourcent_apport_ref_kjo_r',
|
'INCO_COMPO.POURCENT_APPORT_REF_KJO_R' => 'pourcent_apport_ref_kjo_r',
|
||||||
'INCO_COMPO.POURCENT_APPORT_REF_FAT_R' => 'pourcent_apport_ref_fat_r',
|
'INCO_COMPO.POURCENT_APPORT_REF_FAT_R' => 'pourcent_apport_ref_fat_r',
|
||||||
|
|
@ -104,8 +127,10 @@ class XmlIncoReader extends XmlJobs
|
||||||
'INCO_COMPO.SALTEQ_R' => 'salteq_r',
|
'INCO_COMPO.SALTEQ_R' => 'salteq_r',
|
||||||
'INCO_COMPO.POURCENT_APPORT_REF_SALTEQ_R' => 'pourcent_apport_ref_salteq_r',
|
'INCO_COMPO.POURCENT_APPORT_REF_SALTEQ_R' => 'pourcent_apport_ref_salteq_r',
|
||||||
'INCO_COMPO.APPORT_REF' => 'apport_ref',
|
'INCO_COMPO.APPORT_REF' => 'apport_ref',
|
||||||
'INCO_COMPO.TAILLE_PORT' => 'taille_port', //$poidsPortion
|
'INCO_COMPO.TAILLE_PORT' => 'taille_port',
|
||||||
'INCO_COMPO.VAL_ENERG_KJ_P' => 'val_energ_kj_p', //_P apports par portion
|
// $poidsPortion
|
||||||
|
'INCO_COMPO.VAL_ENERG_KJ_P' => 'val_energ_kj_p',
|
||||||
|
// _P apports par portion
|
||||||
'INCO_COMPO.VAL_ENERG_KCAL_P' => 'val_energ_kcal_p',
|
'INCO_COMPO.VAL_ENERG_KCAL_P' => 'val_energ_kcal_p',
|
||||||
'INCO_COMPO.MAT_GRASS_P' => 'mat_grass_p',
|
'INCO_COMPO.MAT_GRASS_P' => 'mat_grass_p',
|
||||||
'INCO_COMPO.AGS_P' => 'ags_p',
|
'INCO_COMPO.AGS_P' => 'ags_p',
|
||||||
|
|
@ -147,8 +172,10 @@ class XmlIncoReader extends XmlJobs
|
||||||
'INCO_COMPO.COMPOSANTE_N' => 'composante_n',
|
'INCO_COMPO.COMPOSANTE_N' => 'composante_n',
|
||||||
'INCO_COMPO.COMPOSANTE_P' => 'composante_p',
|
'INCO_COMPO.COMPOSANTE_P' => 'composante_p',
|
||||||
'INCO_COMPO.POURCENT_FRUITS' => 'pourcent_fruits',
|
'INCO_COMPO.POURCENT_FRUITS' => 'pourcent_fruits',
|
||||||
'INCO_COMPO.SCORE_NUTRI' => 'score_nutri', //du coup nutriscore ?
|
'INCO_COMPO.SCORE_NUTRI' => 'score_nutri',
|
||||||
'INCO_COMPO.CLASSEMENT' => 'classement', //$nutriscore mais pas censé être dans INCO_COMPO
|
// du coup nutriscore ?
|
||||||
|
'INCO_COMPO.CLASSEMENT' => 'classement',
|
||||||
|
// $nutriscore mais pas censé être dans INCO_COMPO
|
||||||
'INCO_COMPO.MAT_GRASS_R_PREC' => 'mat_grass_r_prec',
|
'INCO_COMPO.MAT_GRASS_R_PREC' => 'mat_grass_r_prec',
|
||||||
'INCO_COMPO.AGS_R_PREC' => 'ags_r_prec',
|
'INCO_COMPO.AGS_R_PREC' => 'ags_r_prec',
|
||||||
'INCO_COMPO.GLUCIDES_R_PREC' => 'glucides_r_prec',
|
'INCO_COMPO.GLUCIDES_R_PREC' => 'glucides_r_prec',
|
||||||
|
|
@ -161,6 +188,11 @@ class XmlIncoReader extends XmlJobs
|
||||||
'INCO_COMPO.FRUITS_COQUE_SECH_R_PREC' => 'fruits_coque_sech_r_prec',
|
'INCO_COMPO.FRUITS_COQUE_SECH_R_PREC' => 'fruits_coque_sech_r_prec',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fields that are allowed to be null
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
protected $nullableFields = [
|
protected $nullableFields = [
|
||||||
'taille_ref',
|
'taille_ref',
|
||||||
'conseil_prepa',
|
'conseil_prepa',
|
||||||
|
|
@ -274,8 +306,18 @@ class XmlIncoReader extends XmlJobs
|
||||||
'inco_compo.fruits_coque_sech_r_prec'
|
'inco_compo.fruits_coque_sech_r_prec'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique keys for updating rows
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
protected $uniqueKeyField = ['id'];
|
protected $uniqueKeyField = ['id'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Columns to merge in
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
protected $mergeColumns = [
|
protected $mergeColumns = [
|
||||||
'allegations' => [
|
'allegations' => [
|
||||||
'allegat_sante',
|
'allegat_sante',
|
||||||
|
|
@ -381,15 +423,20 @@ class XmlIncoReader extends XmlJobs
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->parseData('INCO', XmlInco::class, function (SimpleXMLElement $xmlData, array &$line) {
|
$this->parseData('INCO', XmlInco::class, function (SimpleXMLElement $xmlData, array &$line) {
|
||||||
|
|
||||||
// merge columns
|
// * merge columns
|
||||||
foreach ($this->mergeColumns as $key => $values) {
|
foreach ($this->mergeColumns as $key => $values) {
|
||||||
$line[$key] = [];
|
$line[$key] = [];
|
||||||
// merge columns and remove merged columns from line
|
// * merge columns and remove merged columns from line
|
||||||
array_map(function ($kValue) use (&$line, $key) {
|
array_map(function ($kValue) use (&$line, $key) {
|
||||||
if (array_key_exists($kValue, $line)) {
|
if (array_key_exists($kValue, $line)) {
|
||||||
$line[$key][$kValue] = $line[$kValue];
|
$line[$key][$kValue] = $line[$kValue];
|
||||||
|
|
@ -400,22 +447,33 @@ class XmlIncoReader extends XmlJobs
|
||||||
$line[$key] = implode(',', array_filter($line[$key]));
|
$line[$key] = implode(',', array_filter($line[$key]));
|
||||||
$line[$key] = strlen($line[$key]) ? $line[$key] : null;
|
$line[$key] = strlen($line[$key]) ? $line[$key] : null;
|
||||||
}
|
}
|
||||||
//json column cast into array in model => no need for encode
|
// json column cast into array in model => no need for encode
|
||||||
// if ($key == 'nutritional_values') {
|
// if ($key == 'nutritional_values') {
|
||||||
// $line[$key] = json_encode($line[$key], \JSON_FORCE_OBJECT);
|
// $line[$key] = json_encode($line[$key], \JSON_FORCE_OBJECT);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Boolean cast prod_saison
|
// * Boolean cast prod_saison
|
||||||
$line['prod_saison'] = isset($line['prod_saison']) ?
|
$line['prod_saison'] = isset($line['prod_saison']) ?
|
||||||
(strtolower($line['prod_saison']) == 'non' ? false : true) : false;
|
(strtolower($line['prod_saison']) == 'non' ? false : true) : false;
|
||||||
|
|
||||||
$arImgPaths = $this->parseImage($xmlData);
|
/** @var \Illuminate\Database\Eloquent\Model|null $model */
|
||||||
$line['picture'] = $arImgPaths;
|
$model = XmlInco::where($this->getUniqueValues($line))->first();
|
||||||
|
|
||||||
|
$line['pictures'] = $this->updateOrCreateImagesFiles(
|
||||||
|
$this->parseImages($xmlData),
|
||||||
|
$model
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private function parseImage(SimpleXMLElement $xmlData)
|
/**
|
||||||
|
* Parse images
|
||||||
|
*
|
||||||
|
* @param SimpleXMLElement $xmlData
|
||||||
|
* @return array List of arrays [imgNamePath, imageData]
|
||||||
|
*/
|
||||||
|
private function parseImages(SimpleXMLElement $xmlData): array
|
||||||
{
|
{
|
||||||
$images = $xmlData->INCO->MEDIA_VISUEL_EAI->MEDIA_VISUEL_EAI;
|
$images = $xmlData->INCO->MEDIA_VISUEL_EAI->MEDIA_VISUEL_EAI;
|
||||||
$arImgPaths = array();
|
$arImgPaths = array();
|
||||||
|
|
@ -423,21 +481,20 @@ class XmlIncoReader extends XmlJobs
|
||||||
|
|
||||||
foreach ($images as $image) {
|
foreach ($images as $image) {
|
||||||
$imageData = trim((string)$image);
|
$imageData = trim((string)$image);
|
||||||
if (strlen($imageData)) {
|
if (strlen($imageData) and $imageData = base64_decode($imageData)) {
|
||||||
$imageData = base64_decode($imageData);
|
|
||||||
$imgName = strval($xmlData->INCO->INCO_COMPO->TBL_INCO_COMPO->ID_INCO);
|
$imgName = strval($xmlData->INCO->INCO_COMPO->TBL_INCO_COMPO->ID_INCO);
|
||||||
if(empty($imgName)) {
|
if (empty($imgName)) {
|
||||||
$imgName = strval($xmlData->INCO->NO_ART);
|
$imgName = strval($xmlData->INCO->NO_ART);
|
||||||
}
|
}
|
||||||
$imgName = $imgName."_".$index;
|
$imgName = $imgName . "_" . $index;
|
||||||
$imgNamePath = sprintf('%s/%s', rtrim($this->imagesPath, '/'), $imgName);
|
$imgNamePath = sprintf('%s/%s', rtrim($this->imagesPath, '/'), $imgName);
|
||||||
|
|
||||||
$disk = $this->getDisk();
|
|
||||||
|
|
||||||
$info = getimagesizefromstring($imageData);
|
$info = getimagesizefromstring($imageData);
|
||||||
if($info !== false) {
|
if ($info === false) {
|
||||||
$mime = $info['mime'];
|
Log::debug("Could not get file info from $imgName");
|
||||||
switch ($mime) {
|
continue;
|
||||||
|
}
|
||||||
|
switch ($info['mime'] ?? '') {
|
||||||
case 'image/jpeg':
|
case 'image/jpeg':
|
||||||
$imgExtension = 'jpg';
|
$imgExtension = 'jpg';
|
||||||
break;
|
break;
|
||||||
|
|
@ -448,24 +505,112 @@ class XmlIncoReader extends XmlJobs
|
||||||
$imgExtension = 'gif';
|
$imgExtension = 'gif';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new AbortXmlImportException(sprintf('Unknown image type %s', $imgName));
|
Log::debug("Unknown image type from $imgName");
|
||||||
}
|
}
|
||||||
|
|
||||||
$imgNamePath = sprintf('%s.%s', $imgNamePath, $imgExtension);
|
$imgNamePath = sprintf('%s.%s', $imgNamePath, $imgExtension);
|
||||||
|
|
||||||
|
$arImgPaths[$index] = compact('imgNamePath', 'imageData');
|
||||||
|
$index++;
|
||||||
|
} //end if
|
||||||
|
} //end foreach
|
||||||
|
|
||||||
|
return $arImgPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update or create images
|
||||||
|
*
|
||||||
|
* @param array $images
|
||||||
|
* @param \Illuminate\Database\Eloquent\Model|null $model
|
||||||
|
* @return array Pictures pathes with checksums as keys that will be save into model.
|
||||||
|
*/
|
||||||
|
private function updateOrCreateImagesFiles(array $images, Model $model = null): array
|
||||||
|
{
|
||||||
|
$disk = $this->getDisk();
|
||||||
|
|
||||||
|
$oldChecksums = [];
|
||||||
|
$oldPicturesPaths = [];
|
||||||
|
|
||||||
|
if ($model) {
|
||||||
|
/** @var array $oldChecksums */
|
||||||
|
$oldChecksums = \array_keys($model->pictures ?? []);
|
||||||
|
/** @var array $oldPicturesPaths */
|
||||||
|
$oldPicturesPaths = $model->pictures ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var array $oldPictures old pictures pathes with checksums as keys */
|
||||||
|
$oldPictures = \array_combine($oldChecksums, $oldPicturesPaths);
|
||||||
|
|
||||||
|
/** @var array $newPictures new pictures pathes with checksums as keys */
|
||||||
|
$newPictures = collect($images)->mapWithKeys(function (array $imageProps) {
|
||||||
|
/** @var string $imgNamePath */
|
||||||
|
/** @var string $imageData */
|
||||||
|
extract($imageProps);
|
||||||
|
|
||||||
|
$imageFinalPath = sprintf('%s/%s', $this->disk, $imgNamePath);
|
||||||
|
|
||||||
|
// ? Sucessfully calculate checksum ?
|
||||||
|
if (!($newFileChk = hash('crc32b', $imageFinalPath . $imageData))) {
|
||||||
|
throw new \ErrorException('crc32b doesnt seems to be supported.');
|
||||||
|
}
|
||||||
|
return [$newFileChk => $imageFinalPath];
|
||||||
|
});
|
||||||
|
|
||||||
|
/** @var array $modelPictures pictures that wont change */
|
||||||
|
$modelPictures = \array_intersect_key($oldPictures, $newPictures);
|
||||||
|
|
||||||
|
/** @var \Illuminate\Support\Collection $removePictures pictures that we will remove (delete from disk) */
|
||||||
|
$removePictures = collect(\array_diff_key($oldPictures, $newPictures));
|
||||||
|
|
||||||
|
/** @var \Illuminate\Support\Collection $addPictures pictures that we will create (save on disk) */
|
||||||
|
$addPictures = collect(\array_diff_key($newPictures, $oldPictures));
|
||||||
|
|
||||||
|
// * Save or remove pictures from storage as needed
|
||||||
|
collect($images)->mapWithKeys(function (
|
||||||
|
array $imageProps,
|
||||||
|
string $checksum
|
||||||
|
) use (
|
||||||
|
$disk,
|
||||||
|
$removePictures,
|
||||||
|
$addPictures
|
||||||
|
) {
|
||||||
|
/** @var string $imgNamePath */
|
||||||
|
/** @var string $imageData */
|
||||||
|
extract($imageProps);
|
||||||
|
|
||||||
|
if ($addPictures->has($checksum)) {
|
||||||
|
// * Save new picture
|
||||||
if (!$disk->put($imgNamePath, $imageData)) {
|
if (!$disk->put($imgNamePath, $imageData)) {
|
||||||
throw new AbortXmlImportException(sprintf('The image %s could not be saved', $imgNamePath));
|
throw new AbortXmlImportException(sprintf('The image %s could not be saved', $imgNamePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
$arImgPaths[$index] = sprintf('%s/%s', $this->disk, $imgNamePath);
|
|
||||||
$index++;
|
|
||||||
} else {
|
|
||||||
//probably not an image could be a PDF, PPT, AI anything is possible
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $arImgPaths;
|
if ($removePictures->has($checksum)) {
|
||||||
|
// * Remove old picture (if exists)
|
||||||
|
if ($disk->exists($imgNamePath) and !$disk->delete($imgNamePath)) {
|
||||||
|
throw new AbortXmlImportException(sprintf('The image %s could not be delete', $imgNamePath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return $newPictures;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Unique Values for a specific file
|
||||||
|
*
|
||||||
|
* @param array $line
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getUniqueValues(array $line): array
|
||||||
|
{
|
||||||
|
/** @var array Unique Key columns */
|
||||||
|
$uniqueColumnsKey = [];
|
||||||
|
foreach ($this->uniqueKeyField as $uField) {
|
||||||
|
$uniqueColumnsKey[$uField] = $line[$uField];
|
||||||
|
}
|
||||||
|
return $uniqueColumnsKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -500,15 +645,18 @@ class XmlIncoReader extends XmlJobs
|
||||||
if ($value->count()) {
|
if ($value->count()) {
|
||||||
$extraction = $this->extract($value);
|
$extraction = $this->extract($value);
|
||||||
|
|
||||||
if(count($extraction)
|
if (
|
||||||
&& array_key_exists("code",$extraction)
|
count($extraction)
|
||||||
&& array_key_exists("libelle",$extraction)) {
|
&& array_key_exists("code", $extraction)
|
||||||
|
&& array_key_exists("libelle", $extraction)
|
||||||
|
) {
|
||||||
$val = strval($extraction['libelle']);
|
$val = strval($extraction['libelle']);
|
||||||
$extract[$key] = strlen($val) ? $val : null;
|
$extract[$key] = strlen($val) ? $val : null;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$extract[$key][] = $extraction; //$this->extract($value);
|
$extract[$key][] = $extraction;
|
||||||
|
// $this->extract($value);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$value = strval($value);
|
$value = strval($value);
|
||||||
|
|
@ -517,7 +665,7 @@ class XmlIncoReader extends XmlJobs
|
||||||
} else {
|
} else {
|
||||||
$extract[$key] = strlen($value) ? $value : null;
|
$extract[$key] = strlen($value) ? $value : null;
|
||||||
}
|
}
|
||||||
}
|
} //end foreach
|
||||||
return $extract;
|
return $extract;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,38 +1,32 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs\Arcadia;
|
||||||
|
|
||||||
use App\Enums\LogType;
|
use App\Enums\LogType;
|
||||||
use App\Exceptions\AbortXmlImportException;
|
use App\Exceptions\AbortXmlImportException;
|
||||||
use App\Exceptions\InvalidXmlException;
|
use App\Exceptions\InvalidXmlException;
|
||||||
use App\Lib\Utils;
|
|
||||||
use ErrorException;
|
|
||||||
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use RecursiveArrayIterator;
|
|
||||||
use RecursiveIteratorIterator;
|
|
||||||
use SimpleXMLElement;
|
|
||||||
use Throwable;
|
|
||||||
use TypeError;
|
|
||||||
|
|
||||||
abstract class XmlJobs extends XArcadiaFlux
|
abstract class XmlJobs extends XArcadiaFlux
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* invalid xml folder
|
* The xsd file path used to validate files
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string|null
|
||||||
*/
|
*/
|
||||||
protected $invalidFolderName = 'invalid';
|
protected $xsdSchemaPath = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* xsd file location
|
* The xsd file content used to validate files
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string|null
|
||||||
*/
|
*/
|
||||||
protected $xsdFile = '';
|
protected $xsdSchema = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The columns to take from the XML
|
* The columns to take from the XML
|
||||||
|
|
@ -62,17 +56,28 @@ abstract class XmlJobs extends XArcadiaFlux
|
||||||
*
|
*
|
||||||
* @param string $entityName
|
* @param string $entityName
|
||||||
* @param string $modelClass
|
* @param string $modelClass
|
||||||
|
* @param callable $afterParseCallback After parse is done.
|
||||||
|
* @param callable $saveCallback Instead of updateOrCreate, execute $saveCallback.
|
||||||
|
* @param callable $afterSaveCallback After save is done.
|
||||||
* @return void
|
* @return void
|
||||||
|
* @throws InvalidXmlException If xml file is not valid.
|
||||||
|
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.MaxExceeded
|
||||||
*/
|
*/
|
||||||
// phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
|
protected function parseData(
|
||||||
protected function parseData(string $entityName, string $modelClass, callable $callback = null): void
|
string $entityName,
|
||||||
{
|
string $modelClass,
|
||||||
|
callable $afterParseCallback = null,
|
||||||
|
callable $saveCallback = null,
|
||||||
|
callable $afterSaveCallback = null
|
||||||
|
): void {
|
||||||
/** @var Filesystem */
|
/** @var Filesystem */
|
||||||
$disk = $this->getDisk();
|
$disk = $this->getDisk();
|
||||||
$this->log('Démarrage du traitement');
|
$this->log('Démarrage du traitement');
|
||||||
|
|
||||||
|
$this->assertXsdIsHereIfAny();
|
||||||
|
|
||||||
$files = $disk->files();
|
$files = $disk->files();
|
||||||
|
|
||||||
$progress = 1;
|
|
||||||
$total = count(array_filter($files, function ($filename) {
|
$total = count(array_filter($files, function ($filename) {
|
||||||
if ($this->fileHasToBeIgnored($filename)) {
|
if ($this->fileHasToBeIgnored($filename)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -80,60 +85,82 @@ abstract class XmlJobs extends XArcadiaFlux
|
||||||
return true;
|
return true;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
$this->startProgressBar($total);
|
||||||
|
|
||||||
foreach ($files as $filename) {
|
foreach ($files as $filename) {
|
||||||
if ($this->fileHasToBeIgnored($filename)) {
|
if ($this->fileHasToBeIgnored($filename)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->log(sprintf(' traitement %d sur %s ', $progress++, $total));
|
$this->incrementProgressBar();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/** @var string $data */
|
/** @var string $data */
|
||||||
$data = $disk->get($filename);
|
$data = $disk->get($filename);
|
||||||
|
|
||||||
|
$this->validateXmlFile($filename, $data);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->validateSchema($data);
|
|
||||||
if ($this->encapsulate) {
|
if ($this->encapsulate) {
|
||||||
$data = sprintf('<Root>%s</Root>', $data);
|
$data = sprintf('<Root>%s</Root>', $data);
|
||||||
/** @var SimpleXMLElement|false */
|
/** @var \SimpleXMLElement|false */
|
||||||
$xmlContent = simplexml_load_string($data);
|
$xmlContent = simplexml_load_string($data);
|
||||||
} else {
|
} else {
|
||||||
/** @var SimpleXMLElement|false */
|
/** @var \SimpleXMLElement|false */
|
||||||
$xmlContent = simplexml_load_string($data, null, LIBXML_NOCDATA | LIBXML_PARSEHUGE);
|
$xmlContent = simplexml_load_string($data, null, LIBXML_NOCDATA | LIBXML_PARSEHUGE);
|
||||||
}
|
}
|
||||||
if ($xmlContent === false) {
|
if ($xmlContent === false) {
|
||||||
throw new InvalidXmlException('XML is Invalid (simplexml_load_string returned false)');
|
throw new InvalidXmlException('XML is Invalid (simplexml_load_string returned false)');
|
||||||
}
|
}
|
||||||
} catch (ErrorException $e) {
|
} catch (\ErrorException $e) {
|
||||||
throw new InvalidXmlException('XML is Invalid', 0, $e);
|
throw new InvalidXmlException('XML is Invalid', 0, $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $this->extract($xmlContent);
|
$data = $this->extract($xmlContent);
|
||||||
|
|
||||||
$lines = [];
|
$lines = [];
|
||||||
|
|
||||||
|
// * Multiple Entities within file
|
||||||
|
if (isset($data[$entityName])) {
|
||||||
foreach ($data[$entityName] as $traca) {
|
foreach ($data[$entityName] as $traca) {
|
||||||
$line = $this->processFields($traca);
|
$line = $this->processFields($traca);
|
||||||
if ($callback) {
|
if ($afterParseCallback) {
|
||||||
$callback($xmlContent, $line);
|
$afterParseCallback($xmlContent, $line);
|
||||||
|
}
|
||||||
|
$lines[] = $line;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// * Only one entity within file
|
||||||
|
$line = $this->processFields($data);
|
||||||
|
if ($afterParseCallback) {
|
||||||
|
$afterParseCallback($xmlContent, $line);
|
||||||
}
|
}
|
||||||
$lines[] = $line;
|
$lines[] = $line;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mise en base des données
|
|
||||||
|
// * Mise en base des données
|
||||||
foreach ($lines as $line) {
|
foreach ($lines as $line) {
|
||||||
/** @var array Unique Key columns */
|
/** @var array Unique Key columns */
|
||||||
$uniqueColumnsKey = [];
|
$uniqueColumnsKey = [];
|
||||||
foreach ($this->uniqueKeyField as $uField) {
|
foreach ($this->uniqueKeyField as $uField) {
|
||||||
$uniqueColumnsKey[$uField] = $line[$uField];
|
$uniqueColumnsKey[$uField] = $line[$uField];
|
||||||
}
|
}
|
||||||
|
if (count($uniqueColumnsKey)) {
|
||||||
|
if ($saveCallback) {
|
||||||
|
$saveCallback($uniqueColumnsKey, $line);
|
||||||
|
} else {
|
||||||
forward_static_call_array(
|
forward_static_call_array(
|
||||||
[$modelClass, 'updateOrCreate'],
|
[$modelClass, 'updateOrCreate'],
|
||||||
[$uniqueColumnsKey, $line]
|
[$uniqueColumnsKey, $line]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if ($afterSaveCallback) {
|
||||||
|
$afterSaveCallback($xmlContent, $line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //end foreach
|
||||||
} catch (InvalidXmlException $e) {
|
} catch (InvalidXmlException $e) {
|
||||||
$message = $e->getMessage();
|
$message = $e->getMessage();
|
||||||
if (get_class($e) === QueryException::class) {
|
|
||||||
$message = implode(' ', $e->errorInfo);
|
|
||||||
}
|
|
||||||
$this->log(
|
$this->log(
|
||||||
sprintf(
|
sprintf(
|
||||||
'File %s could not be parsed => %s on line %d',
|
'File %s could not be parsed => %s on line %d',
|
||||||
|
|
@ -146,19 +173,6 @@ abstract class XmlJobs extends XArcadiaFlux
|
||||||
$this->sendInvalidXmlEmail($filename, $e);
|
$this->sendInvalidXmlEmail($filename, $e);
|
||||||
$this->moveToInvalid($filename);
|
$this->moveToInvalid($filename);
|
||||||
continue;
|
continue;
|
||||||
} catch (ErrorException | TypeError $e) {
|
|
||||||
$this->log(
|
|
||||||
sprintf(
|
|
||||||
'CRITICAL: File %s could not be parsed => %s on line %d',
|
|
||||||
$filename,
|
|
||||||
$e->getMessage(),
|
|
||||||
$e->getLine()
|
|
||||||
),
|
|
||||||
LogType::critical()
|
|
||||||
);
|
|
||||||
$this->sendErrorParseEmailToServiceWeb($e);
|
|
||||||
continue;
|
|
||||||
// ignore the file
|
|
||||||
} catch (AbortXmlImportException $e) {
|
} catch (AbortXmlImportException $e) {
|
||||||
$this->log(
|
$this->log(
|
||||||
sprintf(
|
sprintf(
|
||||||
|
|
@ -169,15 +183,16 @@ abstract class XmlJobs extends XArcadiaFlux
|
||||||
),
|
),
|
||||||
LogType::error()
|
LogType::error()
|
||||||
);
|
);
|
||||||
$this->sendErrorParseEmailToServiceWeb($e);
|
report($e);
|
||||||
continue;
|
continue;
|
||||||
// ignore the file
|
// * ignore the file
|
||||||
} catch (FileNotFoundException | QueryException $e) {
|
} catch (QueryException $e) {
|
||||||
// This happens because of concurent xml jobs read
|
$this->log(implode(' ', $e->errorInfo), LogType::critical());
|
||||||
|
report($e);
|
||||||
|
continue;
|
||||||
|
} catch (FileNotFoundException $e) {
|
||||||
|
// This happens because of concurent xml jobs read.
|
||||||
$message = $e->getMessage();
|
$message = $e->getMessage();
|
||||||
if (get_class($e) === QueryException::class) {
|
|
||||||
$message = implode(' ', $e->errorInfo);
|
|
||||||
}
|
|
||||||
$this->log(
|
$this->log(
|
||||||
sprintf(
|
sprintf(
|
||||||
'ERROR: File %s could not be read (not found) => %s on line %d',
|
'ERROR: File %s could not be read (not found) => %s on line %d',
|
||||||
|
|
@ -188,13 +203,31 @@ abstract class XmlJobs extends XArcadiaFlux
|
||||||
LogType::debug()
|
LogType::debug()
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
// ignore the file
|
// * ignore the file
|
||||||
}//end try
|
} catch (\Exception | \ErrorException | \TypeError $e) {
|
||||||
|
$this->log(
|
||||||
|
sprintf(
|
||||||
|
'File %s could not be parsed => %s on line %d',
|
||||||
|
$filename,
|
||||||
|
$e->getMessage(),
|
||||||
|
$e->getLine()
|
||||||
|
),
|
||||||
|
LogType::critical()
|
||||||
|
);
|
||||||
|
report($e);
|
||||||
|
continue;
|
||||||
|
// * ignore the file
|
||||||
|
} //end try
|
||||||
$this->moveToArchives($filename);
|
$this->moveToArchives($filename);
|
||||||
}//end foreach
|
} //end foreach
|
||||||
|
|
||||||
// Clear old archives
|
// Clear old invalids.
|
||||||
|
$this->clearOldInvalids();
|
||||||
|
|
||||||
|
// Clear old archives.
|
||||||
$this->clearOldArchives();
|
$this->clearOldArchives();
|
||||||
|
|
||||||
|
$this->endProgressBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -205,7 +238,7 @@ abstract class XmlJobs extends XArcadiaFlux
|
||||||
*/
|
*/
|
||||||
protected function extract($sxe = null): array
|
protected function extract($sxe = null): array
|
||||||
{
|
{
|
||||||
if (!$sxe instanceof SimpleXMLElement) {
|
if (!$sxe instanceof \SimpleXMLElement) {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
$extract = array();
|
$extract = array();
|
||||||
|
|
@ -229,7 +262,7 @@ abstract class XmlJobs extends XArcadiaFlux
|
||||||
*
|
*
|
||||||
* @param array $fields
|
* @param array $fields
|
||||||
* @return array
|
* @return array
|
||||||
* @throws AbortXmlImportException
|
* @throws AbortXmlImportException If a field could not be validated.
|
||||||
*/
|
*/
|
||||||
protected function processFields(array $fields): array
|
protected function processFields(array $fields): array
|
||||||
{
|
{
|
||||||
|
|
@ -242,8 +275,8 @@ abstract class XmlJobs extends XArcadiaFlux
|
||||||
throw new AbortXmlImportException(sprintf('The field %s is not allowed to be null', $key));
|
throw new AbortXmlImportException(sprintf('The field %s is not allowed to be null', $key));
|
||||||
}
|
}
|
||||||
if (is_array($field)) {
|
if (is_array($field)) {
|
||||||
// Flatten data
|
// * Flatten data
|
||||||
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($field));
|
$it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($field));
|
||||||
foreach ($it as $k => $f) {
|
foreach ($it as $k => $f) {
|
||||||
$ko = strtolower(sprintf('%s.%s', $key, $k));
|
$ko = strtolower(sprintf('%s.%s', $key, $k));
|
||||||
if (!$this->isFieldNullable($ko) && is_null($f)) {
|
if (!$this->isFieldNullable($ko) && is_null($f)) {
|
||||||
|
|
@ -261,7 +294,7 @@ abstract class XmlJobs extends XArcadiaFlux
|
||||||
} else {
|
} else {
|
||||||
$result[$key] = $field;
|
$result[$key] = $field;
|
||||||
}
|
}
|
||||||
}//end foreach
|
} //end foreach
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -309,23 +342,6 @@ abstract class XmlJobs extends XArcadiaFlux
|
||||||
return in_array($filename, $this->ignoreFiles) or !$this->checkExtension($filename, 'xml');
|
return in_array($filename, $this->ignoreFiles) or !$this->checkExtension($filename, 'xml');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate xml data against xsd file if provided
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function validateSchema($data) {
|
|
||||||
if($this->xsdFile) {
|
|
||||||
//check against XSD
|
|
||||||
$xsd = storage_path($this->disk."/".$this->xsdFile);
|
|
||||||
$domDocument= new \DOMDocument();
|
|
||||||
$domDocument->loadXML($data);
|
|
||||||
//free some memroy to avoid overload
|
|
||||||
unset($data);
|
|
||||||
$domDocument->schemaValidate($xsd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the file is an xml file
|
* Check if the file is an xml file
|
||||||
*
|
*
|
||||||
|
|
@ -341,78 +357,81 @@ abstract class XmlJobs extends XArcadiaFlux
|
||||||
return $isXml;
|
return $isXml;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function sendErrorParseEmailToServiceWeb(Throwable $e)
|
/**
|
||||||
{
|
* Send invalid XML email
|
||||||
Utils::sendEmailErrorManually($e);
|
*
|
||||||
}
|
* @param string $filename
|
||||||
|
* @param \Throwable $e
|
||||||
protected function sendInvalidXmlEmail(string $filename, Throwable $e)
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function sendInvalidXmlEmail(string $filename, \Throwable $e)
|
||||||
{
|
{
|
||||||
$message = $e->getPrevious()->getMessage();
|
$message = $e->getPrevious()->getMessage();
|
||||||
Mail::send('emails.invalidxml', [
|
Mail::send('mails.invalidxml', [
|
||||||
'strError' => sprintf(
|
'strError' => sprintf(
|
||||||
'File %s could not be parsed => %s on line %d',
|
'File %s could not be parsed => %s on line %d',
|
||||||
$filename,
|
$filename,
|
||||||
$message,
|
$message,
|
||||||
$e->getLine()
|
$e->getLine()
|
||||||
),
|
),
|
||||||
'appName' => config('app.name'),
|
|
||||||
'filename' => $filename,
|
'filename' => $filename,
|
||||||
'exception' => $e->getPrevious()
|
'exception' => $e->getPrevious()
|
||||||
], function ($m) {
|
], function ($m) {
|
||||||
$m->to(
|
$m->to(
|
||||||
config('mail.contactsList.invalidInco'),
|
config('mail.contactsList.invalidInco'),
|
||||||
sprintf('%s Exception Handler', config('app.name'))
|
sprintf('%s XML Exception Handler', config('app.name'))
|
||||||
)->subject('You sent me an invalid XML file, please check its validity before resend ASAP.');
|
)->subject('You sent me an invalid XML file, please check its validity before resend ASAP.');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move the file to invalid folder
|
* Assert XSD is readable if configured
|
||||||
*
|
*
|
||||||
* @param string $filename
|
|
||||||
* @return void
|
* @return void
|
||||||
|
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException If xsd file cant be read.
|
||||||
*/
|
*/
|
||||||
protected function moveToInvalid(string $filename): void
|
private function assertXsdIsHereIfAny()
|
||||||
{
|
{
|
||||||
$newFileName = $filename;
|
if ($this->xsdSchemaPath) {
|
||||||
if (!$this->getDisk()->exists($filename)) {
|
$this->xsdSchema = File::get($this->xsdSchemaPath);
|
||||||
$this->log(
|
|
||||||
sprintf(
|
|
||||||
'File %s could not be moved to invalid (not found)',
|
|
||||||
$filename
|
|
||||||
),
|
|
||||||
LogType::warning()
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if ($this->getDisk()->exists(sprintf('%s/%s', $this->invalidFolderName, $filename))) {
|
|
||||||
$ext = \pathinfo($filename, \PATHINFO_EXTENSION);
|
|
||||||
$newFileName = \sprintf(
|
|
||||||
'%s_%d.%s',
|
|
||||||
\substr($filename, 0, 0 - (strlen($ext) + 1)),
|
|
||||||
\time(),
|
|
||||||
$ext
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate Xml file against XSD if loaded
|
||||||
|
*
|
||||||
|
* @param string $fileName
|
||||||
|
* @param string $fileContent
|
||||||
|
* @return void
|
||||||
|
* @throws InvalidXmlException If XML could not be parsed or validated against XSD.
|
||||||
|
* @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
||||||
|
*/
|
||||||
|
private function validateXmlFile(string $fileName, string $fileContent)
|
||||||
|
{
|
||||||
|
if ($this->xsdSchema) {
|
||||||
|
$domDocument = new \DOMDocument('1.0', 'UTF-8');
|
||||||
try {
|
try {
|
||||||
if (!$this->getDisk()->move($filename, sprintf('%s/%s', $this->invalidFolderName, $newFileName))) {
|
if (!$domDocument->loadXML($fileContent)) {
|
||||||
$this->log(
|
throw new InvalidXmlException("XML Validation $fileName content could not be loaded to validate.");
|
||||||
sprintf(
|
}
|
||||||
'File %s could not be moved to invalid',
|
if (!$domDocument->schemaValidateSource($this->xsdSchema, LIBXML_SCHEMA_CREATE)) {
|
||||||
$filename
|
throw new InvalidXmlException(
|
||||||
),
|
"XML Validation $fileName content could not be validated against xsd."
|
||||||
LogType::warning()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->log(
|
if (\get_class($e) === InvalidXmlException::class) {
|
||||||
sprintf(
|
throw $e;
|
||||||
'File %s could not be moved to invalid',
|
|
||||||
$filename
|
|
||||||
),
|
|
||||||
LogType::warning()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
throw new InvalidXmlException(
|
||||||
|
"XML Validation $fileName content could not be validated against xsd.",
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
__FILE__,
|
||||||
|
__LINE__,
|
||||||
|
$e
|
||||||
|
);
|
||||||
|
} //end try
|
||||||
|
} //end if
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
81
app/Jobs/SetServiceStats.php
Normal file
81
app/Jobs/SetServiceStats.php
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\ServiceStat;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
class SetServiceStats implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
use InteractsWithQueue;
|
||||||
|
use Queueable;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
private const KEY = '2202020';
|
||||||
|
|
||||||
|
/** @var \App\Models\Service */
|
||||||
|
protected $service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @param Service $service
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Service $service)
|
||||||
|
{
|
||||||
|
$this->service = $service;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
/** @var \App\Models\ServiceStat */
|
||||||
|
$serviceStat = ServiceStat::firstOrNew([
|
||||||
|
'date' => date('Y-m-d'),
|
||||||
|
'service_id' => $this->service->id,
|
||||||
|
]);
|
||||||
|
if (!$serviceStat->exists) {
|
||||||
|
$serviceStat->service()->associate($this->service);
|
||||||
|
}
|
||||||
|
$serviceStat->nb_calls += 1;
|
||||||
|
$serviceStat->saveOrFail();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the time at which the job should timeout.
|
||||||
|
*
|
||||||
|
* @return \DateTime
|
||||||
|
*/
|
||||||
|
public function retryUntil()
|
||||||
|
{
|
||||||
|
return now()->addMinutes(rand(0, 360));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the middleware the job should pass through.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function middleware()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
(new WithoutOverlapping(self::KEY))
|
||||||
|
->releaseAfter(rand(90, 180))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
180
app/Lib/Helper.php
Normal file
180
app/Lib/Helper.php
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Lib;
|
||||||
|
|
||||||
|
use App\Exceptions\Api\ApplicationException;
|
||||||
|
use Illuminate\Contracts\Encryption\DecryptException;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Crypt;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper Class
|
||||||
|
*/
|
||||||
|
class Helper
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Package infos
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $packageInfos;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells if the request is within apiV1 routes
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function isRequestForApiV1(Request $request): bool
|
||||||
|
{
|
||||||
|
// * Small hack to ensure to know if route is From APIv1.
|
||||||
|
$routePrefix = (strpos(static::getRoutePrefix($request), 'api/v1') ? 'v1' : null) ??
|
||||||
|
(strpos($request->path(), 'api/v1') === 0 ? 'v1' : null) ?? 'none';
|
||||||
|
return strpos($routePrefix, 'v1') === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to get current route name
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public static function getRouteName(Request $request)
|
||||||
|
{
|
||||||
|
return Route::currentRouteName() ?? ($request->route() ? $request->route()->getName() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the bearer token from request
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public static function getBearerTokenFromRequest(Request $request): ?string
|
||||||
|
{
|
||||||
|
$header = $request->header('Authorization', '');
|
||||||
|
|
||||||
|
if (Str::contains($header, 'Bearer ')) {
|
||||||
|
$auths = \explode(',', $header);
|
||||||
|
foreach ($auths as $auth) {
|
||||||
|
if (Str::contains($auth, 'Bearer ')) {
|
||||||
|
return Str::substr($auth, 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to get current route prefix
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public static function getRoutePrefix(Request $request)
|
||||||
|
{
|
||||||
|
return (Route::getCurrentRoute() ? Route::getCurrentRoute()->getPrefix() : null) ??
|
||||||
|
($request->route() ? $request->route()->getPrefix() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to get route from request
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return \Illuminate\Routing\Route|null
|
||||||
|
*/
|
||||||
|
public static function getRoute(Request $request)
|
||||||
|
{
|
||||||
|
return Route::getRoutes()->getByName(static::getRouteName($request));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get composer.json infos
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function getPackageInfo(): array
|
||||||
|
{
|
||||||
|
if (!static::$packageInfos) {
|
||||||
|
static::$packageInfos = \json_decode(\file_get_contents(__DIR__ . '/../../composer.json'), true);
|
||||||
|
}
|
||||||
|
return static::$packageInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the request signature
|
||||||
|
*
|
||||||
|
* @param string $secret
|
||||||
|
* @param string $uri
|
||||||
|
* @param string $method
|
||||||
|
* @param array $parameters
|
||||||
|
* @return string request signature
|
||||||
|
* @throws ApplicationException If an error occurs.
|
||||||
|
*/
|
||||||
|
public static function signRequest(string $secret, string $uri, string $method, array $parameters): string
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$uri = collect(\parse_url($uri));
|
||||||
|
$method = \strtolower($method);
|
||||||
|
|
||||||
|
// * Remove signature from parameters to calculate the hashmac.
|
||||||
|
$signature = collect([
|
||||||
|
'uri' => sprintf(
|
||||||
|
'%s://%s%s',
|
||||||
|
$uri->get('scheme'),
|
||||||
|
$uri->get('host'),
|
||||||
|
$uri->get('path')
|
||||||
|
),
|
||||||
|
'method' => $method
|
||||||
|
]);
|
||||||
|
|
||||||
|
$flattenRequestArray = function (array $array) use (&$flattenRequestArray) {
|
||||||
|
$out = [];
|
||||||
|
foreach ($array as $key => $value) {
|
||||||
|
if (is_array($value)) {
|
||||||
|
$subArray = $flattenRequestArray($value);
|
||||||
|
foreach ($subArray as $subKey => $subValue) {
|
||||||
|
$out["$key.$subKey"] = $subValue;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$out[$key] = $value;
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
};
|
||||||
|
|
||||||
|
$parameters = $flattenRequestArray($parameters);
|
||||||
|
$parameters = collect($parameters);
|
||||||
|
|
||||||
|
// * Remove signature from parameters.
|
||||||
|
$parameters->pull('signature');
|
||||||
|
|
||||||
|
// * Natural sort all parameters.
|
||||||
|
$parameters = $parameters->sortKeys(\SORT_NATURAL)
|
||||||
|
->map(function ($value, $key) {
|
||||||
|
return sprintf('%s=%s', \rawurlencode($key), \rawurlencode($value));
|
||||||
|
});
|
||||||
|
|
||||||
|
$signature->push($parameters->implode('&'));
|
||||||
|
|
||||||
|
$calculatedHash = base64_encode(hash_hmac(
|
||||||
|
'sha256',
|
||||||
|
$signature->implode('\n'),
|
||||||
|
Crypt::decryptString($secret),
|
||||||
|
true
|
||||||
|
));
|
||||||
|
|
||||||
|
if (!$calculatedHash) {
|
||||||
|
throw new ApplicationException('Failed to calculate requests hash');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $calculatedHash;
|
||||||
|
} catch (DecryptException $e) {
|
||||||
|
throw new ApplicationException('Failed to decrypt service credentials', 0, $e);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new ApplicationException('An error occured when authenticating service', 0, $e);
|
||||||
|
} //end try
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,210 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Lib;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Illuminate\Support\Facades\Mail;
|
|
||||||
use Throwable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Some utilities methods
|
|
||||||
*
|
|
||||||
* @author Jérémy Munsch <jeremy.munsch@ldc.fr>
|
|
||||||
*/
|
|
||||||
// phpcs:disable Generic.Files.LineLength.TooLong
|
|
||||||
class Utils
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Replace stange accents
|
|
||||||
*
|
|
||||||
* https://stackoverflow.com/questions/3371697/replacing-accented-characters-php
|
|
||||||
* @author Jérémy Munsch <jeremy.munsch@jeremydev.ovh>
|
|
||||||
* @param string $subject
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function replaceSpecChar(string $subject): string
|
|
||||||
{
|
|
||||||
$char_map = array(
|
|
||||||
"ъ" => "-", "ь" => "-", "Ъ" => "-", "Ь" => "-",
|
|
||||||
"А" => "A", "Ă" => "A", "Ǎ" => "A", "Ą" => "A", "À" => "A", "Ã" => "A", "Á" => "A", "Æ" => "A", "Â" => "A", "Å" => "A", "Ǻ" => "A", "Ā" => "A", "א" => "A",
|
|
||||||
"Б" => "B", "ב" => "B", "Þ" => "B",
|
|
||||||
"Ĉ" => "C", "Ć" => "C", "Ç" => "C", "Ц" => "C", "צ" => "C", "Ċ" => "C", "Č" => "C", "©" => "C", "ץ" => "C",
|
|
||||||
"Д" => "D", "Ď" => "D", "Đ" => "D", "ד" => "D", "Ð" => "D",
|
|
||||||
"È" => "E", "Ę" => "E", "É" => "E", "Ë" => "E", "Ê" => "E", "Е" => "E", "Ē" => "E", "Ė" => "E", "Ě" => "E", "Ĕ" => "E", "Є" => "E", "Ə" => "E", "ע" => "E",
|
|
||||||
"Ф" => "F", "Ƒ" => "F",
|
|
||||||
"Ğ" => "G", "Ġ" => "G", "Ģ" => "G", "Ĝ" => "G", "Г" => "G", "ג" => "G", "Ґ" => "G",
|
|
||||||
"ח" => "H", "Ħ" => "H", "Х" => "H", "Ĥ" => "H", "ה" => "H",
|
|
||||||
"I" => "I", "Ï" => "I", "Î" => "I", "Í" => "I", "Ì" => "I", "Į" => "I", "Ĭ" => "I", "I" => "I", "И" => "I", "Ĩ" => "I", "Ǐ" => "I", "י" => "I", "Ї" => "I", "Ī" => "I", "І" => "I",
|
|
||||||
"Й" => "J", "Ĵ" => "J",
|
|
||||||
"ĸ" => "K", "כ" => "K", "Ķ" => "K", "К" => "K", "ך" => "K",
|
|
||||||
"Ł" => "L", "Ŀ" => "L", "Л" => "L", "Ļ" => "L", "Ĺ" => "L", "Ľ" => "L", "ל" => "L",
|
|
||||||
"מ" => "M", "М" => "M", "ם" => "M",
|
|
||||||
"Ñ" => "N", "Ń" => "N", "Н" => "N", "Ņ" => "N", "ן" => "N", "Ŋ" => "N", "נ" => "N", "ʼn" => "N", "Ň" => "N",
|
|
||||||
"Ø" => "O", "Ó" => "O", "Ò" => "O", "Ô" => "O", "Õ" => "O", "О" => "O", "Ő" => "O", "Ŏ" => "O", "Ō" => "O", "Ǿ" => "O", "Ǒ" => "O", "Ơ" => "O",
|
|
||||||
"פ" => "P", "ף" => "P", "П" => "P",
|
|
||||||
"ק" => "Q",
|
|
||||||
"Ŕ" => "R", "Ř" => "R", "Ŗ" => "R", "ר" => "R", "Р" => "R", "®" => "R",
|
|
||||||
"Ş" => "S", "Ś" => "S", "Ș" => "S", "Š" => "S", "С" => "S", "Ŝ" => "S", "ס" => "S",
|
|
||||||
"Т" => "T", "Ț" => "T", "ט" => "T", "Ŧ" => "T", "ת" => "T", "Ť" => "T", "Ţ" => "T",
|
|
||||||
"Ù" => "U", "Û" => "U", "Ú" => "U", "Ū" => "U", "У" => "U", "Ũ" => "U", "Ư" => "U", "Ǔ" => "U", "Ų" => "U", "Ŭ" => "U", "Ů" => "U", "Ű" => "U", "Ǖ" => "U", "Ǜ" => "U", "Ǚ" => "U", "Ǘ" => "U",
|
|
||||||
"В" => "V", "ו" => "V",
|
|
||||||
"Ý" => "Y", "Ы" => "Y", "Ŷ" => "Y", "Ÿ" => "Y",
|
|
||||||
"Ź" => "Z", "Ž" => "Z", "Ż" => "Z", "З" => "Z", "ז" => "Z",
|
|
||||||
"а" => "a", "ă" => "a", "ǎ" => "a", "ą" => "a", "à" => "a", "ã" => "a", "á" => "a", "æ" => "a", "â" => "a", "å" => "a", "ǻ" => "a", "ā" => "a", "א" => "a",
|
|
||||||
"б" => "b", "ב" => "b", "þ" => "b",
|
|
||||||
"ĉ" => "c", "ć" => "c", "ç" => "c", "ц" => "c", "צ" => "c", "ċ" => "c", "č" => "c", "©" => "c", "ץ" => "c",
|
|
||||||
"Ч" => "ch", "ч" => "ch",
|
|
||||||
"д" => "d", "ď" => "d", "đ" => "d", "ד" => "d", "ð" => "d",
|
|
||||||
"è" => "e", "ę" => "e", "é" => "e", "ë" => "e", "ê" => "e", "е" => "e", "ē" => "e", "ė" => "e", "ě" => "e", "ĕ" => "e", "є" => "e", "ə" => "e", "ע" => "e",
|
|
||||||
"ф" => "f", "ƒ" => "f",
|
|
||||||
"ğ" => "g", "ġ" => "g", "ģ" => "g", "ĝ" => "g", "г" => "g", "ג" => "g", "ґ" => "g",
|
|
||||||
"ח" => "h", "ħ" => "h", "х" => "h", "ĥ" => "h", "ה" => "h",
|
|
||||||
"i" => "i", "ï" => "i", "î" => "i", "í" => "i", "ì" => "i", "į" => "i", "ĭ" => "i", "ı" => "i", "и" => "i", "ĩ" => "i", "ǐ" => "i", "י" => "i", "ї" => "i", "ī" => "i", "і" => "i",
|
|
||||||
"й" => "j", "Й" => "j", "Ĵ" => "j", "ĵ" => "j",
|
|
||||||
"ĸ" => "k", "כ" => "k", "ķ" => "k", "к" => "k", "ך" => "k",
|
|
||||||
"ł" => "l", "ŀ" => "l", "л" => "l", "ļ" => "l", "ĺ" => "l", "ľ" => "l", "ל" => "l",
|
|
||||||
"מ" => "m", "м" => "m", "ם" => "m",
|
|
||||||
"ñ" => "n", "ń" => "n", "н" => "n", "ņ" => "n", "ן" => "n", "ŋ" => "n", "נ" => "n", "ʼn" => "n", "ň" => "n",
|
|
||||||
"ø" => "o", "ó" => "o", "ò" => "o", "ô" => "o", "õ" => "o", "о" => "o", "ő" => "o", "ŏ" => "o", "ō" => "o", "ǿ" => "o", "ǒ" => "o", "ơ" => "o",
|
|
||||||
"פ" => "p", "ף" => "p", "п" => "p",
|
|
||||||
"ק" => "q",
|
|
||||||
"ŕ" => "r", "ř" => "r", "ŗ" => "r", "ר" => "r", "р" => "r", "®" => "r",
|
|
||||||
"ş" => "s", "ś" => "s", "ș" => "s", "š" => "s", "с" => "s", "ŝ" => "s", "ס" => "s",
|
|
||||||
"т" => "t", "ț" => "t", "ט" => "t", "ŧ" => "t", "ת" => "t", "ť" => "t", "ţ" => "t",
|
|
||||||
"ù" => "u", "û" => "u", "ú" => "u", "ū" => "u", "у" => "u", "ũ" => "u", "ư" => "u", "ǔ" => "u", "ų" => "u", "ŭ" => "u", "ů" => "u", "ű" => "u", "ǖ" => "u", "ǜ" => "u", "ǚ" => "u", "ǘ" => "u",
|
|
||||||
"в" => "v", "ו" => "v",
|
|
||||||
"ý" => "y", "ы" => "y", "ŷ" => "y", "ÿ" => "y",
|
|
||||||
"ź" => "z", "ž" => "z", "ż" => "z", "з" => "z", "ז" => "z", "ſ" => "z",
|
|
||||||
"™" => "tm",
|
|
||||||
"@" => "at",
|
|
||||||
"Ä" => "ae", "Ǽ" => "ae", "ä" => "ae", "æ" => "ae", "ǽ" => "ae",
|
|
||||||
"ij" => "ij", "IJ" => "ij",
|
|
||||||
"я" => "ja", "Я" => "ja",
|
|
||||||
"Э" => "je", "э" => "je",
|
|
||||||
"ё" => "jo", "Ё" => "jo",
|
|
||||||
"ю" => "ju", "Ю" => "ju",
|
|
||||||
"œ" => "oe", "Œ" => "oe", "ö" => "oe", "Ö" => "oe",
|
|
||||||
"щ" => "sch", "Щ" => "sch",
|
|
||||||
"ш" => "sh", "Ш" => "sh",
|
|
||||||
"ß" => "ss",
|
|
||||||
"Ü" => "ue",
|
|
||||||
"Ж" => "zh", "ж" => "zh",
|
|
||||||
);
|
|
||||||
return self::mbStrtr($subject, $char_map);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Multibyte strtr
|
|
||||||
*
|
|
||||||
* @param string $str
|
|
||||||
* @param string|array $from
|
|
||||||
* @param string|null $to
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function mbStrtr($str, $from, $to = null): string
|
|
||||||
{
|
|
||||||
if (is_array($from)) {
|
|
||||||
$from = array_map('utf8_decode', $from);
|
|
||||||
$from = array_map('utf8_decode', array_flip($from));
|
|
||||||
return utf8_encode(strtr(utf8_decode($str), array_flip($from)));
|
|
||||||
}
|
|
||||||
return utf8_encode(strtr(utf8_decode($str), utf8_decode($from), utf8_decode($to)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manually send an exception to team instead of going trough the error handler
|
|
||||||
* (requires package kwaadpepper/exception-handler)
|
|
||||||
*
|
|
||||||
* @param Throwable $e
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function sendEmailErrorManually(Throwable $e)
|
|
||||||
{
|
|
||||||
Mail::send([
|
|
||||||
'html' => 'exception-handler::emails.exception',
|
|
||||||
'text' => 'exception-handler::emails.exception-plain'
|
|
||||||
], [
|
|
||||||
'appName' => config('app.name'),
|
|
||||||
'code' => $e->getCode(),
|
|
||||||
'line' => $e->getLine(),
|
|
||||||
'ename' => \get_class($e),
|
|
||||||
'emessage' => self::getAnonymizedMessage($e->getMessage()),
|
|
||||||
'file' => str_replace(\realpath(\sprintf('%s/../', \app_path())), '', $e->getFile()),
|
|
||||||
'stackTrace' => self::getAnonymizedStackTrace($e),
|
|
||||||
'uri' => app()->runningInConsole() ?
|
|
||||||
'CONSOLE' : (
|
|
||||||
(request() and request()->fullUrl()) ?
|
|
||||||
request()->fullUrl() : 'none')
|
|
||||||
], function ($m) {
|
|
||||||
/** @var \Illuminate\Mail\Message $m */
|
|
||||||
$m->to(
|
|
||||||
config('mail.contactsList.serviceweb'),
|
|
||||||
sprintf('%s Exception Handler', config('app.name'))
|
|
||||||
)->subject('Need help !, an error occured in your application, please check Logs ASAP');
|
|
||||||
});
|
|
||||||
Log::alert(sprintf('Envoie d\'une erreur par mail : %s', json_encode($e)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Anonymize at least Sql queries
|
|
||||||
*
|
|
||||||
* @param string $message
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private static function getAnonymizedMessage(string $message): string
|
|
||||||
{
|
|
||||||
$message = \preg_replace('/^(.*)( \(SQL:.*\))$/', '$1', $message) ?? $message;
|
|
||||||
return $message;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Anonymize stack trace
|
|
||||||
*
|
|
||||||
* @param \Exception $e
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private static function getAnonymizedStackTrace(Throwable $e): string
|
|
||||||
{
|
|
||||||
$projectPath = \realpath(\sprintf('%s/../', \app_path()));
|
|
||||||
$traceStack = $e->getTrace();
|
|
||||||
$traceStr = '';
|
|
||||||
$maxStackStage = 0;
|
|
||||||
foreach ($traceStack as $stackStage => $stack) {
|
|
||||||
$maxStackStage = $stackStage;
|
|
||||||
$traceStr .= \sprintf(
|
|
||||||
'#%s %s(%d): ',
|
|
||||||
$stackStage,
|
|
||||||
str_replace($projectPath, '', $stack['file']),
|
|
||||||
$stack['line']
|
|
||||||
);
|
|
||||||
$traceStr .= "{$stack['class']}->{$stack['function']}(";
|
|
||||||
$traceArgs = '';
|
|
||||||
if (isset($stack['args'])) {
|
|
||||||
$argsCount = count($stack['args']);
|
|
||||||
for ($i = 0; $i < $argsCount; $i++) {
|
|
||||||
$type = \gettype($stack['args'][$i]);
|
|
||||||
switch ($type) {
|
|
||||||
case 'object':
|
|
||||||
$traceArgs .= sprintf('Object(%s)', \get_class($stack['args'][$i]));
|
|
||||||
break;
|
|
||||||
case 'array':
|
|
||||||
$traceArgs .= sprintf('Array(%d)', \count($stack['args'][$i]));
|
|
||||||
break;
|
|
||||||
case 'string':
|
|
||||||
$traceArgs .= sprintf('String(%d)', \strlen($stack['args'][$i]));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$traceArgs .= \gettype($stack['args'][$i]);
|
|
||||||
}
|
|
||||||
$traceArgs .= ($i !== ($argsCount - 1) ? ',' : '');
|
|
||||||
}
|
|
||||||
$traceStr .= $traceArgs;
|
|
||||||
}//end if
|
|
||||||
$traceStr .= ")\n";
|
|
||||||
}//end foreach
|
|
||||||
$maxStackStage++;
|
|
||||||
$traceStr .= "#$maxStackStage {main}";
|
|
||||||
return $traceStr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
90
app/Models/ArcadiaBrand.php
Normal file
90
app/Models/ArcadiaBrand.php
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Rennokki\QueryCache\Traits\QueryCacheable;
|
||||||
|
|
||||||
|
class ArcadiaBrand extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
use QueryCacheable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The timeout cache for this model
|
||||||
|
*
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
protected $cacheFor = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'arcadia_id',
|
||||||
|
'libelle'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ArcadiaBrand
|
||||||
|
*
|
||||||
|
* @param array $attributes
|
||||||
|
*/
|
||||||
|
public function __construct(array $attributes = [])
|
||||||
|
{
|
||||||
|
$this->cacheFor = config('api.arcadiaBrandsDBCache') * 60;
|
||||||
|
|
||||||
|
parent::__construct($attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the base cache tags that will be present
|
||||||
|
* on all queries.
|
||||||
|
*
|
||||||
|
* ! This is used to flush caching
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function getCacheBaseTags(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'incollector_arcadia_brands',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform any actions required after the model boots.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected static function booted()
|
||||||
|
{
|
||||||
|
static::created(function () {
|
||||||
|
// * Flush Eloquent cache.
|
||||||
|
self::flushQueryCache();
|
||||||
|
});
|
||||||
|
static::updated(function () {
|
||||||
|
// * Flush Eloquent cache.
|
||||||
|
self::flushQueryCache();
|
||||||
|
});
|
||||||
|
static::deleted(function () {
|
||||||
|
// * Flush Eloquent cache.
|
||||||
|
self::flushQueryCache();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// * RELATIONS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service belongsToMany relation
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\belongsToMany
|
||||||
|
*/
|
||||||
|
public function services()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Service::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
224
app/Models/Service.php
Normal file
224
app/Models/Service.php
Normal file
|
|
@ -0,0 +1,224 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Jobs\SetServiceStats;
|
||||||
|
use App\Lib\Helper;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Crypt;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Rennokki\QueryCache\Traits\QueryCacheable;
|
||||||
|
|
||||||
|
class Service extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
use QueryCacheable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The timeout cache for this model
|
||||||
|
*
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
protected $cacheFor = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'name'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service
|
||||||
|
*
|
||||||
|
* @param array $attributes
|
||||||
|
*/
|
||||||
|
public function __construct(array $attributes = [])
|
||||||
|
{
|
||||||
|
$this->cacheFor = config('api.servicesDBCache') * 60;
|
||||||
|
|
||||||
|
parent::__construct($attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the base cache tags that will be present
|
||||||
|
* on all queries.
|
||||||
|
*
|
||||||
|
* ! This is used to flush caching
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function getCacheBaseTags(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'incollector_services',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform any actions required after the model boots.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected static function booted()
|
||||||
|
{
|
||||||
|
static::creating(function (self $service) {
|
||||||
|
$service->generateBearerToken();
|
||||||
|
$service->generateBearerPassword();
|
||||||
|
});
|
||||||
|
static::created(function () {
|
||||||
|
// * Flush Eloquent cache.
|
||||||
|
self::flushQueryCache();
|
||||||
|
});
|
||||||
|
static::updated(function () {
|
||||||
|
// * Flush Eloquent cache.
|
||||||
|
self::flushQueryCache();
|
||||||
|
});
|
||||||
|
static::deleted(function () {
|
||||||
|
// * Flush Eloquent cache.
|
||||||
|
self::flushQueryCache();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// * RELATIONS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ArcadiaBrand belongsToMany relation
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\belongsToMany
|
||||||
|
*/
|
||||||
|
public function arcadiaBrands()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(ArcadiaBrand::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ServiceStat hasMany relation
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||||
|
*/
|
||||||
|
public function servicesStats()
|
||||||
|
{
|
||||||
|
return $this->hasMany(ServiceStat::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// * METHODS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment API CRM Stat
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function incrementStat(): void
|
||||||
|
{
|
||||||
|
SetServiceStats::dispatch($this)->afterResponse()
|
||||||
|
->onConnection('database')->onQueue('stats');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates new bearer token
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function generateBearerToken(): string
|
||||||
|
{
|
||||||
|
$this->bearer_token = hash('ripemd160', Str::uuid());
|
||||||
|
$this->bearer_token_date = now();
|
||||||
|
return $this->bearer_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates new bearer password
|
||||||
|
* It store the crypted password in model
|
||||||
|
* and returns the plain password
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function generateBearerPassword(): string
|
||||||
|
{
|
||||||
|
$pwd = hash('sha256', Str::uuid());
|
||||||
|
$this->bearer_pwd = Crypt::encryptString($pwd);
|
||||||
|
return $pwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Service from bearerToken with its brand
|
||||||
|
*
|
||||||
|
* @param string $bearerToken
|
||||||
|
* @return \App\Models\Service
|
||||||
|
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException If the token matches no known service.
|
||||||
|
*/
|
||||||
|
public static function getFromBearerToken(string $bearerToken): self
|
||||||
|
{
|
||||||
|
$services = static::all();
|
||||||
|
/** @var \App\Models\Service $service */
|
||||||
|
foreach ($services as $service) {
|
||||||
|
if ($service->bearer_token and $bearerToken === $service->bearer_token) {
|
||||||
|
return $service->load([
|
||||||
|
'arcadiaBrands' => function ($query) use ($service) {
|
||||||
|
return $query->cacheTags(["service:{$service->id}:arcadiaBrands"]);
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ModelNotFoundException('Could not find service from Service Token');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticate the request for this service
|
||||||
|
*
|
||||||
|
* This will check hash-mac and that the request is wellformed
|
||||||
|
*
|
||||||
|
* The request should have the minimum following parameters
|
||||||
|
* - timestamp using format Y-m-d H:i:s, and must be UTC (Greenwich Mean Time)
|
||||||
|
* - bearerToken the public token generated by the crm
|
||||||
|
* - signature the hashmac signature of the request
|
||||||
|
*
|
||||||
|
* ! Signature must be calculated with every parameter urlencode once only
|
||||||
|
* ! All parameters to calculate the signature must be sorted (natural sort asc)
|
||||||
|
*
|
||||||
|
* ! Signature should look like
|
||||||
|
* * httpVerb\n
|
||||||
|
* * uri\n
|
||||||
|
* * queryString
|
||||||
|
*
|
||||||
|
* ! httpVerb as well as uri must be lower case
|
||||||
|
* ! uri should include scheme, host and path and nothing else, if path is empty a trailing slash should be added
|
||||||
|
* ! eg: https://ldc-crm.fr/customer/1 or https://ldc-crm.fr/
|
||||||
|
* ! The query string shall be separated with '&', eg param1=value1¶m2=value2
|
||||||
|
* ! Key names and values of the query string must be url encoded using RFC 3986
|
||||||
|
* ! http://www.faqs.org/rfcs/rfc3986.html
|
||||||
|
* ! Signature must be hmac sha256 using the private key generatedby the crm
|
||||||
|
*
|
||||||
|
* * ! They should then be concat in a string separated by \n
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return boolean
|
||||||
|
* @throws \App\Exceptions\Api\V2\CrmException If an error occurs.
|
||||||
|
*/
|
||||||
|
public function authenticateApi(Request $request): bool
|
||||||
|
{
|
||||||
|
// ! Hack to prevent Laravel to cast empty strings to null or the opposite.
|
||||||
|
// ! If cannot decode json user legacy all() method.
|
||||||
|
$data = \json_decode($request->getContent(), true);
|
||||||
|
$data = (\is_array($data) and \json_last_error() === \JSON_ERROR_NONE) ?
|
||||||
|
array_replace_recursive($request->all(), $data) : $request->all();
|
||||||
|
|
||||||
|
$calculatedHash = Helper::signRequest(
|
||||||
|
$this->bearer_pwd,
|
||||||
|
$request->getUri(),
|
||||||
|
$request->getMethod(),
|
||||||
|
$data
|
||||||
|
);
|
||||||
|
|
||||||
|
// ! Très important hash_equals https://fr.wikipedia.org/wiki/Attaque_temporelle
|
||||||
|
return \hash_equals($calculatedHash, $request->signature);
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/Models/ServiceStat.php
Normal file
39
app/Models/ServiceStat.php
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class ServiceStat extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected $fillable = ['date'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model's attributes.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $attributes = [
|
||||||
|
'nb_calls' => 0
|
||||||
|
];
|
||||||
|
|
||||||
|
// * RELATIONS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service BelongsTo relation
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function service()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Service::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,15 +2,17 @@
|
||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Laravel\Sanctum\HasApiTokens;
|
use Laravel\Sanctum\HasApiTokens;
|
||||||
|
|
||||||
class User extends Authenticatable
|
class User extends Authenticatable
|
||||||
{
|
{
|
||||||
use HasApiTokens, HasFactory, Notifiable;
|
use HasApiTokens;
|
||||||
|
use HasFactory;
|
||||||
|
use Notifiable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that are mass assignable.
|
* The attributes that are mass assignable.
|
||||||
|
|
@ -41,4 +43,21 @@ class User extends Authenticatable
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'email_verified_at' => 'datetime',
|
'email_verified_at' => 'datetime',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform any actions required after the model boots.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected static function booted()
|
||||||
|
{
|
||||||
|
static::creating(function (self $user) {
|
||||||
|
$user->password = Hash::make($user->password);
|
||||||
|
});
|
||||||
|
static::updating(function (self $user) {
|
||||||
|
if (Hash::needsRehash($user->password)) {
|
||||||
|
$user->password = Hash::make($user->password);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,26 @@ namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Support\Facades\Storage;
|
|
||||||
use Illuminate\Contracts\Filesystem\Filesystem;
|
|
||||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XmlInco
|
||||||
|
*
|
||||||
|
* pictures attribute is organized as following
|
||||||
|
* [
|
||||||
|
* 'checksum' => 'storageFilePath'
|
||||||
|
* ]
|
||||||
|
*
|
||||||
|
* ! checksum includes fullfilepath + data
|
||||||
|
*/
|
||||||
class XmlInco extends Model
|
class XmlInco extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'id',
|
'id',
|
||||||
'no_art',
|
'no_art',
|
||||||
|
|
@ -29,72 +40,40 @@ class XmlInco extends Model
|
||||||
'lst_ing',
|
'lst_ing',
|
||||||
'lib_allergene',
|
'lib_allergene',
|
||||||
'nutritional_values',
|
'nutritional_values',
|
||||||
'picture',
|
'pictures',
|
||||||
];
|
|
||||||
|
|
||||||
protected $casts = [
|
|
||||||
'picture' => 'array',
|
|
||||||
'nutritional_values' => 'array',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Laravel disk name
|
* The attributes that should be cast.
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $disk = 'inco';
|
|
||||||
|
|
||||||
protected $imagesPath = 'images';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The files to ignore
|
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $ignoreFiles = [
|
protected $casts = [
|
||||||
'.gitignore', '.gitkeep'
|
'pictures' => 'array',
|
||||||
|
'nutritional_values' => 'array',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
// * GETTERS
|
||||||
* retrieve a collection of medias
|
|
||||||
*/
|
|
||||||
public function getMediasAttribute() {
|
|
||||||
/** @var Filesystem */
|
|
||||||
$disk = $this->getDisk();
|
|
||||||
$files = $disk->files($this->imagesPath);
|
|
||||||
|
|
||||||
$collFiles = new Collection();
|
|
||||||
// /** @var File */
|
|
||||||
foreach ($files as $file) {
|
|
||||||
$pi = \pathinfo($file, \PATHINFO_BASENAME);
|
|
||||||
if (in_array($pi, $this->ignoreFiles)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!in_array($pi, array($this->id))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$image = base64_encode($disk->get($file));
|
|
||||||
$element = array(
|
|
||||||
"filename" => $pi,
|
|
||||||
"imgdata" => $image
|
|
||||||
);
|
|
||||||
$collFiles->add($element);
|
|
||||||
}
|
|
||||||
return $collFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the concerned disk
|
* Get niceString attribute
|
||||||
*
|
*
|
||||||
* @return Filesystem
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function getDisk(): Filesystem
|
public function getNiceStringAttribute(): string
|
||||||
{
|
|
||||||
return Storage::disk($this->disk);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getNiceStringAttribute()
|
|
||||||
{
|
{
|
||||||
return sprintf('%s - %s', $this->id, $this->descriptif);
|
return sprintf('%s - %s', $this->id, $this->descriptif);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// * RELATIONS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ArcadiaBrand belongsTo relation
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\belongsTo
|
||||||
|
*/
|
||||||
|
public function arcadiaBrand()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(ArcadiaBrand::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
52
app/Policies/ArcadiaBrandPolicy.php
Normal file
52
app/Policies/ArcadiaBrandPolicy.php
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
use App\Models\ArcadiaBrand;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Policies\Rules\UserStaticRules as USR;
|
||||||
|
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||||
|
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
class ArcadiaBrandPolicy extends RootPolicy
|
||||||
|
{
|
||||||
|
use HandlesAuthorization;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init policies on call
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setPolicies()
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This first rule to return true in a policy action authorises the action
|
||||||
|
*/
|
||||||
|
$this->policies = [
|
||||||
|
'arcadia_brand' => [
|
||||||
|
'viewAny' => USR::make('everyOne'),
|
||||||
|
'view' => USR::make('everyOne'),
|
||||||
|
'create' => USR::make('noOne'),
|
||||||
|
'update' => USR::make('noOne'),
|
||||||
|
'delete' => USR::make('noOne')
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the customer belongs to the service
|
||||||
|
*
|
||||||
|
* @param Service $service
|
||||||
|
* @param ArcadiaBrand $arcadiaBrand
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function arcadiaBrandBelongsToService(Service $service, ArcadiaBrand $arcadiaBrand)
|
||||||
|
{
|
||||||
|
return $service->arcadiaBrands()->whereHas('services', function ($q) use ($arcadiaBrand) {
|
||||||
|
$q->whereId($arcadiaBrand->id);
|
||||||
|
})->exists();
|
||||||
|
}
|
||||||
|
}
|
||||||
32
app/Policies/Rules/UserStaticRules.php
Normal file
32
app/Policies/Rules/UserStaticRules.php
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Policies\Rules;
|
||||||
|
|
||||||
|
use Kwaadpepper\CrudPolicies\Policies\Rules\StaticRules;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Userlevel Policiy Static Rules
|
||||||
|
* ! Rules has to return boolean value
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
abstract class UserStaticRules extends StaticRules
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Allow everyone
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
protected static function everyOne(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Allow no one
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
protected static function noOne(): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,14 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
use Illuminate\Pagination\Paginator;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Illuminate\Support\Facades\URL;
|
||||||
|
use Illuminate\Support\Facades\View;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
|
|
@ -13,7 +21,6 @@ class AppServiceProvider extends ServiceProvider
|
||||||
*/
|
*/
|
||||||
public function register()
|
public function register()
|
||||||
{
|
{
|
||||||
//
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -23,6 +30,54 @@ class AppServiceProvider extends ServiceProvider
|
||||||
*/
|
*/
|
||||||
public function boot()
|
public function boot()
|
||||||
{
|
{
|
||||||
//
|
// ! Important pourra etre retiré quand la V1 sera supprimé
|
||||||
|
// Empeche l'encapsulation dans data .
|
||||||
|
JsonResource::withoutWrapping();
|
||||||
|
|
||||||
|
// * LOCAL HTTPS FIX.
|
||||||
|
if (!in_array(config('app.env'), ['local', 'testing'])) {
|
||||||
|
URL::forceScheme('https');
|
||||||
|
}
|
||||||
|
|
||||||
|
// * HA PROXY FIX.
|
||||||
|
App::singleton('clientIp', function () {
|
||||||
|
if (
|
||||||
|
config('app.env') === 'local'
|
||||||
|
) {
|
||||||
|
return request()->getClientIp();
|
||||||
|
} else {
|
||||||
|
return request()->server('HTTP_X_FORWARDED_FOR') ??
|
||||||
|
request()->getClientIp();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// * LOCAL FORCE eg: for Carbon
|
||||||
|
// * Remove numeric to prevent float seperator to breaking numeric transactions
|
||||||
|
\collect([\LC_TIME, \LC_MESSAGES, \LC_COLLATE, \LC_CTYPE, \LC_MONETARY])
|
||||||
|
->map(function ($contant) {
|
||||||
|
setlocale(
|
||||||
|
$contant,
|
||||||
|
\sprintf(
|
||||||
|
'%s_%s.UTF-8',
|
||||||
|
config('app.locale'),
|
||||||
|
\strtoupper(config('app.locale'))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
Carbon::setLocale(config('app.locale'));
|
||||||
|
|
||||||
|
// * Force bootstrap paginator
|
||||||
|
Paginator::useBootstrap();
|
||||||
|
|
||||||
|
$appInfos = Cache::remember(
|
||||||
|
'composer',
|
||||||
|
360,
|
||||||
|
function () {
|
||||||
|
return json_decode(File::get(\app_path('../composer.json')));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
View::share('name', $appInfos->name);
|
||||||
|
View::share('version', $appInfos->version);
|
||||||
|
View::share('license', $appInfos->license);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,12 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Lib\Helper;
|
||||||
|
use App\Models\Service;
|
||||||
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class AuthServiceProvider extends ServiceProvider
|
class AuthServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
|
|
@ -12,9 +16,7 @@ class AuthServiceProvider extends ServiceProvider
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $policies = [
|
protected $policies = [];
|
||||||
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register any authentication / authorization services.
|
* Register any authentication / authorization services.
|
||||||
|
|
@ -25,6 +27,50 @@ class AuthServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
$this->registerPolicies();
|
$this->registerPolicies();
|
||||||
|
|
||||||
//
|
Auth::viaRequest('api-hmac', function (Request $request) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Un token devrais être fourni pour chaque requete
|
||||||
|
*/
|
||||||
|
if (is_null($request->bearer_token)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// * Si la route actuelle n'as pas de nom alors on refuse de la traiter.
|
||||||
|
if (!(Helper::getRoute($request))) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
return null;
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
$service = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// * On récupère le service actuel correspondant au token fourni.
|
||||||
|
$service = Service::getFromBearerToken($request->bearer_token ?? '');
|
||||||
|
} catch (ModelNotFoundException $e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// * Preload the arcadiaBrands as we will access it often later.
|
||||||
|
$service = $service->load('arcadiaBrands');
|
||||||
|
|
||||||
|
/**
|
||||||
|
** All Api routes must pass ArcadiaBrand model to controller action since a ArcadiaBrand owns
|
||||||
|
** a service token, otherwise the api cannot be called.
|
||||||
|
** This hack is a custom binder instead of passing
|
||||||
|
** this to route as a parameter
|
||||||
|
*/
|
||||||
|
app()->bind(Service::class, function () use ($service) {
|
||||||
|
return $service;
|
||||||
|
});
|
||||||
|
|
||||||
|
// * On authenticate la request en vérifiant sa signature.
|
||||||
|
if ($service->authenticateApi($request)) {
|
||||||
|
return $service;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ namespace App\Providers;
|
||||||
use Illuminate\Auth\Events\Registered;
|
use Illuminate\Auth\Events\Registered;
|
||||||
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||||
use Illuminate\Support\Facades\Event;
|
|
||||||
|
|
||||||
class EventServiceProvider extends ServiceProvider
|
class EventServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
|
|
@ -27,6 +26,5 @@ class EventServiceProvider extends ServiceProvider
|
||||||
*/
|
*/
|
||||||
public function boot()
|
public function boot()
|
||||||
{
|
{
|
||||||
//
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,15 +19,6 @@ class RouteServiceProvider extends ServiceProvider
|
||||||
*/
|
*/
|
||||||
public const HOME = '/home';
|
public const HOME = '/home';
|
||||||
|
|
||||||
/**
|
|
||||||
* The controller namespace for the application.
|
|
||||||
*
|
|
||||||
* When present, controller route declarations will automatically be prefixed with this namespace.
|
|
||||||
*
|
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
// protected $namespace = 'App\\Http\\Controllers';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define your route model bindings, pattern filters, etc.
|
* Define your route model bindings, pattern filters, etc.
|
||||||
*
|
*
|
||||||
|
|
@ -56,8 +47,16 @@ class RouteServiceProvider extends ServiceProvider
|
||||||
*/
|
*/
|
||||||
protected function configureRateLimiting()
|
protected function configureRateLimiting()
|
||||||
{
|
{
|
||||||
|
RateLimiter::for('web', function (Request $request) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
return Limit::perMinute(25)
|
||||||
|
->by(optional($request->user())->id ?: app('clientIp'));
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
});
|
||||||
|
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||||
RateLimiter::for('api', function (Request $request) {
|
RateLimiter::for('api', function (Request $request) {
|
||||||
return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
|
return Limit::perMinute(config('api.apiRequestRatePerMinute', 100))
|
||||||
|
->by(app('clientIp'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,31 @@
|
||||||
{
|
{
|
||||||
"name": "laravel/laravel",
|
"name": "ldc/incollector",
|
||||||
"type": "project",
|
"type": "project",
|
||||||
"description": "The Laravel Framework.",
|
"version": "1.0.0",
|
||||||
"keywords": ["framework", "laravel"],
|
"description": "LDC Incollector - A workaround of Arcadia",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Olivier Legat",
|
||||||
|
"email": "olivier.legat@ldc.fr"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jérémy Munsch",
|
||||||
|
"email": "jeremy.munsch@ldc.fr",
|
||||||
|
"homepage": "https://jeremydev.ovh"
|
||||||
|
}
|
||||||
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.3|^8.0",
|
"php": "^7.3|^8.0",
|
||||||
"fruitcake/laravel-cors": "^2.0",
|
"fruitcake/laravel-cors": "^2.0",
|
||||||
"guzzlehttp/guzzle": "^7.0.1",
|
"guzzlehttp/guzzle": "^7.0.1",
|
||||||
|
"kwaadpepper/crud-policies": "^0.3.5",
|
||||||
"kwaadpepper/enum": "^2.1",
|
"kwaadpepper/enum": "^2.1",
|
||||||
"kwaadpepper/exception-handler": "^0.0.5",
|
"kwaadpepper/exception-handler": "^0.0.5",
|
||||||
"laravel/framework": "^8.65",
|
"laravel/framework": "^8.65",
|
||||||
"laravel/sanctum": "^2.11",
|
"laravel/sanctum": "^2.11",
|
||||||
"laravel/tinker": "^2.5",
|
"laravel/tinker": "^2.5",
|
||||||
"squizlabs/php_codesniffer": "^3.6"
|
"rennokki/laravel-eloquent-query-cache": "^3.3"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"facade/ignition": "^2.5",
|
"facade/ignition": "^2.5",
|
||||||
|
|
@ -21,7 +33,9 @@
|
||||||
"laravel/sail": "^1.0.1",
|
"laravel/sail": "^1.0.1",
|
||||||
"mockery/mockery": "^1.4.4",
|
"mockery/mockery": "^1.4.4",
|
||||||
"nunomaduro/collision": "^5.10",
|
"nunomaduro/collision": "^5.10",
|
||||||
"phpunit/phpunit": "^9.5.10"
|
"phpunit/phpunit": "^9.5.10",
|
||||||
|
"slevomat/coding-standard": "^7.1",
|
||||||
|
"squizlabs/php_codesniffer": "^3.6"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|
@ -36,6 +50,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"test": [
|
||||||
|
"@configclear",
|
||||||
|
"./test.sh"
|
||||||
|
],
|
||||||
|
"configclear": "@php artisan config:clear",
|
||||||
|
"runtestmigration": "@php artisan migrate:fresh --env=testing --seed",
|
||||||
|
"runtest": "@php artisan test --no-coverage --testsuite Api",
|
||||||
|
"runtestWithCoverage": "@php artisan test --testsuite Api",
|
||||||
|
"runserve": "@php artisan serve --host=127.0.0.1 --port=8888 --env=testing&",
|
||||||
|
"killserve": "kill $(ps -f | grep '[1]27.0.0.1:8888' | awk '{print $2}')",
|
||||||
"post-autoload-dump": [
|
"post-autoload-dump": [
|
||||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||||
"@php artisan package:discover --ansi"
|
"@php artisan package:discover --ansi"
|
||||||
|
|
@ -58,7 +82,10 @@
|
||||||
"config": {
|
"config": {
|
||||||
"optimize-autoloader": true,
|
"optimize-autoloader": true,
|
||||||
"preferred-install": "dist",
|
"preferred-install": "dist",
|
||||||
"sort-packages": true
|
"sort-packages": true,
|
||||||
|
"allow-plugins": {
|
||||||
|
"dealerdirect/phpcodesniffer-composer-installer": true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"prefer-stable": true
|
"prefer-stable": true
|
||||||
|
|
|
||||||
1140
composer.lock
generated
1140
composer.lock
generated
File diff suppressed because it is too large
Load diff
30
config/api.php
Normal file
30
config/api.php
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Lib\Helper;
|
||||||
|
|
||||||
|
return [
|
||||||
|
/**
|
||||||
|
* Api versions
|
||||||
|
** this value is sent in response
|
||||||
|
*/
|
||||||
|
'version' => Helper::getPackageInfo()['version'],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp timeout in seconds
|
||||||
|
* Request with timestamp older than 30sec
|
||||||
|
* will be rejected
|
||||||
|
*/
|
||||||
|
'timestampTimeout' => 30,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of request
|
||||||
|
* per user or ip per minutes
|
||||||
|
*/
|
||||||
|
'apiRequestRatePerMinute' => 256,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service requests cache
|
||||||
|
* time in minutes
|
||||||
|
*/
|
||||||
|
'servicesDBCache' => 10
|
||||||
|
];
|
||||||
|
|
@ -39,7 +39,7 @@ return [
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'debug' => (bool) env('APP_DEBUG', false),
|
'debug' => (bool)env('APP_DEBUG', false),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
@ -67,7 +67,7 @@ return [
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'timezone' => 'UTC',
|
'timezone' => 'Europe/Paris',
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
@ -80,7 +80,7 @@ return [
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'locale' => 'en',
|
'locale' => 'fr',
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
@ -106,7 +106,7 @@ return [
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'faker_locale' => 'en_US',
|
'faker_locale' => 'fr_FR',
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
@ -139,6 +139,7 @@ return [
|
||||||
/*
|
/*
|
||||||
* Laravel Framework Service Providers...
|
* Laravel Framework Service Providers...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Illuminate\Auth\AuthServiceProvider::class,
|
Illuminate\Auth\AuthServiceProvider::class,
|
||||||
Illuminate\Broadcasting\BroadcastServiceProvider::class,
|
Illuminate\Broadcasting\BroadcastServiceProvider::class,
|
||||||
Illuminate\Bus\BusServiceProvider::class,
|
Illuminate\Bus\BusServiceProvider::class,
|
||||||
|
|
@ -169,9 +170,9 @@ return [
|
||||||
/*
|
/*
|
||||||
* Application Service Providers...
|
* Application Service Providers...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
App\Providers\AppServiceProvider::class,
|
App\Providers\AppServiceProvider::class,
|
||||||
App\Providers\AuthServiceProvider::class,
|
App\Providers\AuthServiceProvider::class,
|
||||||
// App\Providers\BroadcastServiceProvider::class,
|
|
||||||
App\Providers\EventServiceProvider::class,
|
App\Providers\EventServiceProvider::class,
|
||||||
App\Providers\RouteServiceProvider::class,
|
App\Providers\RouteServiceProvider::class,
|
||||||
|
|
||||||
|
|
@ -218,7 +219,6 @@ return [
|
||||||
'Queue' => Illuminate\Support\Facades\Queue::class,
|
'Queue' => Illuminate\Support\Facades\Queue::class,
|
||||||
'RateLimiter' => Illuminate\Support\Facades\RateLimiter::class,
|
'RateLimiter' => Illuminate\Support\Facades\RateLimiter::class,
|
||||||
'Redirect' => Illuminate\Support\Facades\Redirect::class,
|
'Redirect' => Illuminate\Support\Facades\Redirect::class,
|
||||||
// 'Redis' => Illuminate\Support\Facades\Redis::class,
|
|
||||||
'Request' => Illuminate\Support\Facades\Request::class,
|
'Request' => Illuminate\Support\Facades\Request::class,
|
||||||
'Response' => Illuminate\Support\Facades\Response::class,
|
'Response' => Illuminate\Support\Facades\Response::class,
|
||||||
'Route' => Illuminate\Support\Facades\Route::class,
|
'Route' => Illuminate\Support\Facades\Route::class,
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,9 @@ return [
|
||||||
'driver' => 'session',
|
'driver' => 'session',
|
||||||
'provider' => 'users',
|
'provider' => 'users',
|
||||||
],
|
],
|
||||||
|
'api' => [
|
||||||
|
'driver' => 'api-hmac'
|
||||||
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -63,12 +66,7 @@ return [
|
||||||
'users' => [
|
'users' => [
|
||||||
'driver' => 'eloquent',
|
'driver' => 'eloquent',
|
||||||
'model' => App\Models\User::class,
|
'model' => App\Models\User::class,
|
||||||
],
|
]
|
||||||
|
|
||||||
// 'users' => [
|
|
||||||
// 'driver' => 'database',
|
|
||||||
// 'table' => 'users',
|
|
||||||
// ],
|
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,6 @@ return [
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache'),
|
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_cache'),
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ return [
|
||||||
|
|
||||||
'options' => [
|
'options' => [
|
||||||
'cluster' => env('REDIS_CLUSTER', 'redis'),
|
'cluster' => env('REDIS_CLUSTER', 'redis'),
|
||||||
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
|
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_database_'),
|
||||||
],
|
],
|
||||||
|
|
||||||
'default' => [
|
'default' => [
|
||||||
|
|
|
||||||
|
|
@ -11,5 +11,7 @@ return [
|
||||||
* Put a developper secured mailbox in here
|
* Put a developper secured mailbox in here
|
||||||
* so you can receive email if things go mad
|
* so you can receive email if things go mad
|
||||||
*/
|
*/
|
||||||
'contactsList' => []
|
'contactsList' => [
|
||||||
|
env('MAIL_LIST_SERVICEWEB', 'equipe.web@ldc.fr')
|
||||||
|
]
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ return [
|
||||||
'public' => [
|
'public' => [
|
||||||
'driver' => 'local',
|
'driver' => 'local',
|
||||||
'root' => storage_path('app/public'),
|
'root' => storage_path('app/public'),
|
||||||
'url' => env('APP_URL').'/storage',
|
'url' => env('APP_URL') . '/storage',
|
||||||
'visibility' => 'public',
|
'visibility' => 'public',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
@ -57,6 +57,10 @@ return [
|
||||||
'driver' => 'local',
|
'driver' => 'local',
|
||||||
'root' => storage_path('inco'),
|
'root' => storage_path('inco'),
|
||||||
],
|
],
|
||||||
|
'arcadia_brand' => [
|
||||||
|
'driver' => 'local',
|
||||||
|
'root' => storage_path('marque'),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
5
config/ldcweb.php
Normal file
5
config/ldcweb.php
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'project_uuid' => env('PROJECT_UUID')
|
||||||
|
];
|
||||||
|
|
@ -97,8 +97,9 @@ return [
|
||||||
],
|
],
|
||||||
|
|
||||||
'contactsList' => [
|
'contactsList' => [
|
||||||
'serviceweb' => explode(",", env('MAIL_LIST_SERVICEWEB', 'equipe.web@ldc.fr')),
|
'serviceweb' => explode(',', env('MAIL_LIST_SERVICEWEB', 'equipe.web@ldc.fr')),
|
||||||
'invalidInco' => explode(",", env('MAIL_LIST_INVALIDINCO', 'equipe.web@ldc.fr')),
|
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||||
|
'invalidInco' => explode(',', env('MAIL_LIST_INVALIDINCO', 'equipe.web@ldc.fr,vincent.pierre@ldc.fr,christelle.manceau@ldc.fr')),
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ return [
|
||||||
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
|
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
|
||||||
'%s%s',
|
'%s%s',
|
||||||
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
|
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
|
||||||
env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : ''
|
env('APP_URL') ? ',' . parse_url(env('APP_URL'), PHP_URL_HOST) : ''
|
||||||
))),
|
))),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ return [
|
||||||
|
|
||||||
'cookie' => env(
|
'cookie' => env(
|
||||||
'SESSION_COOKIE',
|
'SESSION_COOKIE',
|
||||||
Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
|
Str::slug(env('APP_NAME', 'laravel'), '_') . '_session'
|
||||||
),
|
),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
58
config/trustedproxy.php
Normal file
58
config/trustedproxy.php
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set trusted proxy IP addresses.
|
||||||
|
*
|
||||||
|
* Both IPv4 and IPv6 addresses are
|
||||||
|
* supported, along with CIDR notation.
|
||||||
|
*
|
||||||
|
* The "*" character is syntactic sugar
|
||||||
|
* within TrustedProxy to trust any proxy
|
||||||
|
* that connects directly to your server,
|
||||||
|
* a requirement when you cannot know the address
|
||||||
|
* of your proxy (e.g. if using ELB or similar).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
'proxies' => [
|
||||||
|
// * HA PROXY prod "Virtual IP".
|
||||||
|
'192.168.31.9',
|
||||||
|
// * HA PROXY prod "Virtual IP" redondance.
|
||||||
|
'192.168.31.10',
|
||||||
|
// * HA PROXY dev "Virtual IP".
|
||||||
|
'192.168.34.15'
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To trust one or more specific proxies that connect
|
||||||
|
* directly to your server, use an array or a string separated by comma of IP addresses:
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Or, to trust all proxies that connect
|
||||||
|
* directly to your server, use a "*"
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Which headers to use to detect proxy related data (For, Host, Proto, Port)
|
||||||
|
*
|
||||||
|
* Options include:
|
||||||
|
*
|
||||||
|
* - Illuminate\Http\Request::HEADER_X_FORWARDED_ALL (use all x-forwarded-* headers to establish trust)
|
||||||
|
* - Illuminate\Http\Request::HEADER_FORWARDED (use the FORWARDED header to establish trust)
|
||||||
|
* - Illuminate\Http\Request::HEADER_X_FORWARDED_AWS_ELB (If you are using AWS Elastic Load Balancer)
|
||||||
|
*
|
||||||
|
* - 'HEADER_X_FORWARDED_ALL' (use all x-forwarded-* headers to establish trust)
|
||||||
|
* - 'HEADER_FORWARDED' (use the FORWARDED header to establish trust)
|
||||||
|
* - 'HEADER_X_FORWARDED_AWS_ELB' (If you are using AWS Elastic Load Balancer)
|
||||||
|
*
|
||||||
|
* @link https://symfony.com/doc/current/deployment/proxies.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
'headers' => Illuminate\Http\Request::HEADER_X_FORWARDED_FOR |
|
||||||
|
Illuminate\Http\Request::HEADER_X_FORWARDED_HOST |
|
||||||
|
Illuminate\Http\Request::HEADER_X_FORWARDED_PORT |
|
||||||
|
Illuminate\Http\Request::HEADER_X_FORWARDED_PROTO,
|
||||||
|
];
|
||||||
21
database/factories/ArcadiaBrandFactory.php
Normal file
21
database/factories/ArcadiaBrandFactory.php
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
class ArcadiaBrandFactory extends Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function definition()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'arcadia_id' => $this->faker->unique()->numberBetween(1, 999),
|
||||||
|
'libelle' => $this->faker->unique()->company()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
20
database/factories/ServiceFactory.php
Normal file
20
database/factories/ServiceFactory.php
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
class ServiceFactory extends Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function definition()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => $this->faker->unique()->name() . ' Service'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,7 @@ class UserFactory extends Factory
|
||||||
'name' => $this->faker->name(),
|
'name' => $this->faker->name(),
|
||||||
'email' => $this->faker->unique()->safeEmail(),
|
'email' => $this->faker->unique()->safeEmail(),
|
||||||
'email_verified_at' => now(),
|
'email_verified_at' => now(),
|
||||||
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
|
'password' => 'password',
|
||||||
'remember_token' => Str::random(10),
|
'remember_token' => Str::random(10),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
@ -30,6 +30,7 @@ class UserFactory extends Factory
|
||||||
*/
|
*/
|
||||||
public function unverified()
|
public function unverified()
|
||||||
{
|
{
|
||||||
|
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||||
return $this->state(function (array $attributes) {
|
return $this->state(function (array $attributes) {
|
||||||
return [
|
return [
|
||||||
'email_verified_at' => null,
|
'email_verified_at' => null,
|
||||||
|
|
|
||||||
105
database/factories/XmlIncoFactory.php
Normal file
105
database/factories/XmlIncoFactory.php
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
class XmlIncoFactory extends Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
|
||||||
|
*/
|
||||||
|
public function definition()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'nom' => $this->faker->unique()->words(rand(2, 3), true),
|
||||||
|
'prod_saison' => $this->faker->boolean(),
|
||||||
|
'allegations' => $this->faker->boolean(75) ? $this->faker->sentence(7) : null,
|
||||||
|
'descriptif' => $this->faker->boolean(75) ? 'descriptif ' . $this->faker->text(150) : null,
|
||||||
|
'pictures' => [],
|
||||||
|
'astuces' => $this->faker->boolean(75) ? 'astuces ' . $this->faker->text(150) : null,
|
||||||
|
'lib_conso' => $this->faker->boolean(75) ? $this->faker->sentence(7) : null,
|
||||||
|
'nb_portion' => $this->faker->boolean(75) ? $this->faker->text(20) : null,
|
||||||
|
'typ_portion' => $this->faker->boolean(75) ? 'typ_portion ' . $this->faker->text(150) : null,
|
||||||
|
'pds_moy_theo' => $this->faker->boolean(75) ? $this->faker->numberBetween(0, 250) : null,
|
||||||
|
|
||||||
|
'conseil_prepa' => $this->faker->boolean(75) ? 'conseil_prepa ' . $this->faker->text(150) : null,
|
||||||
|
'arg_vente' => $this->faker->boolean(75) ? 'arg_vente ' . $this->faker->text(150) : null,
|
||||||
|
'lst_ing' => $this->faker->boolean(75) ? 'lst_ing ' . $this->faker->text(150) : null,
|
||||||
|
'lib_allergene' => $this->faker->boolean(75) ? 'lib_allergene ' . $this->faker->text(150) : null,
|
||||||
|
|
||||||
|
'nutritional_values' => [
|
||||||
|
'val_energ_kj_r' => $this->faker->numberBetween(250, 1200),
|
||||||
|
'val_energ_kcal_r' => $this->faker->numberBetween(50, 400),
|
||||||
|
'mat_grass_r' => $this->faker->numberBetween(6, 30),
|
||||||
|
'ags_r' => '5.3',
|
||||||
|
'glucides_r' => '2',
|
||||||
|
'sucre_r' => '1.4',
|
||||||
|
'fib_alim_r' => '0.8',
|
||||||
|
'proteine_r' => '17',
|
||||||
|
'sel_r' => '0.47',
|
||||||
|
'sodium_r' => '188',
|
||||||
|
'fruits_r' => '0',
|
||||||
|
'fruits_coque_sech_r' => '0',
|
||||||
|
'pourcent_apport_ref_e14_r' => '11.4',
|
||||||
|
'pourcent_apport_ref_kjo_r' => '11.5',
|
||||||
|
'pourcent_apport_ref_fat_r' => '24.7',
|
||||||
|
'pourcent_apport_ref_fatsat_r' => '29.8',
|
||||||
|
'pourcent_apport_ref_choval_r' => '0.6',
|
||||||
|
'pourcent_apport_ref_sugar_r' => '1.7',
|
||||||
|
'pourcent_apport_ref_fibtg_r' => null,
|
||||||
|
'pourcent_apport_ref_pro_r' => '33',
|
||||||
|
'pourcent_apport_ref_salteq_r' => '6.7',
|
||||||
|
'taille_port' => null,
|
||||||
|
'val_energ_kj_p' => null,
|
||||||
|
'val_energ_kcal_p' => null,
|
||||||
|
'mat_grass_p' => null,
|
||||||
|
'ags_p' => null,
|
||||||
|
'glucides_p' => null,
|
||||||
|
'sucre_p' => null,
|
||||||
|
'fib_alim_p' => null,
|
||||||
|
'proteine_p' => null,
|
||||||
|
'sodium_p' => null,
|
||||||
|
'fruits_p' => null,
|
||||||
|
'sel_p' => null,
|
||||||
|
'fruits_coque_sech_p' => null,
|
||||||
|
'pourcent_apport_ref_e14_p' => null,
|
||||||
|
'pourcent_apport_ref_kjo_p' => null,
|
||||||
|
'pourcent_apport_ref_fat_p' => null,
|
||||||
|
'pourcent_apport_ref_fatsat_p' => null,
|
||||||
|
'pourcent_apport_ref_choval_p' => null,
|
||||||
|
'pourcent_apport_ref_sugar_p' => null,
|
||||||
|
'pourcent_apport_ref_fibtg_p' => null,
|
||||||
|
'pourcent_apport_ref_pro_p' => null,
|
||||||
|
'pourcent_apport_ref_salteq_p' => null,
|
||||||
|
'apport_port' => 'Apport de r\u00e9f\u00e9rence pour un adulte type (8400Kj-2000Kcal)',
|
||||||
|
'part_p' => '32',
|
||||||
|
'part_g' => '4',
|
||||||
|
'part_l' => '63',
|
||||||
|
'p_sur_l' => '1.13',
|
||||||
|
'pourcent_ags' => '35',
|
||||||
|
'pourcent_agi' => '65',
|
||||||
|
'pourcent_ss' => '70',
|
||||||
|
'pourcent_amidon' => '30',
|
||||||
|
'composante_n' => '9',
|
||||||
|
'composante_p' => '5',
|
||||||
|
'pourcent_fruits' => '0',
|
||||||
|
'score_nutri' => '4',
|
||||||
|
'classement' => 'C',
|
||||||
|
'mat_grass_r_prec' => '=',
|
||||||
|
'ags_r_prec' => '=',
|
||||||
|
'glucides_r_prec' => '=',
|
||||||
|
'sucre_r_prec' => '=',
|
||||||
|
'fib_alim_r_prec' => '=',
|
||||||
|
'proteine_r_prec' => '=',
|
||||||
|
'sel_r_prec' => '=',
|
||||||
|
'sodium_r_prec' => null,
|
||||||
|
'fruits_r_prec' => null,
|
||||||
|
'fruits_coque_sech_r_prec' => null
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class CreateArcadiaBrandsTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('arcadia_brands', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
$table->unsignedBigInteger('arcadia_id')->unique();
|
||||||
|
$table->string('libelle');
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('arcadia_brands');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
class CreatePersonalAccessTokensTable extends Migration
|
class CreateServicesTable extends Migration
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Run the migrations.
|
* Run the migrations.
|
||||||
|
|
@ -13,13 +13,14 @@ class CreatePersonalAccessTokensTable extends Migration
|
||||||
*/
|
*/
|
||||||
public function up()
|
public function up()
|
||||||
{
|
{
|
||||||
Schema::create('personal_access_tokens', function (Blueprint $table) {
|
Schema::create('services', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->morphs('tokenable');
|
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
$table->string('token', 64)->unique();
|
$table->string('bearer_token')->unique();
|
||||||
$table->text('abilities')->nullable();
|
$table->string('bearer_pwd', 312);
|
||||||
$table->timestamp('last_used_at')->nullable();
|
$table->timestamp('bearer_token_date');
|
||||||
|
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -31,6 +32,6 @@ class CreatePersonalAccessTokensTable extends Migration
|
||||||
*/
|
*/
|
||||||
public function down()
|
public function down()
|
||||||
{
|
{
|
||||||
Schema::dropIfExists('personal_access_tokens');
|
Schema::dropIfExists('services');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class CreateArcadiaBrandServiceTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('arcadia_brand_service', function (Blueprint $table) {
|
||||||
|
$table->foreignId('service_id')->constrained('services');
|
||||||
|
$table->foreignId('arcadia_brand_id')->constrained('arcadia_brands');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('arcadia_brand_service');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,13 +15,13 @@ class CreateXmlIncosTable extends Migration
|
||||||
public function up()
|
public function up()
|
||||||
{
|
{
|
||||||
Schema::create('xml_incos', function (Blueprint $table) {
|
Schema::create('xml_incos', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id()->comment('Inco ID (NO_ART)');
|
||||||
$table->string('nom')->nullable();
|
$table->string('nom')->nullable();
|
||||||
$table->boolean('prod_saison');
|
$table->boolean('prod_saison');
|
||||||
|
|
||||||
$table->string('allegations')->nullable();
|
$table->string('allegations')->nullable();
|
||||||
$table->text('descriptif')->nullable();
|
$table->text('descriptif')->nullable();
|
||||||
$table->json('picture');
|
$table->json('pictures');
|
||||||
$table->text('astuces')->nullable();
|
$table->text('astuces')->nullable();
|
||||||
$table->string('lib_conso')->nullable();
|
$table->string('lib_conso')->nullable();
|
||||||
$table->string('nb_portion', 20)->nullable();
|
$table->string('nb_portion', 20)->nullable();
|
||||||
|
|
@ -35,6 +35,8 @@ class CreateXmlIncosTable extends Migration
|
||||||
|
|
||||||
$table->json('nutritional_values');
|
$table->json('nutritional_values');
|
||||||
|
|
||||||
|
$table->foreignId('arcadia_brand_id')->constrained('arcadia_brands');
|
||||||
|
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
// phpcs:ignore PSR1.Classes.ClassDeclaration.MissingNamespace
|
||||||
|
class CreateServiceStats extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('service_stats', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
$table->foreignId('service_id')->constrained('services');
|
||||||
|
$table->date('date');
|
||||||
|
$table->unsignedBigInteger('nb_calls')->default(0);
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('service_stats');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,13 @@
|
||||||
|
|
||||||
namespace Database\Seeders;
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\ArcadiaBrand;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\XmlInco;
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use SplFileInfo;
|
||||||
|
|
||||||
class DatabaseSeeder extends Seeder
|
class DatabaseSeeder extends Seeder
|
||||||
{
|
{
|
||||||
|
|
@ -13,6 +19,35 @@ class DatabaseSeeder extends Seeder
|
||||||
*/
|
*/
|
||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
// \App\Models\User::factory(10)->create();
|
/** @var \Illuminate\Database\Eloquent\Collection $services */
|
||||||
|
$services = Service::factory(5)->create();
|
||||||
|
/** @var \Illuminate\Database\Eloquent\Collection $brands */
|
||||||
|
$brands = ArcadiaBrand::factory(10)->create();
|
||||||
|
|
||||||
|
// * Associate brands to services
|
||||||
|
$services->map(function (Service $service) use (&$brands) {
|
||||||
|
$pullBrands = $brands->random(rand(1, 2));
|
||||||
|
$service->arcadiaBrands()->sync($pullBrands);
|
||||||
|
$brands = $brands->diff($pullBrands);
|
||||||
|
});
|
||||||
|
|
||||||
|
$disk = Storage::disk('inco');
|
||||||
|
|
||||||
|
$brands->map(function (ArcadiaBrand $brand) use ($disk) {
|
||||||
|
XmlInco::factory(rand(150, 355))->make()
|
||||||
|
->each(function (XmlInco $xmlInco) use ($disk, $brand) {
|
||||||
|
$xmlInco->pictures = collect([
|
||||||
|
new SplFileInfo(resource_path('seeders/xml_inco/fakeimage1.png')),
|
||||||
|
new SplFileInfo(resource_path('seeders/xml_inco/fakeimage2.png'))
|
||||||
|
])->random(rand(0, 2))->map(function (SplFileInfo $image) use ($disk) {
|
||||||
|
$imgNamePath = $image->getFilename();
|
||||||
|
$imageFinalPath = "images/inco/$imgNamePath";
|
||||||
|
$disk->put($imgNamePath, File::get($image->getPathname()));
|
||||||
|
return $imageFinalPath;
|
||||||
|
})->all();
|
||||||
|
$xmlInco->arcadiaBrand()->associate($brand);
|
||||||
|
$xmlInco->saveOrFail();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16468
package-lock.json
generated
Normal file
16468
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -11,7 +11,9 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"axios": "^0.21",
|
"axios": "^0.21",
|
||||||
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
"laravel-mix": "^6.0.6",
|
"laravel-mix": "^6.0.6",
|
||||||
|
"laravel-mix-polyfill": "^3.0.1",
|
||||||
"lodash": "^4.17.19",
|
"lodash": "^4.17.19",
|
||||||
"postcss": "^8.1.14"
|
"postcss": "^8.1.14"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
48
phpunit.xml
48
phpunit.xml
|
|
@ -1,31 +1,31 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/schema/9.2.xsd" bootstrap="vendor/autoload.php" colors="true">
|
||||||
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
|
|
||||||
bootstrap="vendor/autoload.php"
|
|
||||||
colors="true"
|
|
||||||
>
|
|
||||||
<testsuites>
|
<testsuites>
|
||||||
<testsuite name="Unit">
|
<testsuite name="Api">
|
||||||
<directory suffix="Test.php">./tests/Unit</directory>
|
<directory suffix="Test.php">./tests/Api</directory>
|
||||||
</testsuite>
|
|
||||||
<testsuite name="Feature">
|
|
||||||
<directory suffix="Test.php">./tests/Feature</directory>
|
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
<coverage processUncoveredFiles="true">
|
<filter>
|
||||||
<include>
|
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||||
<directory suffix=".php">./app</directory>
|
<directory>./app</directory>
|
||||||
</include>
|
</whitelist>
|
||||||
</coverage>
|
</filter>
|
||||||
|
<logging>
|
||||||
|
<log type="coverage-html" target="tests/coverage" />
|
||||||
|
</logging>
|
||||||
<php>
|
<php>
|
||||||
<server name="APP_ENV" value="testing"/>
|
<server name="APP_ENV" value="testing" />
|
||||||
<server name="BCRYPT_ROUNDS" value="4"/>
|
<server name="APP_URL" value="http://127.0.0.1:8888" />
|
||||||
<server name="CACHE_DRIVER" value="array"/>
|
|
||||||
<!-- <server name="DB_CONNECTION" value="sqlite"/> -->
|
<server name="BCRYPT_ROUNDS" value="4" />
|
||||||
<!-- <server name="DB_DATABASE" value=":memory:"/> -->
|
<server name="CACHE_DRIVER" value="array" />
|
||||||
<server name="MAIL_MAILER" value="array"/>
|
|
||||||
<server name="QUEUE_CONNECTION" value="sync"/>
|
<server name="MAIL_MAILER" value="array" />
|
||||||
<server name="SESSION_DRIVER" value="array"/>
|
<env name="QUEUE_DRIVER" value="database" />
|
||||||
<server name="TELESCOPE_ENABLED" value="false"/>
|
<server name="QUEUE_CONNECTION" value="database" />
|
||||||
|
|
||||||
|
<server name="SESSION_DRIVER" value="array" />
|
||||||
|
<server name="TELESCOPE_ENABLED" value="false" />
|
||||||
|
|
||||||
</php>
|
</php>
|
||||||
</phpunit>
|
</phpunit>
|
||||||
|
|
|
||||||
0
resources/documents/arcadia/marque.xsd
Normal file
0
resources/documents/arcadia/marque.xsd
Normal file
270
resources/documents/inco/incollector.xsd
Normal file
270
resources/documents/inco/incollector.xsd
Normal file
|
|
@ -0,0 +1,270 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Created with Liquid Technologies Online Tools 1.0 (https://www.liquid-technologies.com) -->
|
||||||
|
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
|
||||||
|
xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xs:element name="INCO">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="typemouvementinformatique" type="xs:string" />
|
||||||
|
<xs:element name="NO_ART" type="xs:string" />
|
||||||
|
<xs:element name="INCO_COMPO">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="TBL_INCO_COMPO">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="ID" type="xs:string" />
|
||||||
|
<xs:element name="ID_INCO" type="xs:string" />
|
||||||
|
<xs:element name="NOM" type="xs:string" />
|
||||||
|
<xs:element name="NUMERO" type="xs:string" />
|
||||||
|
<xs:element name="TAILLE_REF" type="xs:string" />
|
||||||
|
<xs:element name="VAL_ENERG_KJ_R" type="xs:string" />
|
||||||
|
<xs:element name="VAL_ENERG_KCAL_R" type="xs:string" />
|
||||||
|
<xs:element name="MAT_GRASS_R" type="xs:string" />
|
||||||
|
<xs:element name="AGS_R" type="xs:string" />
|
||||||
|
<xs:element name="GLUCIDES_R" type="xs:string" />
|
||||||
|
<xs:element name="SUCRE_R" type="xs:string" />
|
||||||
|
<xs:element name="FIB_ALIM_R" type="xs:string" />
|
||||||
|
<xs:element name="PROTEINE_R" type="xs:string" />
|
||||||
|
<xs:element name="SEL_R" type="xs:string" />
|
||||||
|
<xs:element name="SODIUM_R" type="xs:string" />
|
||||||
|
<xs:element name="FRUITS_R" type="xs:string" />
|
||||||
|
<xs:element name="FRUITS_COQUE_SECH_R" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_E14_R" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_KJO_R" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_FAT_R" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_FATSAT_R" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_CHOVAL_R" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_SUGAR_R" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_FIBTG_R" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_PRO_R" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_SALTEQ_R" type="xs:string" />
|
||||||
|
<xs:element name="TAILLE_PORT" type="xs:string" />
|
||||||
|
<xs:element name="VAL_ENERG_KJ_P" type="xs:string" />
|
||||||
|
<xs:element name="VAL_ENERG_KCAL_P" type="xs:string" />
|
||||||
|
<xs:element name="MAT_GRASS_P" type="xs:string" />
|
||||||
|
<xs:element name="AGS_P" type="xs:string" />
|
||||||
|
<xs:element name="GLUCIDES_P" type="xs:string" />
|
||||||
|
<xs:element name="SUCRE_P" type="xs:string" />
|
||||||
|
<xs:element name="FIB_ALIM_P" type="xs:string" />
|
||||||
|
<xs:element name="PROTEINE_P" type="xs:string" />
|
||||||
|
<xs:element name="SEL_P" type="xs:string" />
|
||||||
|
<xs:element name="SODIUM_P" type="xs:string" />
|
||||||
|
<xs:element name="FRUITS_P" type="xs:string" />
|
||||||
|
<xs:element name="FRUITS_COQUE_SECH_P" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_E14_P" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_KJO_P" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_FAT_P" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_FATSAT_P" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_CHOVAL_P" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_SUGAR_P" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_FIBTG_P" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_PRO_P" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_APPORT_REF_SALTEQ_P" type="xs:string" />
|
||||||
|
<xs:element name="APPORT_PORT" type="xs:string" />
|
||||||
|
<xs:element name="PART_P" type="xs:string" />
|
||||||
|
<xs:element name="PART_G" type="xs:string" />
|
||||||
|
<xs:element name="PART_L" type="xs:string" />
|
||||||
|
<xs:element name="P_SUR_L" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_AGS" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_AGI" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_SS" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_AMIDON" type="xs:string" />
|
||||||
|
<xs:element name="COMPOSANTE_N" type="xs:string" />
|
||||||
|
<xs:element name="COMPOSANTE_P" type="xs:string" />
|
||||||
|
<xs:element name="POURCENT_FRUITS" type="xs:string" />
|
||||||
|
<xs:element name="SCORE_NUTRI" type="xs:string" />
|
||||||
|
<xs:element name="CLASSEMENT" type="xs:string" />
|
||||||
|
<xs:element name="MAT_GRASS_R_PREC">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="code" type="xs:string" />
|
||||||
|
<xs:element name="libelle" type="xs:string" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="AGS_R_PREC">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="code" type="xs:string" />
|
||||||
|
<xs:element name="libelle" type="xs:string" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="GLUCIDES_R_PREC">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="code" type="xs:string" />
|
||||||
|
<xs:element name="libelle" type="xs:string" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="SUCRE_R_PREC">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="code" type="xs:string" />
|
||||||
|
<xs:element name="libelle" type="xs:string" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="FIB_ALIM_R_PREC">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="code" type="xs:string" />
|
||||||
|
<xs:element name="libelle" type="xs:string" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="PROTEINE_R_PREC">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="code" type="xs:string" />
|
||||||
|
<xs:element name="libelle" type="xs:string" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="SEL_R_PREC">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="code" type="xs:string" />
|
||||||
|
<xs:element name="libelle" type="xs:string" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="SODIUM_R_PREC">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="code" />
|
||||||
|
<xs:element name="libelle" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="FRUITS_R_PREC">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="code" />
|
||||||
|
<xs:element name="libelle" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="FRUITS_COQUE_SECH_R_PREC">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="code" />
|
||||||
|
<xs:element name="libelle" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="PROD_SAISON">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="code" type="xs:string" />
|
||||||
|
<xs:element name="libelle" type="xs:string" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="CONSEIL_PREPA" type="xs:string" />
|
||||||
|
<xs:element name="ALLEGATIONS" />
|
||||||
|
<xs:element name="ASTUCES" />
|
||||||
|
<xs:element name="DESCRIPTIF" type="xs:string" />
|
||||||
|
<xs:element name="ARG_VENTE" type="xs:string" />
|
||||||
|
<xs:element name="NB_PORTION" type="xs:string" />
|
||||||
|
<xs:element name="TYP_PORTION">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="code" type="xs:string" />
|
||||||
|
<xs:element name="libelle" type="xs:string" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="FAMILLE_VTE">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="COD_FAMVTE">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="FAM_VENTE">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="COD_FAMVTE" type="xs:string" />
|
||||||
|
<xs:element name="LIB_FAMVTE" type="xs:string" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="DATE_STATUT_CHEF_PRODUIT" type="xs:string" />
|
||||||
|
<xs:element name="EXPLOITANT">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="SOCIETE">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="ID_SOCIETE" type="xs:string" />
|
||||||
|
<xs:element name="NOM_SOCIETE" type="xs:string" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="PDS_MOY_THEO" />
|
||||||
|
<xs:element name="LIB_CONSO" />
|
||||||
|
<xs:element name="LST_ING" />
|
||||||
|
<xs:element name="LST_ALLERGENE" />
|
||||||
|
<xs:element name="ALLEGAT_SANTE" />
|
||||||
|
<xs:element name="ALLEGAT_NUTRIT" />
|
||||||
|
<xs:element name="ALLEGAT_AUTRE" />
|
||||||
|
<xs:element name="MEDIA_VISUEL_EAI">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="MEDIA_VISUEL_EAI">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="NumeroArticle" type="xs:string" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="version" type="xs:string" />
|
||||||
|
<xs:element name="DATEVERSION" type="xs:string" />
|
||||||
|
<xs:element name="STATUT_CHEF_PRODUIT">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="code" type="xs:string" />
|
||||||
|
<xs:element name="libelle" type="xs:string" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="CHEF_PRODUIT">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="UTILISATEURS">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="code" type="xs:string" />
|
||||||
|
<xs:element name="libelle" type="xs:string" />
|
||||||
|
|
||||||
|
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:schema>
|
||||||
BIN
resources/seeders/xml_inco/fakeimage1.png
Normal file
BIN
resources/seeders/xml_inco/fakeimage1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3 MiB |
BIN
resources/seeders/xml_inco/fakeimage2.png
Normal file
BIN
resources/seeders/xml_inco/fakeimage2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 MiB |
|
|
@ -1,11 +1,11 @@
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
<head>
|
<head>
|
||||||
<title>{{ $appName }} Exception Handler: Unvalid XML</title>
|
<title>XML Exception Handler: Invalid XML</title>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>{{ $appName }} à reçu un fichier xml non valide !!</h1>
|
<h1>{{ config('app.name') }} à reçu un fichier xml non valide</h1>
|
||||||
<h3>{{ $strError }}</h3>
|
<h3>{{ $strError }}</h3>
|
||||||
<h4>FICHIER: `{{ $filename }}`</h4>
|
<h4>FICHIER: `{{ $filename }}`</h4>
|
||||||
<p>{{ $exception->getMessage() }}</p>
|
<p>{{ $exception->getMessage() }}</p>
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
<b>file</b> : {{$exception->getFile() }}<br>
|
<b>file</b> : {{$exception->getFile() }}<br>
|
||||||
<b>line</b> : {{$exception->getLine() }}<br>
|
<b>line</b> : {{$exception->getLine() }}<br>
|
||||||
<p>
|
<p>
|
||||||
<b>Merci de corriger le flux, puis de renvoyer le fichier en provoquant un mouvement EAI.</b><br><br>
|
<b>Merci de corriger les données, puis de renvoyer le fichier en provoquant un mouvement EAI.</b><br><br>
|
||||||
<b>Pour rappel</b> : Il est recommandé de valider un fichier XML à l'aide d'un <b>DTD</b><br>
|
<b>Pour rappel</b> : Il est recommandé de valider un fichier XML à l'aide d'un <b>DTD</b><br>
|
||||||
<a href="https://www.w3schools.com/xml/xml_dtd.asp">https://www.w3schools.com/xml/xml_dtd.asp</a><br>
|
<a href="https://www.w3schools.com/xml/xml_dtd.asp">https://www.w3schools.com/xml/xml_dtd.asp</a><br>
|
||||||
ou bien <b>XSD</b> <a href="https://www.w3schools.com/xml/schema_intro.asp">https://www.w3schools.com/xml/schema_intro.asp</a><br><br>
|
ou bien <b>XSD</b> <a href="https://www.w3schools.com/xml/schema_intro.asp">https://www.w3schools.com/xml/schema_intro.asp</a><br><br>
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
use App\Http\Controllers\Api\V1\ApiController as V1ApiController;
|
||||||
|
use App\Http\Controllers\Api\V1\XmlIncoController as V1XmlIncoController;
|
||||||
|
|
||||||
/*
|
Route::prefix('v1')->name('apiV1.')->group(function () {
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| API Routes
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Here is where you can register API routes for your application. These
|
|
||||||
| routes are loaded by the RouteServiceProvider within a group which
|
|
||||||
| is assigned the "api" middleware group. Enjoy building your API!
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
|
// * PUBLIC
|
||||||
return $request->user();
|
Route::get('/ping', [V1ApiController::class, 'ping'])->name('ping');
|
||||||
|
|
||||||
|
// * PRIVATE
|
||||||
|
Route::middleware('auth:api')->group(function () {
|
||||||
|
Route::prefix('inco')->name('incos.')->group(function () {
|
||||||
|
Route::get('/', [V1XmlIncoController::class, 'index'])->name('index');
|
||||||
|
Route::get('/{xmlinco}', [V1XmlIncoController::class, 'show'])->name('show');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Broadcast;
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Broadcast Channels
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Here you may register all of the event broadcasting channels that your
|
|
||||||
| application supports. The given channel authorization callbacks are
|
|
||||||
| used to check if an authenticated user can listen to the channel.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
Broadcast::channel('App.Models.User.{id}', function ($user, $id) {
|
|
||||||
return (int) $user->id === (int) $id;
|
|
||||||
});
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Inspiring;
|
|
||||||
use Illuminate\Support\Facades\Artisan;
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Console Routes
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| This file is where you may define all of your Closure based console
|
|
||||||
| commands. Each Closure is bound to a command instance allowing a
|
|
||||||
| simple approach to interacting with each command's IO methods.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
Artisan::command('inspire', function () {
|
|
||||||
$this->comment(Inspiring::quote());
|
|
||||||
})->purpose('Display an inspiring quote');
|
|
||||||
|
|
@ -3,14 +3,14 @@
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Web Routes
|
| Web Routes
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| Here is where you can register web routes for your application. These
|
| Here is where you can register web routes for your application. These
|
||||||
| routes are loaded by the RouteServiceProvider within a group which
|
| routes are loaded by the RouteServiceProvider within a group which
|
||||||
| contains the "web" middleware group. Now create something great!
|
| contains the "web" middleware group. Now create something great!
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Route::get('/', function () {
|
Route::get('/', function () {
|
||||||
|
|
|
||||||
1
storage/.gitignore
vendored
1
storage/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
||||||
inco/*
|
|
||||||
|
|
@ -1,273 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Created with Liquid Technologies Online Tools 1.0 (https://www.liquid-technologies.com) -->
|
|
||||||
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
||||||
<xs:element name="LIST">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="INCO">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="typemouvementinformatique" type="xs:string" />
|
|
||||||
<xs:element name="NO_ART" type="xs:string" />
|
|
||||||
<xs:element name="INCO_COMPO">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="TBL_INCO_COMPO">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="ID" type="xs:string" />
|
|
||||||
<xs:element name="ID_INCO" type="xs:string" />
|
|
||||||
<xs:element name="NOM" type="xs:string" />
|
|
||||||
<xs:element name="NUMERO" type="xs:string" />
|
|
||||||
<xs:element name="TAILLE_REF" type="xs:string" />
|
|
||||||
<xs:element name="VAL_ENERG_KJ_R" type="xs:string" />
|
|
||||||
<xs:element name="VAL_ENERG_KCAL_R" type="xs:string" />
|
|
||||||
<xs:element name="MAT_GRASS_R" type="xs:string" />
|
|
||||||
<xs:element name="AGS_R" type="xs:string" />
|
|
||||||
<xs:element name="GLUCIDES_R" type="xs:string" />
|
|
||||||
<xs:element name="SUCRE_R" type="xs:string" />
|
|
||||||
<xs:element name="FIB_ALIM_R" type="xs:string" />
|
|
||||||
<xs:element name="PROTEINE_R" type="xs:string" />
|
|
||||||
<xs:element name="SEL_R" type="xs:string" />
|
|
||||||
<xs:element name="SODIUM_R" type="xs:string" />
|
|
||||||
<xs:element name="FRUITS_R" type="xs:string" />
|
|
||||||
<xs:element name="FRUITS_COQUE_SECH_R" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_E14_R" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_KJO_R" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_FAT_R" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_FATSAT_R" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_CHOVAL_R" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_SUGAR_R" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_FIBTG_R" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_PRO_R" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_SALTEQ_R" type="xs:string" />
|
|
||||||
<xs:element name="TAILLE_PORT" type="xs:string" />
|
|
||||||
<xs:element name="VAL_ENERG_KJ_P" type="xs:string" />
|
|
||||||
<xs:element name="VAL_ENERG_KCAL_P" type="xs:string" />
|
|
||||||
<xs:element name="MAT_GRASS_P" type="xs:string" />
|
|
||||||
<xs:element name="AGS_P" type="xs:string" />
|
|
||||||
<xs:element name="GLUCIDES_P" type="xs:string" />
|
|
||||||
<xs:element name="SUCRE_P" type="xs:string" />
|
|
||||||
<xs:element name="FIB_ALIM_P" type="xs:string" />
|
|
||||||
<xs:element name="PROTEINE_P" type="xs:string" />
|
|
||||||
<xs:element name="SEL_P" type="xs:string" />
|
|
||||||
<xs:element name="SODIUM_P" type="xs:string" />
|
|
||||||
<xs:element name="FRUITS_P" type="xs:string" />
|
|
||||||
<xs:element name="FRUITS_COQUE_SECH_P" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_E14_P" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_KJO_P" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_FAT_P" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_FATSAT_P" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_CHOVAL_P" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_SUGAR_P" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_FIBTG_P" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_PRO_P" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_APPORT_REF_SALTEQ_P" type="xs:string" />
|
|
||||||
<xs:element name="APPORT_PORT" type="xs:string" />
|
|
||||||
<xs:element name="PART_P" type="xs:string" />
|
|
||||||
<xs:element name="PART_G" type="xs:string" />
|
|
||||||
<xs:element name="PART_L" type="xs:string" />
|
|
||||||
<xs:element name="P_SUR_L" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_AGS" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_AGI" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_SS" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_AMIDON" type="xs:string" />
|
|
||||||
<xs:element name="COMPOSANTE_N" type="xs:string" />
|
|
||||||
<xs:element name="COMPOSANTE_P" type="xs:string" />
|
|
||||||
<xs:element name="POURCENT_FRUITS" type="xs:string" />
|
|
||||||
<xs:element name="SCORE_NUTRI" type="xs:string" />
|
|
||||||
<xs:element name="CLASSEMENT" type="xs:string" />
|
|
||||||
<xs:element name="MAT_GRASS_R_PREC">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="code" type="xs:string" />
|
|
||||||
<xs:element name="libelle" type="xs:string" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element name="AGS_R_PREC">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="code" type="xs:string" />
|
|
||||||
<xs:element name="libelle" type="xs:string" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element name="GLUCIDES_R_PREC">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="code" type="xs:string" />
|
|
||||||
<xs:element name="libelle" type="xs:string" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element name="SUCRE_R_PREC">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="code" type="xs:string" />
|
|
||||||
<xs:element name="libelle" type="xs:string" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element name="FIB_ALIM_R_PREC">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="code" type="xs:string" />
|
|
||||||
<xs:element name="libelle" type="xs:string" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element name="PROTEINE_R_PREC">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="code" type="xs:string" />
|
|
||||||
<xs:element name="libelle" type="xs:string" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element name="SEL_R_PREC">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="code" type="xs:string" />
|
|
||||||
<xs:element name="libelle" type="xs:string" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element name="SODIUM_R_PREC">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="code" />
|
|
||||||
<xs:element name="libelle" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element name="FRUITS_R_PREC">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="code" />
|
|
||||||
<xs:element name="libelle" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element name="FRUITS_COQUE_SECH_R_PREC">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="code" />
|
|
||||||
<xs:element name="libelle" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element name="PROD_SAISON">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="code" type="xs:string" />
|
|
||||||
<xs:element name="libelle" type="xs:string" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element name="CONSEIL_PREPA" type="xs:string" />
|
|
||||||
<xs:element name="ALLEGATIONS" />
|
|
||||||
<xs:element name="ASTUCES" />
|
|
||||||
<xs:element name="DESCRIPTIF" type="xs:string" />
|
|
||||||
<xs:element name="ARG_VENTE" type="xs:string" />
|
|
||||||
<xs:element name="NB_PORTION" type="xs:string" />
|
|
||||||
<xs:element name="TYP_PORTION">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="code" type="xs:string" />
|
|
||||||
<xs:element name="libelle" type="xs:string" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element name="FAMILLE_VTE">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="COD_FAMVTE">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="FAM_VENTE">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="COD_FAMVTE" type="xs:string" />
|
|
||||||
<xs:element name="LIB_FAMVTE" type="xs:string" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element name="DATE_STATUT_CHEF_PRODUIT" type="xs:string" />
|
|
||||||
<xs:element name="EXPLOITANT">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="SOCIETE">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="ID_SOCIETE" type="xs:string" />
|
|
||||||
<xs:element name="NOM_SOCIETE" type="xs:string" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element name="PDS_MOY_THEO" />
|
|
||||||
<xs:element name="LIB_CONSO" />
|
|
||||||
<xs:element name="LST_ING" />
|
|
||||||
<xs:element name="LST_ALLERGENE" />
|
|
||||||
<xs:element name="ALLEGAT_SANTE" />
|
|
||||||
<xs:element name="ALLEGAT_NUTRIT" />
|
|
||||||
<xs:element name="ALLEGAT_AUTRE" />
|
|
||||||
<xs:element name="MEDIA_VISUEL_EAI">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="MEDIA_VISUEL_EAI">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="NumeroArticle" type="xs:string" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element name="version" type="xs:string" />
|
|
||||||
<xs:element name="DATEVERSION" type="xs:string" />
|
|
||||||
<xs:element name="STATUT_CHEF_PRODUIT">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="code" type="xs:string" />
|
|
||||||
<xs:element name="libelle" type="xs:string" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element name="CHEF_PRODUIT">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="UTILISATEURS">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="code" type="xs:string" />
|
|
||||||
<xs:element name="libelle" type="xs:string" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
</xs:schema>
|
|
||||||
0
storage/marque/archives/.gitkeep
Normal file
0
storage/marque/archives/.gitkeep
Normal file
0
storage/marque/invalid/.gitkeep
Normal file
0
storage/marque/invalid/.gitkeep
Normal file
13
test.sh
Executable file
13
test.sh
Executable file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#composer runtestmigration >/dev/null
|
||||||
|
composer runserve
|
||||||
|
# If argument is passed run with coverage
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo 'no coverage'
|
||||||
|
composer runtest
|
||||||
|
else
|
||||||
|
echo 'with coverage'
|
||||||
|
composer runtestWithCoverage
|
||||||
|
fi
|
||||||
|
composer killserve
|
||||||
77
tests/Api/V1/ApiBase.php
Normal file
77
tests/Api/V1/ApiBase.php
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Api\V1;
|
||||||
|
|
||||||
|
use App\Enums\ApiStatus;
|
||||||
|
use App\Lib\Helper;
|
||||||
|
use App\Models\Service;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||||
|
use Illuminate\Testing\AssertableJsonString;
|
||||||
|
use Illuminate\Testing\TestResponse;
|
||||||
|
use Tests\CreatesApplication;
|
||||||
|
|
||||||
|
class ApiBase extends BaseTestCase
|
||||||
|
{
|
||||||
|
use CreatesApplication;
|
||||||
|
use DatabaseTransactions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make assertion that json response has status.
|
||||||
|
*
|
||||||
|
* @param AssertableJsonString $json
|
||||||
|
* @param ApiStatus $status
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function assertApiResponseStatusIs(AssertableJsonString $json, ApiStatus $status)
|
||||||
|
{
|
||||||
|
$json->assertFragment(['status' => $status->value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raw Function to call api
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param string $uri
|
||||||
|
* @param array $data
|
||||||
|
* @param array $headers
|
||||||
|
* @return \Illuminate\Testing\TestResponse
|
||||||
|
*/
|
||||||
|
protected function callApi(string $method, string $uri, array $data = [], array $headers = []): TestResponse
|
||||||
|
{
|
||||||
|
if (!isset($data['timestamp'])) {
|
||||||
|
$data['timestamp'] = \time();
|
||||||
|
}
|
||||||
|
return $this->json($method, $uri, $data, $headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raw Function to call api with Token
|
||||||
|
*
|
||||||
|
* @param Service $service
|
||||||
|
* @param string $method
|
||||||
|
* @param string $uri
|
||||||
|
* @param array $data
|
||||||
|
* @param array $headers
|
||||||
|
* @return \Illuminate\Testing\TestResponse
|
||||||
|
*/
|
||||||
|
protected function callApiWithToken(
|
||||||
|
Service $service,
|
||||||
|
string $method,
|
||||||
|
string $uri,
|
||||||
|
array $data = [],
|
||||||
|
array $headers = []
|
||||||
|
): TestResponse {
|
||||||
|
$data['bearer_token'] = $service->bearer_token;
|
||||||
|
$data['timestamp'] = \gmdate('Y-m-d H:i:s');
|
||||||
|
|
||||||
|
$data['signature'] = Helper::signRequest(
|
||||||
|
$service->bearer_pwd,
|
||||||
|
$uri,
|
||||||
|
$method,
|
||||||
|
$data
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->callApi($method, $uri, $data, $headers);
|
||||||
|
}
|
||||||
|
}
|
||||||
186
tests/Api/V1/ApiTest.php
Normal file
186
tests/Api/V1/ApiTest.php
Normal file
|
|
@ -0,0 +1,186 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Api\V1;
|
||||||
|
|
||||||
|
use App\Enums\ApiError;
|
||||||
|
use App\Enums\ApiStatus;
|
||||||
|
use App\Models\Service;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Tests\CreatesApplication;
|
||||||
|
|
||||||
|
class ApiTest extends ApiBase
|
||||||
|
{
|
||||||
|
use CreatesApplication;
|
||||||
|
use DatabaseTransactions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test ApiV2 only handle json calls
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testOnlyJsonCallsAreHandled()
|
||||||
|
{
|
||||||
|
$response = $this->call('get', route('apiV1.ping'), [
|
||||||
|
'timestamp' => \gmdate('Y-m-d H:i:s')
|
||||||
|
]);
|
||||||
|
$response->assertStatus(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test timestamp middleware works as expected.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testTimestampMiddleware()
|
||||||
|
{
|
||||||
|
$response = $this->json('get', route('apiV1.ping'), [
|
||||||
|
'timestamp' => \gmdate('Y-m-d H:i:s')
|
||||||
|
]);
|
||||||
|
$json = $response->decodeResponseJson();
|
||||||
|
$this->assertApiResponseStatusIs($json, ApiStatus::OK());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp middleware should fail if timestamp parameter is missing.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testTimestampMiddlewareCheckFailMissing()
|
||||||
|
{
|
||||||
|
$response = $this->json('get', route('apiV1.ping'), []);
|
||||||
|
$json = $response->decodeResponseJson();
|
||||||
|
$this->assertApiResponseStatusIs($json, ApiStatus::KO());
|
||||||
|
$json->assertFragment(['data' => [
|
||||||
|
'code' => ApiError::wrongTimeStamp()->value,
|
||||||
|
'message' => ApiError::wrongTimeStamp()->label
|
||||||
|
]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp middleware should fail if timestamp parameter has wrong format.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testTimestampMiddlewareCheckFailWrongFormat()
|
||||||
|
{
|
||||||
|
$response = $this->json('get', route('apiV1.ping'), ['timestamp' => \gmdate('Y/m/d')]);
|
||||||
|
$json = $response->decodeResponseJson();
|
||||||
|
$this->assertApiResponseStatusIs($json, ApiStatus::KO());
|
||||||
|
$json->assertFragment(['data' => [
|
||||||
|
'code' => ApiError::wrongTimeStamp()->value,
|
||||||
|
'message' => ApiError::wrongTimeStamp()->label
|
||||||
|
]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp middleware should fail if timestamp is older than "config('api.timestampTimeout', 10)".
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testTimestampMiddlewareCheckFailIfTooOld()
|
||||||
|
{
|
||||||
|
$response = $this->json('get', route('apiV1.ping'), [
|
||||||
|
'timestamp' => \gmdate('Y-m-d H:i:s', \time() - config('api.timestampTimeout', 10))
|
||||||
|
]);
|
||||||
|
$json = $response->decodeResponseJson();
|
||||||
|
$this->assertApiResponseStatusIs($json, ApiStatus::KO());
|
||||||
|
$json->assertFragment(['data' => [
|
||||||
|
'code' => ApiError::wrongTimeStamp()->value,
|
||||||
|
'message' => ApiError::wrongTimeStamp()->label
|
||||||
|
]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test api route throttle.
|
||||||
|
*
|
||||||
|
* ! This has to be the last test of this class.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testApiThrottle()
|
||||||
|
{
|
||||||
|
for ($i = config('api.apiRequestRatePerMinute', 100); $i >= 0; $i--) {
|
||||||
|
$this->json('get', route('apiV1.ping'), [
|
||||||
|
'timestamp' => \gmdate('Y-m-d H:i:s')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->json('get', route('apiV1.ping'), [
|
||||||
|
'timestamp' => \gmdate('Y-m-d H:i:s')
|
||||||
|
]);
|
||||||
|
$json = $response->decodeResponseJson();
|
||||||
|
$this->assertApiResponseStatusIs($json, ApiStatus::KO());
|
||||||
|
$response->assertStatus(429);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test wrong http method call
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testWrongMethodCall()
|
||||||
|
{
|
||||||
|
$response = $this->json('post', route('apiV1.ping'), [
|
||||||
|
'timestamp' => \gmdate('Y-m-d H:i:s', \time())
|
||||||
|
]);
|
||||||
|
$json = $response->decodeResponseJson();
|
||||||
|
$this->assertApiResponseStatusIs($json, ApiStatus::KO());
|
||||||
|
$this->assertEquals(ApiError::methodNotAllowed()->value, $json->json('data.code'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Auth fail when no token
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testAuthFailNoToken()
|
||||||
|
{
|
||||||
|
$response = $this->json('get', route('apiV1.incos.show', [
|
||||||
|
'xmlinco' => Str::uuid()
|
||||||
|
]), [
|
||||||
|
'timestamp' => \gmdate('Y-m-d H:i:s', \time())
|
||||||
|
]);
|
||||||
|
$json = $response->decodeResponseJson();
|
||||||
|
$this->assertApiResponseStatusIs($json, ApiStatus::KO());
|
||||||
|
$this->assertEquals(ApiError::apiAuthFailed()->value, $json->json('data.code'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Auth fail when unknown token
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testAuthFailUnknownToken()
|
||||||
|
{
|
||||||
|
$response = $this->json('get', route('apiV1.incos.show', [
|
||||||
|
'xmlinco' => Str::uuid()
|
||||||
|
]), [
|
||||||
|
'bearer_token' => 'qsdqsdqsd',
|
||||||
|
'timestamp' => \gmdate('Y-m-d H:i:s', \time())
|
||||||
|
]);
|
||||||
|
$json = $response->decodeResponseJson();
|
||||||
|
$this->assertApiResponseStatusIs($json, ApiStatus::KO());
|
||||||
|
$this->assertEquals(ApiError::apiAuthFailed()->value, $json->json('data.code'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Auth fail when wrong signature
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testAuthFailWrongSignature()
|
||||||
|
{
|
||||||
|
$service = Service::query()->firstOrFail();
|
||||||
|
$response = $this->json('get', route('apiV1.incos.show', [
|
||||||
|
'xmlinco' => Str::uuid()
|
||||||
|
]), [
|
||||||
|
'bearer_token' => $service->bearer_token,
|
||||||
|
'timestamp' => \gmdate('Y-m-d H:i:s', \time()),
|
||||||
|
'signature' => 'sdqdqsdqd'
|
||||||
|
]);
|
||||||
|
$json = $response->decodeResponseJson();
|
||||||
|
$this->assertApiResponseStatusIs($json, ApiStatus::KO());
|
||||||
|
$this->assertEquals(ApiError::apiAuthFailed()->value, $json->json('data.code'));
|
||||||
|
}
|
||||||
|
}
|
||||||
161
tests/Api/V1/XmlIncoTest.php
Normal file
161
tests/Api/V1/XmlIncoTest.php
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Api\V1;
|
||||||
|
|
||||||
|
use App\Enums\ApiStatus;
|
||||||
|
use App\Models\ArcadiaBrand;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\XmlInco;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Tests\CreatesApplication;
|
||||||
|
|
||||||
|
class XmlIncoTest extends ApiBase
|
||||||
|
{
|
||||||
|
use CreatesApplication;
|
||||||
|
use DatabaseTransactions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test XmlInco relations to its related models
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testXmlIncoRelations()
|
||||||
|
{
|
||||||
|
/** @var \App\Models\XmlInco $xmlInco */
|
||||||
|
$xmlInco = XmlInco::query()->firstOrFail();
|
||||||
|
$arcadiaBrand = $xmlInco->arcadiaBrand;
|
||||||
|
$this->assertInstanceOf(ArcadiaBrand::class, $arcadiaBrand);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test customer Authentification ok.
|
||||||
|
* route: apiV2.customers.authenticate
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testXmlIncoIndex()
|
||||||
|
{
|
||||||
|
/** @var \App\Models\Service $service */
|
||||||
|
extract($this->getService(true));
|
||||||
|
|
||||||
|
$response = $this->callApiWithToken(
|
||||||
|
$service,
|
||||||
|
'get',
|
||||||
|
route('apiV1.incos.index'),
|
||||||
|
['per_page' => '2']
|
||||||
|
);
|
||||||
|
|
||||||
|
$json = $response->decodeResponseJson();
|
||||||
|
|
||||||
|
$this->assertApiResponseStatusIs($json, ApiStatus::OK());
|
||||||
|
$response->assertJsonStructure([
|
||||||
|
'data' => [
|
||||||
|
'*' => [
|
||||||
|
'id',
|
||||||
|
'prod_saison',
|
||||||
|
'allegations',
|
||||||
|
'descriptif',
|
||||||
|
'pictures' => [
|
||||||
|
'*'
|
||||||
|
],
|
||||||
|
'astuces',
|
||||||
|
'lib_conso',
|
||||||
|
'nb_portion',
|
||||||
|
'typ_portion',
|
||||||
|
'pds_moy_theo',
|
||||||
|
'conseil_prepa',
|
||||||
|
'arg_vente',
|
||||||
|
'lst_ing',
|
||||||
|
'lib_allergene',
|
||||||
|
'nutritional_values' => [
|
||||||
|
'val_energ_kj_r',
|
||||||
|
'val_energ_kcal_r',
|
||||||
|
'mat_grass_r',
|
||||||
|
'ags_r',
|
||||||
|
'glucides_r',
|
||||||
|
'sucre_r',
|
||||||
|
'fib_alim_r',
|
||||||
|
'sel_r',
|
||||||
|
'sodium_r',
|
||||||
|
'fruits_r',
|
||||||
|
'fruits_coque_sech_r',
|
||||||
|
'pourcent_apport_ref_e14_r',
|
||||||
|
'pourcent_apport_ref_kjo_r',
|
||||||
|
'pourcent_apport_ref_fat_r',
|
||||||
|
'pourcent_apport_ref_fatsat_r',
|
||||||
|
'pourcent_apport_ref_choval_r',
|
||||||
|
'pourcent_apport_ref_sugar_r',
|
||||||
|
'pourcent_apport_ref_fibtg_r',
|
||||||
|
'pourcent_apport_ref_pro_r',
|
||||||
|
'pourcent_apport_ref_salteq_r',
|
||||||
|
'taille_port',
|
||||||
|
'val_energ_kj_p',
|
||||||
|
'val_energ_kcal_p',
|
||||||
|
'mat_grass_p',
|
||||||
|
'ags_p',
|
||||||
|
'glucides_p',
|
||||||
|
'sucre_p',
|
||||||
|
'fib_alim_p',
|
||||||
|
'proteine_p',
|
||||||
|
'sodium_p',
|
||||||
|
'fruits_p',
|
||||||
|
'sel_p',
|
||||||
|
'fruits_coque_sech_p',
|
||||||
|
'pourcent_apport_ref_e14_p',
|
||||||
|
'pourcent_apport_ref_kjo_p',
|
||||||
|
'pourcent_apport_ref_fat_p',
|
||||||
|
'pourcent_apport_ref_fatsat_p',
|
||||||
|
'pourcent_apport_ref_choval_p',
|
||||||
|
'pourcent_apport_ref_sugar_p',
|
||||||
|
'pourcent_apport_ref_fibtg_p',
|
||||||
|
'pourcent_apport_ref_pro_p',
|
||||||
|
'pourcent_apport_ref_salteq_p',
|
||||||
|
'apport_port',
|
||||||
|
'part_p',
|
||||||
|
'part_g',
|
||||||
|
'part_l',
|
||||||
|
'p_sur_l',
|
||||||
|
'pourcent_ags',
|
||||||
|
'pourcent_agi',
|
||||||
|
'pourcent_ss',
|
||||||
|
'pourcent_amidon',
|
||||||
|
'composante_n',
|
||||||
|
'composante_p',
|
||||||
|
'pourcent_fruits',
|
||||||
|
'score_nutri',
|
||||||
|
'classement',
|
||||||
|
'mat_grass_r_prec',
|
||||||
|
'ags_r_prec',
|
||||||
|
'glucides_r_prec',
|
||||||
|
'sucre_r_prec',
|
||||||
|
'fib_alim_r_prec',
|
||||||
|
'proteine_r_prec',
|
||||||
|
'sel_r_prec',
|
||||||
|
'sodium_r_prec',
|
||||||
|
'fruits_r_prec',
|
||||||
|
'fruits_coque_sech_r_prec',
|
||||||
|
],
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Customer models with given status (active or not)
|
||||||
|
*
|
||||||
|
* @param boolean $random Randomize the customer to get.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function getService(bool $random = true): array
|
||||||
|
{
|
||||||
|
if ($random) {
|
||||||
|
/** @var \App\Models\Service $service */
|
||||||
|
$service = Service::inRandomOrder()->whereHas('arcadiaBrands')->firstOrFail();
|
||||||
|
} else {
|
||||||
|
/** @var \App\Models\Service $service */
|
||||||
|
$service = Service::query()->whereHas('arcadiaBrands')->firstOrFail();
|
||||||
|
}
|
||||||
|
|
||||||
|
return compact('service');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,7 @@ trait CreatesApplication
|
||||||
*/
|
*/
|
||||||
public function createApplication()
|
public function createApplication()
|
||||||
{
|
{
|
||||||
$app = require __DIR__.'/../bootstrap/app.php';
|
$app = require __DIR__ . '/../bootstrap/app.php';
|
||||||
|
|
||||||
$app->make(Kernel::class)->bootstrap();
|
$app->make(Kernel::class)->bootstrap();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Feature;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
class ExampleTest extends TestCase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* A basic test example.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function test_example()
|
|
||||||
{
|
|
||||||
$response = $this->get('/');
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
<?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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
64
webpack.mix.js
vendored
64
webpack.mix.js
vendored
|
|
@ -1,17 +1,53 @@
|
||||||
|
const path = require('path');
|
||||||
const mix = require('laravel-mix');
|
const mix = require('laravel-mix');
|
||||||
|
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||||
|
|
||||||
/*
|
require('laravel-mix-polyfill');
|
||||||
|--------------------------------------------------------------------------
|
mix.webpackConfig({
|
||||||
| Mix Asset Management
|
resolve: {
|
||||||
|--------------------------------------------------------------------------
|
alias: {
|
||||||
|
|
'@': path.resolve('resources/assets')
|
||||||
| Mix provides a clean, fluent API for defining some Webpack build steps
|
}
|
||||||
| for your Laravel applications. By default, we are compiling the CSS
|
},
|
||||||
| file for the application as well as bundling up all the JS files.
|
plugins: [
|
||||||
|
|
new CleanWebpackPlugin({
|
||||||
*/
|
cleanOnceBeforeBuildPatterns: [
|
||||||
|
'css/*',
|
||||||
|
'js/*',
|
||||||
|
'images/*',
|
||||||
|
'fonts/*',
|
||||||
|
'assets/*'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
mix.js('resources/js/app.js', 'public/js')
|
let assetDir = 'assets';
|
||||||
.postCss('resources/css/app.css', 'public/css', [
|
mix.options({
|
||||||
//
|
fileLoaderDirs: {
|
||||||
]);
|
images: `${assetDir}/images`,
|
||||||
|
fonts: `${assetDir}/fonts`
|
||||||
|
},
|
||||||
|
postCss: [
|
||||||
|
require('postcss-discard-comments')({
|
||||||
|
removeAll: true
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
mix.js('resources/js/app.js', 'public/js/app.js')
|
||||||
|
.sass('resources/sass/app.scss', 'public/css')
|
||||||
|
.vue()
|
||||||
|
.polyfill({
|
||||||
|
corejs: 3,
|
||||||
|
enabled: true,
|
||||||
|
useBuiltIns: "usage",
|
||||||
|
targets: { "firefox": "50", "ie": 11 }
|
||||||
|
});
|
||||||
|
|
||||||
|
// * COPY JS STATIC VENDORS
|
||||||
|
mix.copyDirectory('resources/vendor/js', "public/js");
|
||||||
|
mix.copyDirectory('resources/vendor/css', "public/css");
|
||||||
|
|
||||||
|
// Copy Blade Images Assets
|
||||||
|
mix.copyDirectory('resources/assets', 'public/assets');
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue