3.0.0 release
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEEbXCAeh4tnJbE5ao1VRlr7kRfYEkFAmHWzmEACgkQVRlr7kRf YEnOawv/dh5IkA3miThsXr+ALWqVipe2qack2OhZ2I5w8GkuzOhXtaMo6eGIFwjV vOtLISsQnCPlcZMw/HY3gVc3EWF2MKku7NgMMnsWi85Gi5jI5LlXygwhx0eZkC4O 6Fc1SACyaSl8zxgiGpgwLqTTQcloWt2qJ1UdA4v6bD5y8ubcZ18vsgfxsnpfdmvN P0h6yGej/KeQtwb0d9UdgdBzU7dcI0s75Oy2NurgPMN2NYHBOUcfF/en8rrBb6Sy IrPWquDWyBTntPzLWhOpqllm/oRvBYjNTacxH4fqEWdHWPy21MwHsClkkbteNWyN cSGEnJuknh9GV5TPjG8YjKTlYnIrvBiPh6odMmOQDSS0NR19Tah30P0sXfj5JTX2 Phi+8ktyyMdS2xBKU+8e6hgYuDWWJN7z/yFLdBdow4DBV+aZn6M++q7xN+AdWkML 0ICaYjuZYVN1k7a+dt5/YvRfqS1y3zvfi8UmKBdvGz/NQIfH5nJod7/BEUW/IbAp Erm4+3ft =oP5n -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEEbXCAeh4tnJbE5ao1VRlr7kRfYEkFAmHWznkACgkQVRlr7kRf YEkI/wwAhEqDJ2ohlg7ral3KrGKRiYfrQ3FxWdG4WF7c2yBPEIEUfhl7RtomKnQ/ rrMXsbKFEDs2BnfDXoZth4KYLiWjgXGCtcTHZTRBQ/LJTaeAnfXzuPUtiaoHj003 8sKmW8tMTPSbHEtZ6f0zZVqQGFuZvzBSbDhLd25m/H8pZgYrCGsltsOBFEgCRf1v wbStHyWWgw2HCGfZ8TCl5GGRDVUIKC0KV0N4X3cqW4TmrGdm5e8N8WEzT/hpcYnF p4/DUexa3R4QIc6N0MaEohYeslmtKycRzNloAu+omlCzmOz2mvd4stRfoOPwAAxt B0KH6vVO8hULhGFSf2+/zffHcnIuqVRgjdE8d5AszgVQaJJF4tTPzQSVslwmobZJ JDN+KEWfYE9vY1OGiuYt1wBkF3IURDCZ1mRQ3Fq4uYSQchj+G27V/QKrPfAHF3Wu sfl035HlWgIv3Zd/QoAO1hil8QX2tMy6O0fzdBysK7KAYNri/SEgSCK39gRfjl2D EoRuomau =NcUP -----END PGP SIGNATURE----- Merge tag '3.0.0' into trunk 3.0.0 release
This commit is contained in:
commit
a6731e98ae
565 changed files with 119064 additions and 3648 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -5,8 +5,6 @@
|
|||
/vendor
|
||||
.vscode/
|
||||
.idea/
|
||||
package-lock.json
|
||||
composer.lock
|
||||
.env
|
||||
.env.testing
|
||||
.env.backup
|
||||
|
|
|
|||
2
.gitmodules
vendored
2
.gitmodules
vendored
|
|
@ -1,3 +1,3 @@
|
|||
[submodule "clients/V2"]
|
||||
path = clients/V2
|
||||
url = https://ldc-web:phaeZ6ej@voight001.lesidc.com/crm-client
|
||||
url = https://voight001.lesidc.com/crm-client
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
]
|
||||
}
|
||||
73
README.md
73
README.md
|
|
@ -1,61 +1,16 @@
|
|||
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400"></a></p>
|
||||
# Comment lancer les tests unitaires ?
|
||||
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/laravel/framework"><img src="https://travis-ci.org/laravel/framework.svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
||||
</p>
|
||||
### Attention, si vous utilisez juste un .env à la place d'un .env.testing votre base de donnée de developpement sera effacée à chaque lancement des tests unitaires
|
||||
|
||||
## About Laravel
|
||||
|
||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||
|
||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||
|
||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
||||
|
||||
## Learning Laravel
|
||||
|
||||
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
|
||||
|
||||
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains over 1500 video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
||||
|
||||
## Laravel Sponsors
|
||||
|
||||
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the Laravel [Patreon page](https://patreon.com/taylorotwell).
|
||||
|
||||
### Premium Partners
|
||||
|
||||
- **[Vehikl](https://vehikl.com/)**
|
||||
- **[Tighten Co.](https://tighten.co)**
|
||||
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
|
||||
- **[64 Robots](https://64robots.com)**
|
||||
- **[Cubet Techno Labs](https://cubettech.com)**
|
||||
- **[Cyber-Duck](https://cyber-duck.co.uk)**
|
||||
- **[Many](https://www.many.co.uk)**
|
||||
- **[Webdock, Fast VPS Hosting](https://www.webdock.io/en)**
|
||||
- **[DevSquad](https://devsquad.com)**
|
||||
- **[OP.GG](https://op.gg)**
|
||||
|
||||
## Contributing
|
||||
|
||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||
|
||||
## Security Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
||||
|
||||
## License
|
||||
|
||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
Etapes :
|
||||
- cp .env.testing.example .env.testing (spécial tests unitaires)
|
||||
- php artisan key:generate
|
||||
- parameter les BDD dans le .env.testing
|
||||
- php artisan config:cache
|
||||
- git submodule init
|
||||
- git submodule update (récupérer le client)
|
||||
- composer install
|
||||
- service start mysql (votre base de donnée accessible sur 127.0.0.1:3306)
|
||||
- chmod u+x ./test.sh
|
||||
- php artisan optimize
|
||||
- composer run test
|
||||
|
|
|
|||
|
|
@ -7,9 +7,11 @@ use App\Models\CustomerLogin;
|
|||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class DeleteUnconfirmedCustomers extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
|
|
@ -28,25 +30,26 @@ class DeleteUnconfirmedCustomers extends Command
|
|||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
* @return integer
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$customersLogins = CustomerLogin::where([['status', 0], ['created_at', '<', Carbon::now()->subDay(1)]])->get();
|
||||
foreach ($customersLogins as $customerLogin) {
|
||||
// Si non activé et créer il y a plus d'un jour : Suppression
|
||||
// Si non activé et créer il y a plus d'un jour : Suppression.
|
||||
$customerLogin->customer->infos()->where('brand_id', $customerLogin->brand_id)->first()->delete();
|
||||
// On récupère son id pour le cas ou on doit le supprimer complètement après
|
||||
// On récupère son id pour le cas ou on doit le supprimer complètement après.
|
||||
$customerId = $customerLogin->customer->id;
|
||||
// Suppression de ses infos de login pour la marque
|
||||
// Suppression de ses infos de login pour la marque.
|
||||
$customerLogin->delete();
|
||||
// Si le customer n'a plus d'info il n'a donc plus besoin de compte en crm
|
||||
// Si le customer n'a plus d'info il n'a donc plus besoin de compte en crm.
|
||||
if (count($customerLogin->customer->infos) === 0) {
|
||||
// Suppression du customer une fois tout le reste supprimé
|
||||
// Suppression du customer une fois tout le reste supprimé.
|
||||
$customer = Customer::find($customerId);
|
||||
$customer->optInHistory()->delete();
|
||||
$customer->delete();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ namespace App\Console\Commands;
|
|||
|
||||
use App\Enums\ApiError;
|
||||
use App\Enums\ApiStatus;
|
||||
use App\Enums\ArcadiaMailType;
|
||||
use App\Enums\ArcadiaOrderType;
|
||||
use App\Enums\Country;
|
||||
use App\Enums\QuestionType;
|
||||
|
|
@ -14,9 +13,11 @@ use Illuminate\Console\Command;
|
|||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class GenerateCrmClient extends Command
|
||||
{
|
||||
|
||||
private const TARGET = 'clients/V2/src';
|
||||
private const CLIENTNAMESPACE = 'LDC\\LDCCRM';
|
||||
|
||||
|
|
@ -37,7 +38,7 @@ class GenerateCrmClient extends Command
|
|||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
* @return integer
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
|
@ -45,8 +46,14 @@ class GenerateCrmClient extends Command
|
|||
$this->copyExceptions();
|
||||
$this->copyRoutesList();
|
||||
$this->copyApiConfigInfo();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy config files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function copyApiConfigInfo()
|
||||
{
|
||||
$this->info('Copy Api Config');
|
||||
|
|
@ -60,6 +67,11 @@ class GenerateCrmClient extends Command
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy routes into a json file
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function copyRoutesList()
|
||||
{
|
||||
$this->info('Copy Routes');
|
||||
|
|
@ -83,6 +95,11 @@ class GenerateCrmClient extends Command
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy Exeption classes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function copyExceptions(): void
|
||||
{
|
||||
$this->info('Copy Exceptions');
|
||||
|
|
@ -98,13 +115,17 @@ class GenerateCrmClient extends Command
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy enums classes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function copyEnums(): void
|
||||
{
|
||||
$this->info('Copy Enums');
|
||||
$classes = [
|
||||
ApiError::class,
|
||||
ApiStatus::class,
|
||||
ArcadiaMailType::class,
|
||||
ArcadiaOrderType::class,
|
||||
Country::class,
|
||||
QuestionType::class
|
||||
|
|
@ -121,7 +142,7 @@ class GenerateCrmClient extends Command
|
|||
/**
|
||||
* Copy Classes to specified folder
|
||||
*
|
||||
* @param array $classes
|
||||
* @param array $classes
|
||||
* @param string $folder
|
||||
* @return void
|
||||
*/
|
||||
|
|
@ -142,16 +163,24 @@ class GenerateCrmClient extends Command
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces a specific string with another on a file
|
||||
*
|
||||
* @param string $source
|
||||
* @param string $search
|
||||
* @param string $replace
|
||||
* @return void
|
||||
*/
|
||||
private function replaceInPhpFile(string $source, string $search, string $replace): void
|
||||
{
|
||||
$fContent = File::get($source);
|
||||
|
||||
// remove trailing \Api\V2
|
||||
// Remove trailing \Api\V2 .
|
||||
if ($pos = strpos($fContent, '\Api\V2')) {
|
||||
$fContent = substr_replace($fContent, '', $pos, strlen('\Api\V2'));
|
||||
}
|
||||
|
||||
// replace namespace top level
|
||||
// Replace namespace top level.
|
||||
$pos = strpos($fContent, $search);
|
||||
if ($pos !== false) {
|
||||
File::put($source, \str_replace($search, $replace, $fContent));
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@ 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
|
@ -36,7 +36,7 @@ class LdcWebStatusChecker extends Command
|
|||
*/
|
||||
public function handle()
|
||||
{
|
||||
$data = [];
|
||||
$data = [];
|
||||
$data['uuid'] = config('ldcweb.project_uuid');
|
||||
|
||||
// Sitemap.xml ?
|
||||
|
|
@ -53,10 +53,10 @@ class LdcWebStatusChecker extends Command
|
|||
$data['robots'] = 'false';
|
||||
}
|
||||
|
||||
//Check logs
|
||||
// Check logs.
|
||||
$data['logs'] = 0;
|
||||
$patherror = storage_path('/logs');
|
||||
$files = glob($patherror . '/*.log');
|
||||
$patherror = storage_path('/logs');
|
||||
$files = glob($patherror . '/*.log');
|
||||
if (count($files) > 0) {
|
||||
foreach ($files as $file) {
|
||||
$data['logs'] = $data['logs'] + filesize($file);
|
||||
|
|
@ -65,9 +65,9 @@ class LdcWebStatusChecker extends Command
|
|||
|
||||
$data['code']['version'] = app()->version();
|
||||
|
||||
// Send by cURL
|
||||
$url = 'https://ldc-web.ldc.fr/api/status-checker';
|
||||
$ch = curl_init($url);
|
||||
// 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);
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@ use Exception;
|
|||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class MigrateCrmUsers extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
|
|
@ -29,7 +31,7 @@ class MigrateCrmUsers extends Command
|
|||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
* @return integer
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
|
@ -65,7 +67,7 @@ class MigrateCrmUsers extends Command
|
|||
} catch (Exception $e) {
|
||||
$this->error(sprintf('Failed %s', $e));
|
||||
return -1;
|
||||
}
|
||||
}//end try
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,11 @@ namespace App\Console\Commands;
|
|||
use App\Jobs\StartStatJobs as JobsStartStatJobs;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class StartStatJobs extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
|
|
@ -25,7 +27,7 @@ class StartStatJobs extends Command
|
|||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
* @return integer
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@ use App\Models\User;
|
|||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class UserCreate extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
|
|
@ -26,12 +28,10 @@ class UserCreate extends Command
|
|||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
* @return integer
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$admin = false;
|
||||
|
||||
$firstname = $this->ask('What is the firstname?');
|
||||
$lastname = $this->ask('What is the lastname?');
|
||||
$email = $this->ask('What is the email?');
|
||||
|
|
@ -45,5 +45,6 @@ class UserCreate extends Command
|
|||
$user->save();
|
||||
|
||||
$this->info('User created.');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@ use App\Jobs\StartStatJobs;
|
|||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
|
||||
/**
|
||||
* The Artisan commands provided by your application.
|
||||
*
|
||||
|
|
@ -19,12 +21,11 @@ class Kernel extends ConsoleKernel
|
|||
/**
|
||||
* Define the application's command schedule.
|
||||
*
|
||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||
* @return void
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
// stats job on night
|
||||
$schedule->job(StartStatJobs::class)->dailyAt('03:30');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,14 +31,11 @@ use Kwaadpepper\Enum\BaseEnumRoutable;
|
|||
*
|
||||
* @method static self apiAuthFailed()
|
||||
* The authentification to the api failed. You may have to
|
||||
* check into config or .env file for your brand credentials
|
||||
* check into config or .env file for your service credentials
|
||||
*
|
||||
* @method static self authFailed()
|
||||
* The authentification for this customer failed because of wrong credentials
|
||||
*
|
||||
* @method static self needAuth()
|
||||
* This action requires to be authenticated
|
||||
*
|
||||
* @method static self methodNotAllowed()
|
||||
* This http method is not allowed
|
||||
*
|
||||
|
|
@ -67,6 +64,11 @@ use Kwaadpepper\Enum\BaseEnumRoutable;
|
|||
*/
|
||||
class ApiError extends BaseEnumRoutable
|
||||
{
|
||||
/**
|
||||
* Valeurs des possibilités de cet énumération (value)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function values(): array
|
||||
{
|
||||
return [
|
||||
|
|
@ -76,7 +78,6 @@ class ApiError extends BaseEnumRoutable
|
|||
'customerAlreadyActive' => 110,
|
||||
'customerNotActivated' => 116,
|
||||
'authFailed' => 117,
|
||||
'needAuth' => 118,
|
||||
'apiAuthFailed' => 119,
|
||||
'singleOptinCustomerExists' => 120,
|
||||
'methodNotAllowed' => 150,
|
||||
|
|
@ -87,6 +88,11 @@ class ApiError extends BaseEnumRoutable
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Libellés des possibilités de cet énumération (label)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function labels(): array
|
||||
{
|
||||
return [
|
||||
|
|
@ -97,7 +103,6 @@ class ApiError extends BaseEnumRoutable
|
|||
'customerAlreadyActive' => 'Le compte est déjà activé',
|
||||
'singleOptinCustomerExists' => 'Le single optin ne peut être ajouté car un compte existe.',
|
||||
'authFailed' => 'Echec de l\'authentification du compte',
|
||||
'needAuth' => 'Vous devez être authentifié',
|
||||
'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',
|
||||
|
|
|
|||
|
|
@ -13,6 +13,11 @@ use Kwaadpepper\Enum\BaseEnumRoutable;
|
|||
*/
|
||||
class ArcadiaMailType extends BaseEnumRoutable
|
||||
{
|
||||
/**
|
||||
* Valeurs des possibilités de cet énumération (value)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function values(): array
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -13,6 +13,11 @@ use Kwaadpepper\Enum\BaseEnumRoutable;
|
|||
*/
|
||||
class ArcadiaOrderType extends BaseEnumRoutable
|
||||
{
|
||||
/**
|
||||
* Valeurs des possibilités de cet énumération (value)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function values(): array
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -262,6 +262,11 @@ use Kwaadpepper\Enum\BaseEnumRoutable;
|
|||
*/
|
||||
class Country extends BaseEnumRoutable
|
||||
{
|
||||
/**
|
||||
* Valeurs des possibilités de cet énumération (value)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function values(): array
|
||||
{
|
||||
return [
|
||||
|
|
@ -517,6 +522,11 @@ class Country extends BaseEnumRoutable
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Libellés des possibilités de cet énumération (label)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function labels(): array
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -9,11 +9,15 @@ use Kwaadpepper\Enum\BaseEnumRoutable;
|
|||
* @method static self customer()
|
||||
* @method static self both()
|
||||
*
|
||||
* @package LDCCrmClient
|
||||
* @subpackage enums
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class CustomerExportType extends BaseEnumRoutable
|
||||
{
|
||||
/**
|
||||
* Valeurs des possibilités de cet énumération (value)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function values(): array
|
||||
{
|
||||
return [
|
||||
|
|
@ -22,6 +26,12 @@ class CustomerExportType extends BaseEnumRoutable
|
|||
'both' => 3
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Libellés des possibilités de cet énumération (label)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function labels(): array
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@ use Kwaadpepper\Enum\BaseEnumRoutable;
|
|||
*/
|
||||
class QuestionType extends BaseEnumRoutable
|
||||
{
|
||||
/**
|
||||
* Valeurs des possibilités de cet énumération (value)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function values(): array
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ use Throwable;
|
|||
*/
|
||||
class ApiErrorException extends Exception
|
||||
{
|
||||
|
||||
/**
|
||||
* Api Error
|
||||
* This value makes the error more explicit.
|
||||
|
|
@ -46,10 +45,18 @@ class ApiErrorException extends Exception
|
|||
* This value could be used to handle errors.
|
||||
* But please take in consideration $error first
|
||||
*
|
||||
* @var int
|
||||
* @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;
|
||||
|
|
@ -75,15 +82,23 @@ class ApiErrorException extends Exception
|
|||
/**
|
||||
* Get the http code
|
||||
*
|
||||
* @return int
|
||||
* @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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,16 @@ use Illuminate\Support\Facades\Log;
|
|||
*
|
||||
* @package LDCCrmClient
|
||||
* @subpackage exceptions
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class CrmException extends Exception
|
||||
{
|
||||
/**
|
||||
* This is used by laravel only.
|
||||
* It uses Log class to print the error.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function report()
|
||||
{
|
||||
Log::critical(sprintf('CRM API: %s', $this->getMessage()));
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ namespace App\Exceptions;
|
|||
use App\Enums\ApiError;
|
||||
use App\Enums\ApiStatus;
|
||||
use App\Exceptions\Api\V2\ApiErrorException;
|
||||
use App\Exceptions\Api\V2\CrmException;
|
||||
use App\Http\Resources\Api\V2\ApiResponse as V2ApiResponse;
|
||||
use App\Lib\Helper;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
|
|
@ -21,7 +20,6 @@ use Throwable;
|
|||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of the exception types that are not reported.
|
||||
*
|
||||
|
|
@ -49,93 +47,103 @@ class Handler extends ExceptionHandler
|
|||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Throwable $e
|
||||
* @param mixed $request
|
||||
* @param \Throwable $e
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
// phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
|
||||
public function render($request, Throwable $e)
|
||||
{
|
||||
if (Helper::isRequestForApiV2($request) and $request->isJson()) {
|
||||
switch (get_class($e)) {
|
||||
case AuthenticationException::class:
|
||||
/** @var AuthenticationException $e */
|
||||
return $this->getReponse(
|
||||
$request,
|
||||
401,
|
||||
ApiError::needAuth()->value,
|
||||
sprintf('%s, %s', ApiError::needAuth()->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
|
||||
);
|
||||
case CrmException::class:
|
||||
default:
|
||||
if (config('app.env') === 'testing') {
|
||||
// phpcs:ignore Squiz.PHP.DiscouragedFunctions.Found
|
||||
print_r((string)$e);
|
||||
}
|
||||
return $this->getReponse($request, 500, $e->getCode(), $e->getMessage());
|
||||
}
|
||||
}
|
||||
return $this->handleApiV2Exception($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 handleApiV2Exception(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
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -144,7 +152,8 @@ class Handler extends ExceptionHandler
|
|||
* @param Request $request
|
||||
* @param integer $httpCode
|
||||
* @param integer $apiCode
|
||||
* @param string $message
|
||||
* @param string $message
|
||||
* @param array $data
|
||||
* @return JsonResponse
|
||||
*/
|
||||
private static function getReponse(
|
||||
|
|
@ -155,13 +164,7 @@ class Handler extends ExceptionHandler
|
|||
array $data = []
|
||||
): JsonResponse {
|
||||
return (new V2ApiResponse(
|
||||
(object)array_merge(
|
||||
$data,
|
||||
[
|
||||
'message' => $message,
|
||||
'code' => $apiCode
|
||||
]
|
||||
),
|
||||
(object)array_merge($data, ['message' => $message, 'code' => $apiCode]),
|
||||
ApiStatus::KO()
|
||||
))->toResponse($request)->setStatusCode($httpCode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ use Illuminate\Database\Eloquent\Builder;
|
|||
use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
|
||||
use Kwaadpepper\CrudPolicies\Traits\CrudController;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class AnswerController extends Controller
|
||||
{
|
||||
use CrudController {
|
||||
|
|
@ -17,8 +20,19 @@ class AnswerController extends Controller
|
|||
/** @var Log */
|
||||
public static $modelClass = Answer::class;
|
||||
|
||||
/**
|
||||
* The Bo app layout
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $layout = 'layouts.app';
|
||||
|
||||
/**
|
||||
* Event index crud-policies
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
public function indexQuery(Builder $query): Builder
|
||||
{
|
||||
$parent = request()->question;
|
||||
|
|
@ -30,6 +44,12 @@ class AnswerController extends Controller
|
|||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload crud-policies index
|
||||
*
|
||||
* @param CrudIndexRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(CrudIndexRequest $request)
|
||||
{
|
||||
/** @var \Illuminate\View\View */
|
||||
|
|
|
|||
|
|
@ -13,8 +13,15 @@ use Illuminate\Support\Facades\Mail;
|
|||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class ArcadiaController extends Controller
|
||||
{
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return StatusResource
|
||||
*/
|
||||
public function outgoingMail(Request $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
|
|
@ -72,12 +79,16 @@ class ArcadiaController extends Controller
|
|||
Mail::to(config('mail.MAIL_ADMIN'))->send(new CourrierArcadiaError(
|
||||
"ARCADIA XML CREATION FAILED",
|
||||
"Une erreur est survenue lors de la création du fichier XML : " . $e->getMessage(),
|
||||
$xml
|
||||
$xml ?: ''
|
||||
));
|
||||
return new StatusResource(['status' => 'KO', 'code' => 204, 'message' => 'Arcadia XML Creation failed.']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $fields
|
||||
* @return array
|
||||
*/
|
||||
private function checkShippingRequiredFields($fields)
|
||||
{
|
||||
$test = [
|
||||
|
|
@ -101,8 +112,14 @@ class ArcadiaController extends Controller
|
|||
return ['status' => 'OK'];
|
||||
}
|
||||
|
||||
// phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
|
||||
private function checkRequiredFields($fields, $type)
|
||||
/**
|
||||
*
|
||||
* @param mixed $fields
|
||||
* @param mixed $type
|
||||
* @return array
|
||||
*/
|
||||
// phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Squiz.Commenting.FunctionComment.Missing
|
||||
private function checkRequiredFields($fields, $type): array
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
|
|
@ -184,7 +201,7 @@ class ArcadiaController extends Controller
|
|||
if ($key === false) {
|
||||
$errors[] = ['field' => 'code_unique', 'error' => 'required for game type'];
|
||||
}
|
||||
}
|
||||
}//end if
|
||||
|
||||
if (!empty($errors)) {
|
||||
return ['status' => 'KO', 'code' => 203, 'message' => $errors];
|
||||
|
|
@ -193,22 +210,37 @@ class ArcadiaController extends Controller
|
|||
return ['status' => 'OK'];
|
||||
}
|
||||
|
||||
// phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
|
||||
/**
|
||||
* @param mixed $fields
|
||||
* @param mixed $type
|
||||
* @param mixed $customer
|
||||
* @param mixed $customerInfos
|
||||
* @param mixed $shipping
|
||||
* @return string|false
|
||||
*/
|
||||
// phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Squiz.Commenting.FunctionComment.Missing
|
||||
private function makeXml($fields, $type, $customer, $customerInfos, $shipping)
|
||||
{
|
||||
$pivot = [
|
||||
'order_num' => 'Num_Commande', // required if type = default - format 9 numeric characters
|
||||
'order_date' => 'Date_Commande', // required - format : DD/MM/YYYY
|
||||
'operation' => 'Operation', // required
|
||||
'support_code' => 'Code_Support', // required
|
||||
'label_type_support' => 'Libelle_Type_Support', // required
|
||||
'package_unit_weight' => 'Poids_Unitaire_Colis', // required if type = default
|
||||
'order_num' => 'Num_Commande',
|
||||
// Required if type = default - format 9 numeric characters.
|
||||
'order_date' => 'Date_Commande',
|
||||
// Required - format : DD/MM/YYYY .
|
||||
'operation' => 'Operation',
|
||||
// Required.
|
||||
'support_code' => 'Code_Support',
|
||||
// Required.
|
||||
'label_type_support' => 'Libelle_Type_Support',
|
||||
// Required.
|
||||
'package_unit_weight' => 'Poids_Unitaire_Colis',
|
||||
// Required if type = default.
|
||||
'qty' => 'Quantite',
|
||||
'code_on_pack' => 'Code_on_Pack',
|
||||
'code_unique' => 'Code_Unique', // required if type = game
|
||||
// required if type = default - format 1 ou Null (oui Null et pas NULL)
|
||||
'code_unique' => 'Code_Unique',
|
||||
// Required if type = game.
|
||||
// Required if type = default - format 1 ou Null (oui Null et pas NULL).
|
||||
'state_order_cb' => 'Etat_Commande_CB',
|
||||
// required if type = default - format 1 ou Null (oui Null et pas NULL)
|
||||
// Required if type = default - format 1 ou Null (oui Null et pas NULL).
|
||||
'state_order_check' => 'Etat_Commande_Cheque',
|
||||
|
||||
];
|
||||
|
|
@ -285,7 +317,7 @@ class ArcadiaController extends Controller
|
|||
->appendChild($dom->createCDATASection("$customerInfos->city"));
|
||||
$order->appendChild($dom->createElement('Telephone'))
|
||||
->appendChild($dom->createCDATASection("$customerInfos->phone"));
|
||||
}
|
||||
}//end if
|
||||
|
||||
$orderLine = $order->appendChild($dom->createElement('Ligne_de_Commande'));
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Enums\Country;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\CustomerResource;
|
||||
use App\Http\Resources\StatusResource;
|
||||
|
|
@ -15,15 +16,17 @@ use Illuminate\Support\Facades\Hash;
|
|||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class CustomerController extends Controller
|
||||
{
|
||||
// TODO : Voir pour les contraintes de mot de passe
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage
|
||||
* @param Request $request
|
||||
* @return CustomerResource|StatusResource
|
||||
*/
|
||||
// phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Squiz.Commenting.FunctionComment.Missing
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
|
|
@ -49,10 +52,10 @@ class CustomerController extends Controller
|
|||
$optIn = filter_var($request->opt_in, FILTER_SANITIZE_STRING);
|
||||
$hashcode = Str::uuid();
|
||||
|
||||
// Récupère le customer s'il existe déjà
|
||||
// Récupère le customer s'il existe déjà.
|
||||
$customer = Customer::where('email', $email)->first();
|
||||
|
||||
// Si le customer n'existe pas déjà
|
||||
// Si le customer n'existe pas déjà.
|
||||
if (!$customer) {
|
||||
$customer = new Customer();
|
||||
$customer->email = $email;
|
||||
|
|
@ -62,7 +65,7 @@ class CustomerController extends Controller
|
|||
$singleOptin->delete();
|
||||
}
|
||||
} else {
|
||||
// Récupère les infos s'ils existent déjà
|
||||
// Récupère les infos s'ils existent déjà.
|
||||
$CustomerInfo = CustomerInfo::where([['customer_id', $customer->id], ['brand_id', $brandId]])->first();
|
||||
if ($CustomerInfo) {
|
||||
return new StatusResource([
|
||||
|
|
@ -72,19 +75,22 @@ class CustomerController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
$customerInfos = new CustomerInfo();
|
||||
$customerInfos->firstname = ucfirst(strtolower($firstname));
|
||||
$customerInfos->lastname = ucfirst(strtolower($lastname));
|
||||
$customerInfos->birth_date = !empty($request->birth_date) ?
|
||||
$customerInfos = new CustomerInfo();
|
||||
$customerInfos->firstname = ucfirst(strtolower($firstname));
|
||||
$customerInfos->lastname = ucfirst(strtolower($lastname));
|
||||
$customerInfos->birth_date = !empty($request->birth_date) ?
|
||||
filter_var($request->birth_date, FILTER_SANITIZE_STRING) : null;
|
||||
$customerInfos->address = !empty($request->address) ?
|
||||
$customerInfos->address = !empty($request->address) ?
|
||||
filter_var($request->address, FILTER_SANITIZE_STRING) : null;
|
||||
$customerInfos->address2 = !empty($request->address2) ?
|
||||
$customerInfos->address2 = !empty($request->address2) ?
|
||||
filter_var($request->address2, FILTER_SANITIZE_STRING) : null;
|
||||
$customerInfos->phone = !empty($request->phone) ?
|
||||
$customerInfos->phone = !empty($request->phone) ?
|
||||
filter_var($request->phone, FILTER_SANITIZE_STRING) : null;
|
||||
$customerInfos->country = !empty($request->country) ?
|
||||
filter_var($request->country, FILTER_SANITIZE_STRING) : null;
|
||||
try {
|
||||
$customerInfos->country = Country::make(filter_var($request->country, FILTER_SANITIZE_STRING));
|
||||
} catch (\Exception $e) {
|
||||
$customerInfos->country = Country::france();
|
||||
}
|
||||
$customerInfos->city = !empty($request->city) ?
|
||||
filter_var($request->city, FILTER_SANITIZE_STRING) : null;
|
||||
$customerInfos->postcode = !empty($request->postcode) ?
|
||||
|
|
@ -96,7 +102,7 @@ class CustomerController extends Controller
|
|||
|
||||
$customer->infos()->save($customerInfos);
|
||||
|
||||
// Création du login pour cet utilisateur sur ce service
|
||||
// Création du login pour cet utilisateur sur ce service.
|
||||
$customerLogin = new CustomerLogin();
|
||||
$customerLogin->customer_id = $customer->id;
|
||||
$customerLogin->password = Hash::make(trim($request->password));
|
||||
|
|
@ -106,7 +112,7 @@ class CustomerController extends Controller
|
|||
|
||||
$customer->logins()->save($customerLogin);
|
||||
|
||||
// Ajout dans la table d'historique des opt-in de la nouvelle valeur
|
||||
// Ajout dans la table d'historique des opt-in de la nouvelle valeur.
|
||||
$optInHistory = new CustomerOptInHistory();
|
||||
$optInHistory->customer_id = $customer->id;
|
||||
$optInHistory->brand_id = $brandId;
|
||||
|
|
@ -119,7 +125,9 @@ class CustomerController extends Controller
|
|||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @param Illuminate\Http\Request $request
|
||||
* @param mixed $type
|
||||
* @param mixed $value
|
||||
* @return CustomerResource | StatusResource
|
||||
*/
|
||||
public function show(Request $request, $type, $value)
|
||||
|
|
@ -156,7 +164,7 @@ class CustomerController extends Controller
|
|||
* @param int $id
|
||||
* @return CustomerResource|StatusResource
|
||||
*/
|
||||
// phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded
|
||||
// phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded, Squiz.Commenting.FunctionComment.Missing
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
|
|
@ -223,10 +231,10 @@ class CustomerController extends Controller
|
|||
}
|
||||
|
||||
if ($request->has('country')) {
|
||||
if (!empty($request->country)) {
|
||||
$CustomerInfo->country = filter_var($request->country, FILTER_SANITIZE_STRING);
|
||||
} else {
|
||||
$CustomerInfo->country = null;
|
||||
try {
|
||||
$CustomerInfo->country = Country::make(filter_var($request->country, FILTER_SANITIZE_STRING));
|
||||
} catch (\Exception $e) {
|
||||
$CustomerInfo->country = Country::france();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -250,7 +258,7 @@ class CustomerController extends Controller
|
|||
$optIn = filter_var($request->opt_in, FILTER_SANITIZE_STRING);
|
||||
|
||||
if ($CustomerInfo->opt_in != $optIn) {
|
||||
// Ajout dans la table d'historique des opt-in de la nouvelle valeur
|
||||
// Ajout dans la table d'historique des opt-in de la nouvelle valeur.
|
||||
$optInHistory = new CustomerOptInHistory();
|
||||
$optInHistory->customer_id = $customer->id;
|
||||
$optInHistory->brand_id = $brandId;
|
||||
|
|
@ -310,7 +318,7 @@ class CustomerController extends Controller
|
|||
'address' => $infos->address,
|
||||
'address2' => $infos->address2,
|
||||
'phone' => $infos->phone,
|
||||
'country' => $infos->country,
|
||||
'country' => $infos->country->label,
|
||||
'city' => $infos->city,
|
||||
'postcode' => $infos->postcode,
|
||||
'opt_in' => $infos->opt_in,
|
||||
|
|
@ -319,28 +327,29 @@ class CustomerController extends Controller
|
|||
]
|
||||
]
|
||||
);
|
||||
}
|
||||
}//end if
|
||||
|
||||
$login->last_connection = now();
|
||||
$customer->logins()->save($login);
|
||||
return new CustomerResource($customer, $brandId);
|
||||
}
|
||||
}//end if
|
||||
// "Mot de passe incorrect"
|
||||
return new StatusResource(['status' => 'KO', 'code' => 117, 'message' => 'Authentication failed.']);
|
||||
} else {
|
||||
// "Ce compte n'est pas enregistré pour cette marque"
|
||||
return new StatusResource(['status' => 'KO', 'code' => 118, 'message' => 'User not found.']);
|
||||
}
|
||||
}//end if
|
||||
} else {
|
||||
// "Ce compte n'existe pas dans la CRM"
|
||||
return new StatusResource(['status' => 'KO', 'code' => 119, 'message' => 'User not found.']);
|
||||
}
|
||||
}//end if
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the password of the user for the brand
|
||||
* @param Request $request
|
||||
* @param $id
|
||||
* @param mixed $id
|
||||
* @return StatusResource
|
||||
*/
|
||||
public function changePassword(Request $request, $id)
|
||||
{
|
||||
|
|
@ -362,7 +371,7 @@ class CustomerController extends Controller
|
|||
if ($customer) {
|
||||
$customerLogin = $customer->logins()->where('brand_id', $brandId)->first();
|
||||
if ($customerLogin) {
|
||||
// Check if same password
|
||||
// Check if same password.
|
||||
if (Hash::check($password, $customerLogin->password)) {
|
||||
return new StatusResource(['status' => 'KO', 'code' => 123, 'message' => 'Same password.']);
|
||||
}
|
||||
|
|
@ -383,12 +392,11 @@ class CustomerController extends Controller
|
|||
/**
|
||||
* Activate the account of the user
|
||||
* @param Request $request
|
||||
* @param $id
|
||||
* @param mixed $id
|
||||
* @return StatusResource
|
||||
*/
|
||||
public function activateAccount(
|
||||
Request $request,
|
||||
$id
|
||||
) {
|
||||
public function activateAccount(Request $request, $id)
|
||||
{
|
||||
$brandId = $request->brand_id;
|
||||
|
||||
$customer = Customer::find($id);
|
||||
|
|
@ -417,14 +425,13 @@ class CustomerController extends Controller
|
|||
/**
|
||||
* Delete the user account for the brand
|
||||
* @param Request $request
|
||||
* @param $id
|
||||
* @param mixed $id
|
||||
*/
|
||||
// phpcs:disable Squiz.PHP.NonExecutableCode.Unreachable
|
||||
// phpcs:disable Squiz.PHP.NonExecutableCode.Unreachable, Squiz.Commenting.FunctionComment.Missing
|
||||
public function delete(Request $request, $id)
|
||||
{
|
||||
die("Not implemented yet");
|
||||
// TODO : A voir pour les stats, obuton dans le bo qui permet de faire les stats sur les customer puis supprimer
|
||||
// TODO : Gestion avec mesBR
|
||||
// TODO : Gestion avec mesBR.
|
||||
$brandId = $request->brand_id;
|
||||
|
||||
$customer = Customer::find($id);
|
||||
|
|
@ -435,25 +442,27 @@ class CustomerController extends Controller
|
|||
if (count($customer->infos) === 0 && count($customer->logins) === 0) {
|
||||
$customer->optInHistory()->delete();
|
||||
$customer->delete();
|
||||
// Suppression du compte de la CRM terminée
|
||||
// Suppression du compte de la CRM terminée.
|
||||
return new StatusResource(['status' => 'OK', 'message' => 'Deletion complete.']);
|
||||
} else {
|
||||
// Suppression des informations du compte pour la marque terminée
|
||||
// Suppression des informations du compte pour la marque terminée.
|
||||
return new StatusResource(['status' => 'OK', 'message' => 'Deletion complete.']);
|
||||
}
|
||||
} else {
|
||||
return new StatusResource(['status' => 'KO', 'code' => 106, 'message' => 'User not found.']);
|
||||
}
|
||||
// phpcs:enable Squiz.PHP.NonExecutableCode.Unreachable, Squiz.Commenting.FunctionComment.Missing
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the status of the opt-in for the customer on this brand
|
||||
* @param Request $request
|
||||
* @param $id
|
||||
* @param mixed $id
|
||||
* @return StatusResource
|
||||
*/
|
||||
public function toggleOptin(Request $request, $id)
|
||||
{
|
||||
// TODO : Liaison des opt-in avec MailJet
|
||||
// TODO : Liaison des opt-in avec MailJet.
|
||||
$brandId = $request->brand_id;
|
||||
|
||||
$customer = Customer::find($id);
|
||||
|
|
@ -462,7 +471,7 @@ class CustomerController extends Controller
|
|||
if ($infos) {
|
||||
$infos->opt_in = $infos->opt_in === 0 ? 1 : 0;
|
||||
|
||||
// On enregistre l'historique du changement d'optin
|
||||
// On enregistre l'historique du changement d'optin.
|
||||
$optinHistory = new CustomerOptInHistory();
|
||||
$optinHistory->customer_id = $customer->id;
|
||||
$optinHistory->new_value = $infos->opt_in;
|
||||
|
|
@ -479,17 +488,18 @@ class CustomerController extends Controller
|
|||
} else {
|
||||
// "Cet utilisateur n'existe pas dans la CRM"
|
||||
return new StatusResource(['status' => 'KO', 'code' => 114, 'message' => 'User not found.']);
|
||||
}
|
||||
}//end if
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove opt-in for the customer on this brand
|
||||
* @param Request $request
|
||||
* @param $hashcode
|
||||
* @param mixed $hashcode
|
||||
* @return StatusResource
|
||||
*/
|
||||
public function removeOptin(Request $request, $hashcode)
|
||||
{
|
||||
// TODO : Liaison des opt-in avec MailJet
|
||||
// TODO : Liaison des opt-in avec MailJet.
|
||||
$brandId = $request->brand_id;
|
||||
|
||||
$infos = CustomerInfo::where([
|
||||
|
|
@ -500,7 +510,7 @@ class CustomerController extends Controller
|
|||
if ($infos) {
|
||||
$infos->opt_in = 0;
|
||||
|
||||
// On enregistre l'historique du changement d'optin
|
||||
// On enregistre l'historique du changement d'optin.
|
||||
$optinHistory = new CustomerOptInHistory();
|
||||
$optinHistory->customer_id = $infos->customer_id;
|
||||
$optinHistory->new_value = 0;
|
||||
|
|
@ -516,6 +526,11 @@ class CustomerController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return void
|
||||
*/
|
||||
// phpcs:ignore Squiz.PHP.NonExecutableCode.Unreachable, Squiz.Commenting.FunctionComment.Missing
|
||||
public function addSingleOptin(Request $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
|
|
@ -532,8 +547,7 @@ class CustomerController extends Controller
|
|||
$brandId = $request->brand_id;
|
||||
$email = filter_var($request->email, FILTER_SANITIZE_EMAIL);
|
||||
|
||||
// Check si le customer ou l'optin simple existe avant de l'ajouter en optin simple
|
||||
// phpcs:ignore PSR2.ControlStructures.ControlStructureSpacing.SpacingAfterOpenBrace
|
||||
// Check si le customer ou l'optin simple existe avant de l'ajouter en optin simple.
|
||||
if (
|
||||
!SingleOptin::where([['email', $email], ['brand_id', $brandId]])->exists() &&
|
||||
!CustomerInfo::join("customers", "customer_infos.customer_id", "=", "customers.id")->where([
|
||||
|
|
|
|||
|
|
@ -14,21 +14,22 @@ class AnswerController extends Controller
|
|||
/**
|
||||
* Store the customer answers
|
||||
*
|
||||
* @param \App\Http\Requests\Api\V2\StoreCustomerAnswers $request
|
||||
* @param \App\Models\Service $service
|
||||
* @param \App\Models\Customer $customer
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @param StoreCustomerAnswers $request
|
||||
* @param Service $service
|
||||
* @param Customer $customer
|
||||
* @return JsonResponse
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||
public function storeCustomerAnswers(
|
||||
StoreCustomerAnswers $request,
|
||||
Service $service,
|
||||
Customer $customer
|
||||
): JsonResponse {
|
||||
// * Make sure we are accessing a customer that belongs to our service.
|
||||
$this->authorize('customerBelongsToService', $customer);
|
||||
|
||||
$existingAnswerIds = $customer->getAnswers($service)->pluck('id');
|
||||
$answersID = Answer::whereIn('id', $request->answers)->pluck('id');
|
||||
$customer->answers()->sync($answersID->merge($existingAnswerIds));
|
||||
return \response()->json();
|
||||
return response()->json();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,40 +2,18 @@
|
|||
|
||||
namespace App\Http\Controllers\Api\V2;
|
||||
|
||||
use App\Enums\ApiError;
|
||||
use App\Exceptions\Api\V2\ApiErrorException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Lib\Helper;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class ApiController extends Controller
|
||||
{
|
||||
public function ping()
|
||||
/**
|
||||
* Just returns HTTP 200 response
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function ping(): JsonResponse
|
||||
{
|
||||
return response()->json();
|
||||
}
|
||||
|
||||
public function authenticate(Request $request)
|
||||
{
|
||||
try {
|
||||
$bearerToken = Helper::getBearerTokenFromRequest($request);
|
||||
if (!$bearerToken) {
|
||||
throw new ApiErrorException(ApiError::missingParameter());
|
||||
}
|
||||
/** @var \App\Models\Service */
|
||||
$service = Service::getFromBearerToken($bearerToken);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
throw new ApiErrorException(ApiError::apiAuthFailed());
|
||||
}
|
||||
if (!Hash::check($request->bearer_pwd, $service->bearer_pwd)) {
|
||||
throw new ApiErrorException(ApiError::apiAuthFailed());
|
||||
}
|
||||
|
||||
$apiToken = $service->generateApiToken();
|
||||
$service->saveOrFail();
|
||||
return response()->json(['api_token' => $apiToken]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,45 +4,63 @@ namespace App\Http\Controllers\Api\V2;
|
|||
|
||||
use App\Enums\ArcadiaMailType;
|
||||
use App\Enums\ArcadiaOrderType;
|
||||
use App\Exceptions\Api\V2\CrmException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V2\BaseApiRequest;
|
||||
use App\Http\Requests\Api\V2\SendArchadiaDefaultMailRequest;
|
||||
use App\Http\Requests\Api\V2\SendArchadiaGameMailRequest;
|
||||
use App\Mail\CourrierArcadiaError;
|
||||
use App\Models\Service;
|
||||
use DOMDocument;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* TODO: séparer ça quand coté arcadia ils auront branché le cerveau
|
||||
* Ici c'est du sale à cause d'arcadia
|
||||
* ! Ici c'est du sale à cause d'arcadia
|
||||
* Ils ont fait le choix de rajouter une fonctionnalité
|
||||
* sur un methode existant en rajoutant des conditions
|
||||
* sales
|
||||
* sur une methode existante en rajoutant des conditions
|
||||
* sales. Ils ont donc une seule méthode pour envoyer plusieurs
|
||||
* types de réclas avec des champs différents...
|
||||
*
|
||||
* Donc un séparation est faite sur les endpoints CRM et dans le client
|
||||
* pour éviter une confusion de noes utilisateurs
|
||||
* Donc une séparation est faite sur les endpoints CRM et dans le client
|
||||
* pour éviter une confusion de nos utilisateurs
|
||||
*/
|
||||
class ArcadiaController extends Controller
|
||||
{
|
||||
public function outgoingMailDefault(SendArchadiaDefaultMailRequest $request, Service $service)
|
||||
/**
|
||||
* Send arcadia default mail (reclamations)
|
||||
*
|
||||
* @param SendArchadiaDefaultMailRequest $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function outgoingMailDefault(SendArchadiaDefaultMailRequest $request): JsonResponse
|
||||
{
|
||||
return $this->handleArcadiaMail($request, ArcadiaMailType::default(), $service);
|
||||
return $this->handleArcadiaMail($request, ArcadiaMailType::default());
|
||||
}
|
||||
|
||||
public function outgoingMailGame(SendArchadiaGameMailRequest $request, Service $service)
|
||||
/**
|
||||
* Send arcadia game mail (games)
|
||||
*
|
||||
* @param SendArchadiaGameMailRequest $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function outgoingMailGame(SendArchadiaGameMailRequest $request): JsonResponse
|
||||
{
|
||||
return $this->handleArcadiaMail($request, ArcadiaMailType::game(), $service);
|
||||
return $this->handleArcadiaMail($request, ArcadiaMailType::game());
|
||||
}
|
||||
|
||||
private function handleArcadiaMail(
|
||||
BaseApiRequest $request,
|
||||
ArcadiaMailType $type
|
||||
) {
|
||||
/**
|
||||
* Form xml message and save it in the apropriate storage location
|
||||
* to be picked up by the arcadia job
|
||||
*
|
||||
* @param BaseApiRequest $request
|
||||
* @param ArcadiaMailType $type
|
||||
* @return JsonResponse
|
||||
* @throws \Exception If an error occurs while generating and saving the xml file.
|
||||
*/
|
||||
private function handleArcadiaMail(BaseApiRequest $request, ArcadiaMailType $type): JsonResponse
|
||||
{
|
||||
$request = $request->merge([
|
||||
'order_num' => str_pad(\intval($request->order_num), 9, '0', STR_PAD_LEFT)
|
||||
]);
|
||||
|
|
@ -61,7 +79,7 @@ class ArcadiaController extends Controller
|
|||
$orderType = ArcadiaOrderType::make(intval($request->state_order));
|
||||
$xml = $this->makeXml($request, $type, $orderType, $customerInfo);
|
||||
|
||||
// Format xml
|
||||
// * Format xml.
|
||||
$doc = new DOMDocument('1.0', 'utf-8');
|
||||
$doc->xmlStandalone = true;
|
||||
$doc->preserveWhiteSpace = false;
|
||||
|
|
@ -70,13 +88,14 @@ class ArcadiaController extends Controller
|
|||
$xml = $doc->saveXML();
|
||||
|
||||
Storage::disk('arcadia')->put(Str::slug(sprintf(
|
||||
'v2_order_%s_%s_%s%s.xml',
|
||||
'v2_order_%s_%s_%s%s',
|
||||
$request->order_num,
|
||||
$type->label,
|
||||
date('Ymd_His'),
|
||||
\microtime(true)
|
||||
)), $xml);
|
||||
)) . '.xml', $xml);
|
||||
return response()->json();
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (\Exception $e) {
|
||||
Log::critical("ARCADIA XML CREATION FAILED FOR $request->order_num : " . $e->getMessage());
|
||||
if (!config('app.debug')) {
|
||||
|
|
@ -84,23 +103,22 @@ class ArcadiaController extends Controller
|
|||
Mail::to(config('mail.MAIL_ADMIN'))->send(new CourrierArcadiaError(
|
||||
"ARCADIA XML CREATION FAILED FOR $request->order_num",
|
||||
"Une erreur est survenue lors de la création du fichier XML : " . $e->getMessage(),
|
||||
$xml
|
||||
$xml ?: ''
|
||||
));
|
||||
})->afterResponse();
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
throw new CrmException("Failed to create the arcadia xml file for $request->order_num", 0, $e);
|
||||
}
|
||||
throw $e;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}//end try
|
||||
}
|
||||
|
||||
/**
|
||||
* render Arcadia xml for order
|
||||
*
|
||||
* @param SendArchadiaMailRequest $request
|
||||
* @param ArcadiaMailType $type
|
||||
* @param BaseApiRequest $request
|
||||
* @param ArcadiaMailType $type
|
||||
* @param ArcadiaOrderType $orderType
|
||||
* @param array $customerInfo
|
||||
* @param array $customerInfo
|
||||
* @return string
|
||||
*/
|
||||
private function makeXml(
|
||||
|
|
@ -109,15 +127,13 @@ class ArcadiaController extends Controller
|
|||
ArcadiaOrderType $orderType,
|
||||
array $customerInfo
|
||||
): string {
|
||||
return view(
|
||||
$type->equals(ArcadiaMailType::default()) ?
|
||||
'mail.arcadiaOrders.default' : 'mail.arcadiaOrders.game',
|
||||
[
|
||||
'request' => $request,
|
||||
'type' => $type,
|
||||
'orderType' => $orderType,
|
||||
'customerInfo' => $customerInfo
|
||||
]
|
||||
)->render();
|
||||
$arcadiaMailType = $type->equals(ArcadiaMailType::default()) ?
|
||||
'mail.arcadiaOrders.default' : 'mail.arcadiaOrders.game';
|
||||
return view($arcadiaMailType, [
|
||||
'request' => $request,
|
||||
'type' => $type,
|
||||
'orderType' => $orderType,
|
||||
'customerInfo' => $customerInfo
|
||||
])->render();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,196 +17,236 @@ use App\Models\SingleOptin;
|
|||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CustomerController extends Controller
|
||||
{
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
* Creates a new customer for the specified service.
|
||||
*
|
||||
* @param \App\Request\StoreCustomerRequest $request
|
||||
* @param \App\Models\Service $service
|
||||
* @return \Illuminate\Http\Resources\Json\JsonResource
|
||||
* @param StoreCustomerRequest $request
|
||||
* @param Service $service
|
||||
* @return JsonResource
|
||||
* @throws ApiErrorException With ApiError::customerExists if the customer with this email already exists.
|
||||
* @throws CrmException If saving the customer to DB failed.
|
||||
*/
|
||||
public function store(StoreCustomerRequest $request, Service $service): JsonResource
|
||||
{
|
||||
$data = (object)$request->validated();
|
||||
|
||||
/** @var \App\Models\Customer */
|
||||
$customer = Customer::where('email', $data->email)->firstOrCreate([
|
||||
'email' => $data->email
|
||||
]);
|
||||
$customer->saveOrFail();
|
||||
$customer->setOptIn($service->brand, $data->opt_in);
|
||||
|
||||
// Delete Single optin if exists
|
||||
if (
|
||||
$singleOptin = SingleOptin::where('email', $data->email)
|
||||
->where('brand_id', $service->brand->id)->first()
|
||||
) {
|
||||
$singleOptin->delete();
|
||||
}
|
||||
DB::transaction(function () use (&$customer, $data, $request, $service) {
|
||||
|
||||
/** @var \App\Models\CustomerInfo */
|
||||
$customerInfo = $customer->info($service->brand, Customer::FIRST_OR_NEW);
|
||||
if ($customerInfo->exists) {
|
||||
throw new ApiErrorException(ApiError::customerExists());
|
||||
}
|
||||
$customerInfo->fill($request->validated());
|
||||
$customerInfo->brand()->associate($service->brand);
|
||||
$customerInfo->customer()->associate($customer);
|
||||
$customerInfo->saveOrFail();
|
||||
$saved = $customer->save();
|
||||
$customer->setOptIn($service->brand, $data->opt_in);
|
||||
|
||||
/** @var \App\Models\CustomerLogin */
|
||||
$customerLogin = $customer->login($service->brand, Customer::FIRST_OR_NEW);
|
||||
$customerLogin->fill($request->validated());
|
||||
$customerLogin->brand()->associate($service->brand);
|
||||
$customerLogin->customer()->associate($customer);
|
||||
$customerLogin->saveOrFail();
|
||||
// * Delete Single optin if exists.
|
||||
/** @var \App\Models\SingleOptin|null $singleOptin */
|
||||
$singleOptin = SingleOptin::where('email', $data->email)
|
||||
->where('brand_id', $service->brand->id)->first();
|
||||
if ($singleOptin) {
|
||||
$saved = $singleOptin->delete() && $saved;
|
||||
}
|
||||
|
||||
/** @var \App\Models\CustomerInfo */
|
||||
$customerInfo = $customer->info($service->brand, Customer::FIRST_OR_NEW);
|
||||
if ($customerInfo->exists) {
|
||||
throw new ApiErrorException(ApiError::customerExists());
|
||||
}
|
||||
$customerInfo->fill($request->validated());
|
||||
$customerInfo->brand()->associate($service->brand);
|
||||
$customerInfo->customer()->associate($customer);
|
||||
$saved = $customerInfo->save() && $saved;
|
||||
|
||||
/** @var \App\Models\CustomerLogin */
|
||||
$customerLogin = $customer->login($service->brand, Customer::FIRST_OR_NEW);
|
||||
$customerLogin->fill($request->validated());
|
||||
$customerLogin->brand()->associate($service->brand);
|
||||
$customerLogin->customer()->associate($customer);
|
||||
$saved = $customerLogin->save() && $saved;
|
||||
|
||||
if (!$saved) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new CrmException('Api failed to store customer.');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
});
|
||||
|
||||
return new CustomerResource($service->brand, $customer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
* Renders the specific customer infos
|
||||
*
|
||||
* @param \App\Models\Service $service
|
||||
* @param \App\Models\Customer $customer
|
||||
* @return \Illuminate\Http\Resources\Json\JsonResource
|
||||
* @param Service $service
|
||||
* @param Customer $customer
|
||||
* @return JsonResource
|
||||
*/
|
||||
public function show(Customer $customer, Service $service): JsonResource
|
||||
public function show(Service $service, Customer $customer): JsonResource
|
||||
{
|
||||
// * Make sure we are accessing a customer that belongs to our service.
|
||||
$this->authorize('customerBelongsToService', $customer);
|
||||
return new CustomerResource($service->brand, $customer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \App\Request\UpdateCustomerRequest $request
|
||||
* @param \App\Models\Service $service
|
||||
* @param \App\Models\Customer $customer
|
||||
* @return \Illuminate\Http\Resources\Json\JsonResource
|
||||
*/
|
||||
public function update(UpdateCustomerRequest $request, Customer $customer, Service $service): JsonResource
|
||||
{
|
||||
$this->authorize('customerBelongsToService', $customer);
|
||||
// To correct with create
|
||||
$customer->fill($request->all());
|
||||
if ($customer->saveOrFail() === false) {
|
||||
throw new CrmException(sprintf('Could not update customer %d', $customer));
|
||||
}
|
||||
if ($request->has('opt_in')) {
|
||||
$customer->setOptIn($service->brand, $request->opt_in);
|
||||
}
|
||||
|
||||
/** @var \App\Models\CustomerInfo */
|
||||
$customerInfo = $customer->info($service->brand, Customer::FIRST_OR_FAIL);
|
||||
$customerInfo->fill($request->validated());
|
||||
$customerInfo->saveOrFail();
|
||||
|
||||
/** @var \App\Models\CustomerLogin */
|
||||
$customerLogin = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
|
||||
$customerLogin->fill($request->validated());
|
||||
$customerLogin->saveOrFail();
|
||||
|
||||
return new CustomerResource($service->brand, $customer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
* Updates information for a specific customer
|
||||
*
|
||||
* @param \App\Models\Service $service
|
||||
* @param \App\Models\Customer $customer
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @param UpdateCustomerRequest $request
|
||||
* @param Service $service
|
||||
* @param Customer $customer
|
||||
* @return JsonResource
|
||||
* @throws CrmException If saving the customer to DB failed.
|
||||
*/
|
||||
public function destroy(Customer $customer, Service $service): JsonResponse
|
||||
public function update(UpdateCustomerRequest $request, Service $service, Customer $customer): JsonResource
|
||||
{
|
||||
// * Make sure we are accessing a customer that belongs to our service.
|
||||
$this->authorize('customerBelongsToService', $customer);
|
||||
// TODO : A voir pour les stats, obuton dans le bo qui permet de faire les stats sur les customer puis supprimer
|
||||
// TODO : Gestion avec mesBR
|
||||
|
||||
DB::transaction(function () use ($request, $customer, $service) {
|
||||
// To correct with create.
|
||||
$customer->fill($request->validated());
|
||||
$saved = $customer->save();
|
||||
if ($request->has('opt_in')) {
|
||||
$customer->setOptIn($service->brand, $request->opt_in);
|
||||
}
|
||||
|
||||
/** @var \App\Models\CustomerInfo */
|
||||
$customerInfo = $customer->info($service->brand, Customer::FIRST_OR_FAIL);
|
||||
$customerInfo->fill($request->validated());
|
||||
$saved = $customerInfo->save() && $saved;
|
||||
|
||||
/** @var \App\Models\CustomerLogin */
|
||||
$customerLogin = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
|
||||
$customerLogin->fill($request->validated());
|
||||
$saved = $customerLogin->save() && $saved;
|
||||
|
||||
if (!$saved) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new CrmException('Api failed to update customer.');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
});
|
||||
|
||||
return new CustomerResource($service->brand, $customer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes customer information for a specific service
|
||||
*
|
||||
* @param Service $service
|
||||
* @param Customer $customer
|
||||
* @return JsonResponse
|
||||
* @throws CrmException If removing the customer off the DB failed.
|
||||
*/
|
||||
public function destroy(Service $service, Customer $customer): JsonResponse
|
||||
{
|
||||
// * Make sure we are accessing a customer that belongs to our service.
|
||||
$this->authorize('customerBelongsToService', $customer);
|
||||
|
||||
// TODO: Gestion avec mesBR.
|
||||
/** @var \App\Models\CustomerInfo */
|
||||
$customerInfo = $customer->info($service->brand, Customer::FIRST_OR_FAIL);
|
||||
/** @var \App\Models\CustomerLogin */
|
||||
$customerLogin = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
|
||||
|
||||
if (
|
||||
!$customerInfo->delete() ||
|
||||
!$customerLogin->delete() ||
|
||||
DB::transaction(function () use ($service, $customer, $customerLogin, $customerInfo) {
|
||||
if (
|
||||
$customerInfo->delete() === false ||
|
||||
$customerLogin->delete() === false
|
||||
) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new CrmException('Api failed to delete customer.');
|
||||
// @codeCoverageIgnoreEnd
|
||||
};
|
||||
|
||||
$customer->optInHistory()->whereHas('brand', function ($q) use ($service) {
|
||||
$q->where('id', $service->brand_id);
|
||||
})->delete() === false
|
||||
) {
|
||||
throw new CrmException(sprintf(
|
||||
'Could not delete customer %d for brand %d',
|
||||
$customer->id,
|
||||
$service->brand->id
|
||||
));
|
||||
};
|
||||
})->delete();
|
||||
|
||||
$logins = $customer->logins()->count();
|
||||
$infos = $customer->infos()->count();
|
||||
$history = $customer->optInHistory()->count();
|
||||
// * Force loading real relation counts.
|
||||
$customer->unsetRelation('logins');
|
||||
$customer->unsetRelation('infos');
|
||||
$logins = $customer->logins()->count();
|
||||
$infos = $customer->infos()->count();
|
||||
|
||||
// If customer is not associated with any brand then delete
|
||||
if (!$logins and !$infos and !$history) {
|
||||
if ($customer->delete() === false) {
|
||||
throw new CrmException(sprintf(
|
||||
'Could not delete customer %d',
|
||||
$customer->id
|
||||
));
|
||||
// If customer is not associated with any brand then delete.
|
||||
if (!$logins and !$infos) {
|
||||
if ($customer->delete() === false) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new CrmException('Api failed to update customer.');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return response()->json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the user
|
||||
* @param \App\Models\Service $service
|
||||
* @param \App\Request\AuthenticateRequest $request
|
||||
* @return \Illuminate\Http\Resources\Json\JsonResource
|
||||
* @param AuthenticateRequest $request
|
||||
* @param Service $service
|
||||
* @return JsonResource
|
||||
* @throws ApiErrorException With ApiError::authFailed() in case of wrong credentials.
|
||||
* @throws ApiErrorException With ApiError::customerNotActivated() if customer account is not activated.
|
||||
*/
|
||||
public function authenticate(AuthenticateRequest $request, Service $service): JsonResource
|
||||
{
|
||||
try {
|
||||
/** @var \App\Models\Customer */
|
||||
$customer = Customer::where('email', $request->email)->firstOrFail();
|
||||
$customer->loadBrandData($service->brand, true);
|
||||
$customerLogin = $customer->logins->first();
|
||||
|
||||
/** @var \App\Models\CustomerLogin $customerLogin */
|
||||
$customerLogin = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
|
||||
if (!$customerLogin->passwordMatch($request->password)) {
|
||||
throw new ApiErrorException(ApiError::authFailed());
|
||||
}
|
||||
|
||||
if (!$customerLogin->status) {
|
||||
throw new ApiErrorException(ApiError::customerNotActivated());
|
||||
}
|
||||
|
||||
UpdateCustomerLastConnection::dispatch($customer, $customerLogin, now())
|
||||
->onConnection('database')->afterResponse();
|
||||
|
||||
return new CustomerResource($service->brand, $customer);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
throw new ApiErrorException(ApiError::authFailed());
|
||||
}
|
||||
|
||||
if (!$customerLogin->passwordMatch($request->password)) {
|
||||
throw new ApiErrorException(ApiError::authFailed());
|
||||
}
|
||||
|
||||
if (!$customerLogin->status) {
|
||||
throw new ApiErrorException(ApiError::customerNotActivated());
|
||||
}
|
||||
|
||||
UpdateCustomerLastConnection::dispatch($customer, $customerLogin, now())
|
||||
->onConnection('database');
|
||||
|
||||
return new CustomerResource($service->brand, $customer);
|
||||
}//end try
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate the account of the user
|
||||
* @param Customer $customer
|
||||
* @param Service $service
|
||||
* @param Service $service
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws ApiErrorException With ApiError::customerAlreadyActive if customer account is already active.
|
||||
* @throws CrmException If saving the customer to DB failed.
|
||||
*/
|
||||
public function activateAccount(Customer $customer, Service $service)
|
||||
{
|
||||
// * Make sure we are accessing a customer that belongs to our service.
|
||||
$this->authorize('customerBelongsToService', $customer);
|
||||
|
||||
/** @var \App\Models\CustomerLogin $login */
|
||||
$login = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
|
||||
|
||||
if (!$login->status) {
|
||||
$login->status = true;
|
||||
$login->save();
|
||||
return response()->json();
|
||||
} else {
|
||||
if ($login->status) {
|
||||
throw new ApiErrorException(ApiError::customerAlreadyActive());
|
||||
}
|
||||
|
||||
$login->status = true;
|
||||
if (!$login->save()) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new CrmException('Failed to save login');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
return response()->json();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V2;
|
|||
|
||||
use App\Enums\ApiError;
|
||||
use App\Exceptions\Api\V2\ApiErrorException;
|
||||
use App\Exceptions\Api\V2\CrmException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V2\SingleOptinRequest;
|
||||
use App\Models\CustomerInfo;
|
||||
|
|
@ -13,11 +14,13 @@ use App\Models\SingleOptin;
|
|||
class OptinController extends Controller
|
||||
{
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
* Creates a new Single optin or destroys it if exists
|
||||
*
|
||||
* @param \App\Request\SingleOptinRequest $request
|
||||
* @param \App\Models\Service $service
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @param SingleOptinRequest $request
|
||||
* @param Service $service
|
||||
* @return JsonResponse
|
||||
* @throws ApiErrorException With ApiError::singleOptinCustomerExists if this email already has a full account.
|
||||
* @throws CrmException If the single optin could not be added or removed.
|
||||
*/
|
||||
public function optin(SingleOptinRequest $request, Service $service)
|
||||
{
|
||||
|
|
@ -26,15 +29,25 @@ class OptinController extends Controller
|
|||
}
|
||||
|
||||
/** @var \App\Models\SingleOptin */
|
||||
$singleOptin = SingleOptin::where('email', $request->email)->firstOrNew();
|
||||
$singleOptin = SingleOptin::where('email', $request->email)
|
||||
->whereHas('brand', function ($q) use ($service) {
|
||||
$q->whereId($service->brand->id);
|
||||
})->firstOrNew();
|
||||
|
||||
if ($request->opt_in) {
|
||||
$singleOptin->fill($request->validated());
|
||||
$singleOptin->brand()->associate($service->brand);
|
||||
$singleOptin->saveOrFail();
|
||||
$success = $singleOptin->save();
|
||||
} else {
|
||||
$singleOptin->delete();
|
||||
$success = $singleOptin->delete() !== false;
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new CrmException('Api failed to update single optin.');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
return response()->json();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,42 +14,44 @@ use Illuminate\Http\Resources\Json\ResourceCollection;
|
|||
class QuestionController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display all specified resources.
|
||||
* Returns all service questions.
|
||||
*
|
||||
* @param \App\Models\Service $service
|
||||
* @return \Illuminate\Http\Resources\Json\JsonResource
|
||||
* @param Service $service
|
||||
* @return JsonResource
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
public function index(Service $service): ResourceCollection
|
||||
{
|
||||
return new QuestionsResource($service->brand->questions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display all specified resources.
|
||||
* Returns all service questions with user answers for all of them.
|
||||
*
|
||||
* @param \App\Models\Service $service
|
||||
* @return \Illuminate\Http\Resources\Json\JsonResource
|
||||
* @param Service $service
|
||||
* @param Customer $customer
|
||||
* @return JsonResource
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
public function indexWithResponse(Service $service, Customer $customer): ResourceCollection
|
||||
{
|
||||
// * Make sure we are accessing a customer that belongs to our service.
|
||||
$this->authorize('customerBelongsToService', $customer);
|
||||
|
||||
$customerAnswers = $customer->getAnswers($service);
|
||||
return new QuestionsResource($service->brand->questions, $customerAnswers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display all specified resource by customer.
|
||||
* Returns all service questions that was not answered by a user.
|
||||
*
|
||||
* @param \App\Models\Service $service
|
||||
* @param \App\Models\Customer $customer
|
||||
* @return \Illuminate\Http\Resources\Json\JsonResource
|
||||
* @param Service $service
|
||||
* @param Customer $customer
|
||||
* @return JsonResource
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
public function unAnsweredBy(Service $service, Customer $customer): JsonResource
|
||||
{
|
||||
// * Make sure we are accessing a customer that belongs to our service.
|
||||
$this->authorize('customerBelongsToService', $customer);
|
||||
|
||||
$questions = Question::whereHas('brand', function ($q) use ($service) {
|
||||
$q->where('id', $service->brand->id);
|
||||
})->whereDoesntHave('answers', function ($q) use ($customer) {
|
||||
|
|
|
|||
|
|
@ -17,8 +17,14 @@ use Illuminate\Support\Collection;
|
|||
|
||||
class StatsController extends Controller
|
||||
{
|
||||
|
||||
public function brandStats(Service $service, GetBrandStatsRequest $request): ResourceCollection
|
||||
/**
|
||||
* Get specific brand statistics
|
||||
*
|
||||
* @param GetBrandStatsRequest $request
|
||||
* @param Service $service
|
||||
* @return ResourceCollection
|
||||
*/
|
||||
public function brandStats(GetBrandStatsRequest $request, Service $service): ResourceCollection
|
||||
{
|
||||
$stats = BrandStat::whereDate('created_at', '>=', $request->start)
|
||||
->whereDate('created_at', '<=', $request->end)
|
||||
|
|
@ -26,10 +32,16 @@ class StatsController extends Controller
|
|||
return new BrandStatsResource($stats);
|
||||
}
|
||||
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||
public function questionStats(Service $service, GetQuestionStatsRequest $request)
|
||||
/**
|
||||
* Get statistics for a specific question
|
||||
*
|
||||
* @param GetQuestionStatsRequest $request
|
||||
* @param Question $question
|
||||
* @return QuestionStatsResource
|
||||
*/
|
||||
public function questionStats(GetQuestionStatsRequest $request, Question $question): QuestionStatsResource
|
||||
{
|
||||
$question = Question::findOrFail($request->question);
|
||||
// * Make sure we are accessing a customer that belongs to our service.
|
||||
$this->authorize('questionBelongsToService', $question);
|
||||
|
||||
$answers = $question->answers;
|
||||
|
|
@ -46,13 +58,13 @@ class StatsController extends Controller
|
|||
* Total réponses par dates
|
||||
*
|
||||
* @param Collection $answers
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @return Collection
|
||||
*/
|
||||
private function answersByDays(Collection $answers, Carbon $start, Carbon $end): Collection
|
||||
{
|
||||
// Totalize by date all answers
|
||||
// * Totalize by date all answers.
|
||||
$answersByDays = CustomerAnswer::whereDate('created_at', '>=', $start)
|
||||
->whereDate('created_at', '<=', $end)
|
||||
->whereIn('answer_id', $answers->pluck('id'))
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ use App\Models\Brand;
|
|||
use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
|
||||
use Kwaadpepper\CrudPolicies\Traits\CrudController;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class BrandController extends Controller
|
||||
{
|
||||
use CrudController {
|
||||
|
|
@ -16,8 +19,19 @@ class BrandController extends Controller
|
|||
/** @var Brand */
|
||||
public static $modelClass = Brand::class;
|
||||
|
||||
/**
|
||||
* The Bo app layout
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $layout = 'layouts.app';
|
||||
|
||||
/**
|
||||
* Overload crud-policies index
|
||||
*
|
||||
* @param CrudIndexRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(CrudIndexRequest $request)
|
||||
{
|
||||
/** @var \Illuminate\View\View */
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ use Illuminate\Foundation\Bus\DispatchesJobs;
|
|||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ use App\Models\Customer;
|
|||
use App\Models\CustomerInfo;
|
||||
use App\Models\CustomerLogin;
|
||||
use App\Models\SingleOptin;
|
||||
use Exception;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\View;
|
||||
|
|
@ -20,8 +20,14 @@ use Illuminate\Support\Str;
|
|||
use League\Csv\Writer;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class CustomerController extends Controller
|
||||
{
|
||||
/**
|
||||
* View share crud policies modelClass
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware(function ($request, $next) {
|
||||
|
|
@ -29,6 +35,13 @@ class CustomerController extends Controller
|
|||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* View all customers
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$modelTable = (new Customer())->getTable();
|
||||
|
|
@ -36,7 +49,7 @@ class CustomerController extends Controller
|
|||
$brands = Brand::select('id', 'name')->get();
|
||||
$columns = ['customer_infos.lastname', 'customer_infos.firstname'];
|
||||
|
||||
// Jointure pour pouvoir trier les colonnes
|
||||
// * Jointure pour pouvoir trier les colonnes.
|
||||
/** @var \Illuminate\Database\Eloquent\Builder */
|
||||
$query = CustomerInfo::join('customer_logins', function (JoinClause $join) {
|
||||
$join->on('customer_logins.brand_id', '=', 'customer_infos.brand_id')
|
||||
|
|
@ -54,7 +67,7 @@ class CustomerController extends Controller
|
|||
$query = $query->where('customer_infos.brand_id', $brand);
|
||||
}
|
||||
|
||||
// Search on crud Label column
|
||||
// * Search on crud Label column.
|
||||
if ($request->search or Session::has("crud.$modelTable.search")) {
|
||||
$search = $request->search ?? Session::get("crud.$modelTable.search");
|
||||
Session::put("crud.$modelTable.search", $search);
|
||||
|
|
@ -67,7 +80,7 @@ class CustomerController extends Controller
|
|||
});
|
||||
}
|
||||
|
||||
// Sort columns
|
||||
// * Sort columns.
|
||||
if ($request->sort_col) {
|
||||
Session::put("crud.$modelTable.sort_col", $request->sort_col);
|
||||
}
|
||||
|
|
@ -112,6 +125,13 @@ class CustomerController extends Controller
|
|||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a specific custmer
|
||||
*
|
||||
* @param Customer $customer
|
||||
* @param Brand $brand
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show(Customer $customer, Brand $brand)
|
||||
{
|
||||
$customerInfos = CustomerInfo::where('customer_id', $customer->id)
|
||||
|
|
@ -125,21 +145,63 @@ class CustomerController extends Controller
|
|||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a specific customer info
|
||||
*
|
||||
* @param Customer $customer
|
||||
* @param Brand $brand
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function delete(Customer $customer, Brand $brand)
|
||||
{
|
||||
$nbBrand = $customer->infos()->count();
|
||||
try {
|
||||
// TODO: Gestion avec mesBR.
|
||||
/** @var \App\Models\CustomerInfo */
|
||||
$customerInfo = $customer->info($brand, Customer::FIRST_OR_FAIL);
|
||||
/** @var \App\Models\CustomerLogin */
|
||||
$customerLogin = $customer->login($brand, Customer::FIRST_OR_FAIL);
|
||||
|
||||
$customer->infos()->where('brand_id', $brand->id)->delete();
|
||||
$customer->logins()->where('brand_id', $brand->id)->delete();
|
||||
$customer->optInHistory()->where('brand_id', $brand->id)->delete();
|
||||
DB::transaction(function () use (
|
||||
$brand,
|
||||
$customer,
|
||||
$customerLogin,
|
||||
$customerInfo
|
||||
) {
|
||||
if (
|
||||
$customerInfo->delete() === false ||
|
||||
$customerLogin->delete() === false
|
||||
) {
|
||||
throw new \ErrorException('Api failed to delete customer.');
|
||||
};
|
||||
|
||||
if ($nbBrand === 1) {
|
||||
$customer->delete();
|
||||
}
|
||||
$customer->optInHistory()->whereHas('brand', function ($q) use ($brand) {
|
||||
$q->where('id', $brand->id);
|
||||
})->delete();
|
||||
|
||||
return redirect()->route('customers.index')->with('success', "Compte supprimé.");
|
||||
$logins = $customer->logins()->count();
|
||||
$infos = $customer->infos()->count();
|
||||
$history = $customer->optInHistory()->count();
|
||||
|
||||
// If customer is not associated with any brand then delete.
|
||||
if (!$logins and !$infos and !$history) {
|
||||
if ($customer->delete() === false) {
|
||||
throw new \ErrorException('Api failed to update customer.');
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (\ErrorException $e) {
|
||||
return redirect()->route('customers.index')
|
||||
->with('error', trans('Le compte n\'as pas pu être supprimé.'));
|
||||
} //end try
|
||||
|
||||
return redirect()->route('customers.index')->with('warning', trans('Compte supprimé.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show export the customers form
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function export()
|
||||
{
|
||||
$brands = Brand::all();
|
||||
|
|
@ -148,6 +210,13 @@ class CustomerController extends Controller
|
|||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports customers following the requested export type
|
||||
*
|
||||
* @param CustomerExport $request
|
||||
* @return \Symfony\Component\HttpFoundation\StreamedResponse
|
||||
* @throws \Exception If the request export type in not handled.
|
||||
*/
|
||||
public function exportSubmit(CustomerExport $request)
|
||||
{
|
||||
$pagination = 2000;
|
||||
|
|
@ -160,7 +229,7 @@ class CustomerController extends Controller
|
|||
|
||||
$exportType = CustomerExportType::make(\intval($request->export_type));
|
||||
|
||||
//We send the CSV using Symfony StreamedResponse
|
||||
// * We send the CSV using Symfony StreamedResponse.
|
||||
$response = new StreamedResponse();
|
||||
$response->headers->set('Content-Encoding', 'none');
|
||||
$response->headers->set('Content-Type', 'text/csv; charset=UTF-8');
|
||||
|
|
@ -177,7 +246,7 @@ class CustomerController extends Controller
|
|||
switch ($exportType) {
|
||||
case CustomerExportType::both():
|
||||
$continue = true;
|
||||
// continue
|
||||
// ! Continue
|
||||
case CustomerExportType::customer():
|
||||
$page = 1;
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $req */
|
||||
|
|
@ -207,7 +276,7 @@ class CustomerController extends Controller
|
|||
if (!isset($continue)) {
|
||||
break;
|
||||
}
|
||||
// continue
|
||||
// ! Continue
|
||||
case CustomerExportType::singleOptin():
|
||||
SingleOptin::where('brand_id', $request->brand)->get()
|
||||
->map(function (SingleOptin $singleOptin) use ($w) {
|
||||
|
|
@ -220,13 +289,18 @@ class CustomerController extends Controller
|
|||
});
|
||||
break;
|
||||
default:
|
||||
throw new Exception(sprintf('Unhandled CustomerExportType %s', $exportType->label));
|
||||
}
|
||||
throw new \Exception(sprintf('Unhandled CustomerExportType %s', $exportType->label));
|
||||
} //end switch
|
||||
|
||||
/*
|
||||
We create a callable to output the CSV in chunk
|
||||
with Symfony StreamResponse you can flush the body content if necessary
|
||||
see Symfony documentation for more information
|
||||
*/
|
||||
|
||||
// * The flush value should depend on your CSV size.
|
||||
$flush_threshold = 1000;
|
||||
|
||||
//we create a callable to output the CSV in chunk
|
||||
//with Symfony StreamResponse you can flush the body content if necessary
|
||||
//see Symfony documentation for more information
|
||||
$flush_threshold = 1000; //the flush value should depend on your CSV size.
|
||||
$response->setCallback(function () use ($w, $flush_threshold) {
|
||||
foreach ($w->chunk(1024) as $offset => $chunk) {
|
||||
echo $chunk;
|
||||
|
|
|
|||
|
|
@ -5,16 +5,29 @@ namespace App\Http\Controllers;
|
|||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class LoginController extends Controller
|
||||
{
|
||||
/**
|
||||
* Shows the login view
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('login');
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function authenticate(Request $request)
|
||||
{
|
||||
// phpcs:ignore PSR2.ControlStructures.ControlStructureSpacing.SpacingAfterOpenBrace
|
||||
if (
|
||||
Auth::attempt(
|
||||
["email" => $request->email, "password" => $request->password],
|
||||
|
|
@ -28,6 +41,11 @@ class LoginController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function logout()
|
||||
{
|
||||
Auth::logout();
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ use Kwaadpepper\CrudPolicies\Traits\CrudController;
|
|||
use League\Csv\Writer;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class QuestionController extends Controller
|
||||
{
|
||||
use CrudController {
|
||||
|
|
@ -24,8 +27,33 @@ class QuestionController extends Controller
|
|||
/** @var Question */
|
||||
public static $modelClass = Question::class;
|
||||
|
||||
/**
|
||||
* The Bo app layout
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $layout = 'layouts.app';
|
||||
|
||||
/**
|
||||
* Event index crud-policies
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
public function indexQuery(Builder $query): Builder
|
||||
{
|
||||
if (request()->brand) {
|
||||
$query = $query->where('brand_id', '=', request()->brand);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload crud-policies index
|
||||
*
|
||||
* @param CrudIndexRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(CrudIndexRequest $request)
|
||||
{
|
||||
/** @var \Illuminate\View\View */
|
||||
|
|
@ -36,20 +64,23 @@ class QuestionController extends Controller
|
|||
return view('question.index', $view->getData());
|
||||
}
|
||||
|
||||
public function indexQuery(Builder $query): Builder
|
||||
{
|
||||
if (request()->brand) {
|
||||
$query = $query->where('brand_id', '=', request()->brand);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the question export form
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function export()
|
||||
{
|
||||
$brands = Brand::all();
|
||||
return view('question.export', compact('brands'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports questions as csv the requested export type
|
||||
*
|
||||
* @param QuestionExport $request
|
||||
* @return \Symfony\Component\HttpFoundation\StreamedResponse
|
||||
*/
|
||||
public function exportSubmit(QuestionExport $request)
|
||||
{
|
||||
$results = [];
|
||||
|
|
@ -77,14 +108,12 @@ class QuestionController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
$pagination = 2000;
|
||||
|
||||
$w = Writer::createFromString('');
|
||||
$w->setDelimiter(';');
|
||||
$w->setEnclosure('"');
|
||||
$w->setOutputBOM(Writer::BOM_UTF8);
|
||||
|
||||
//We send the CSV using Symfony StreamedResponse
|
||||
// * We send the CSV using Symfony StreamedResponse.
|
||||
$response = new StreamedResponse();
|
||||
$response->headers->set('Content-Encoding', 'none');
|
||||
$response->headers->set('Content-Type', 'text/csv; charset=UTF-8');
|
||||
|
|
@ -108,10 +137,15 @@ class QuestionController extends Controller
|
|||
$w->insertOne([]);
|
||||
}
|
||||
|
||||
//we create a callable to output the CSV in chunk
|
||||
//with Symfony StreamResponse you can flush the body content if necessary
|
||||
//see Symfony documentation for more information
|
||||
$flush_threshold = 1000; //the flush value should depend on your CSV size.
|
||||
/*
|
||||
We create a callable to output the CSV in chunk
|
||||
with Symfony StreamResponse you can flush the body content if necessary
|
||||
see Symfony documentation for more information
|
||||
*/
|
||||
|
||||
// * The flush value should depend on your CSV size.
|
||||
$flush_threshold = 1000;
|
||||
|
||||
$response->setCallback(function () use ($w, $flush_threshold) {
|
||||
foreach ($w->chunk(1024) as $offset => $chunk) {
|
||||
echo $chunk;
|
||||
|
|
|
|||
|
|
@ -2,13 +2,17 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\GenerateApiToken;
|
||||
use App\Http\Requests\GenerateServiceToken;
|
||||
use App\Mail\ServiceCredentialRenewed;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
|
||||
use Kwaadpepper\CrudPolicies\Traits\CrudController;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class ServiceController extends Controller
|
||||
{
|
||||
use CrudController {
|
||||
|
|
@ -18,8 +22,19 @@ class ServiceController extends Controller
|
|||
/** @var Service */
|
||||
public static $modelClass = Service::class;
|
||||
|
||||
/**
|
||||
* The Bo app layout
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $layout = 'layouts.app';
|
||||
|
||||
/**
|
||||
* Overload crud-policies index
|
||||
*
|
||||
* @param CrudIndexRequest $request
|
||||
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
public function index(CrudIndexRequest $request)
|
||||
{
|
||||
/** @var \Illuminate\View\View */
|
||||
|
|
@ -30,17 +45,28 @@ class ServiceController extends Controller
|
|||
return view('service.index', $view->getData());
|
||||
}
|
||||
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||
public function generateApiToken(GenerateApiToken $request, Service $service)
|
||||
/**
|
||||
* Regen service credentials
|
||||
*
|
||||
* @param GenerateServiceToken $request
|
||||
* @param Service $service
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed, Squiz.Commenting.FunctionComment.Missing
|
||||
public function generateServicePassword(GenerateServiceToken $request, Service $service)
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
$bearerToken = $service->generateBearerToken();
|
||||
$bearerPwd = $service->bearer_pwd;
|
||||
$user = auth()->user();
|
||||
|
||||
// * Special Condition for migration from v2 to v3.
|
||||
$service->bearer_token = !Hash::needsRehash($service->bearer_token) ?
|
||||
$service->generateBearerToken() : $service->bearer_token;
|
||||
|
||||
$bearerPwd = $service->generateBearerPassword();
|
||||
$service->saveOrFail();
|
||||
Mail::send(new ServiceCredentialRenewed($service, $user));
|
||||
return back()
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
->with('error', "ATTENTION : METTEZ À JOUR LE SITE ! \nNouveau token d'API de $service->name : '$bearerToken' et mot de passe '$bearerPwd'");
|
||||
->with('error', "ATTENTION : METTEZ À JOUR LE SITE ! \nNouveau token d'API de $service->name : '$service->bearer_token' et mot de passe '$bearerPwd'");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ use Illuminate\Database\Eloquent\Builder;
|
|||
use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
|
||||
use Kwaadpepper\CrudPolicies\Traits\CrudController;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class SingleOptinController extends Controller
|
||||
{
|
||||
use CrudController {
|
||||
|
|
@ -16,8 +19,33 @@ class SingleOptinController extends Controller
|
|||
/** @var SingleOptin */
|
||||
public static $modelClass = SingleOptin::class;
|
||||
|
||||
/**
|
||||
* The Bo app layout
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $layout = 'layouts.app';
|
||||
|
||||
/**
|
||||
* Event index crud-policies
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
public function indexQuery(Builder $query): Builder
|
||||
{
|
||||
if (request()->brand) {
|
||||
$query = $query->where('brand_id', request()->brand);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload crud-policies index
|
||||
*
|
||||
* @param CrudIndexRequest $request
|
||||
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
public function index(CrudIndexRequest $request)
|
||||
{
|
||||
/** @var \Illuminate\View\View */
|
||||
|
|
@ -27,12 +55,4 @@ class SingleOptinController extends Controller
|
|||
}
|
||||
return view('single_optin.index', $view->getData());
|
||||
}
|
||||
|
||||
public function indexQuery(Builder $query): Builder
|
||||
{
|
||||
if (request()->brand) {
|
||||
$query = $query->where('brand_id', request()->brand);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,17 @@ use Illuminate\Support\Collection;
|
|||
use Illuminate\Support\Facades\Schema;
|
||||
use League\Csv\Writer;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class StatsController extends Controller
|
||||
{
|
||||
/**
|
||||
* Shows stats by brand
|
||||
*
|
||||
* @param StatsByBrandRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function viewStatsByBrand(StatsByBrandRequest $request)
|
||||
{
|
||||
$stats = null;
|
||||
|
|
@ -26,6 +35,12 @@ class StatsController extends Controller
|
|||
return view('stats.view-stats-by-brands', \compact('stats'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports stats by brand as a csv file
|
||||
*
|
||||
* @param StatsByBrandRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function exportByBrandCsv(StatsByBrandRequest $request)
|
||||
{
|
||||
$brand = Brand::findOrFail($request->brand);
|
||||
|
|
@ -64,6 +79,12 @@ class StatsController extends Controller
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Views stats for a specific question
|
||||
*
|
||||
* @param StatsByQuestionRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function viewByQuestion(StatsByQuestionRequest $request)
|
||||
{
|
||||
$question = null;
|
||||
|
|
@ -81,6 +102,12 @@ class StatsController extends Controller
|
|||
return view('stats.view-stats-by-questions', \compact('question', 'answers', 'answersByDays'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports stats for a specific question as csv
|
||||
*
|
||||
* @param StatsByQuestionRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function exportByQuestionCsv(StatsByQuestionRequest $request)
|
||||
{
|
||||
$question = Question::findOrFail($request->question);
|
||||
|
|
@ -116,9 +143,17 @@ class StatsController extends Controller
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a collection of total users answers by day
|
||||
*
|
||||
* @param Collection $answers A collection of Answer.
|
||||
* @param Carbon $start The wanted start period.
|
||||
* @param Carbon $end The wanted end period.
|
||||
* @return Collection A collection with date as Y_m_d index with array of answers and their total.
|
||||
*/
|
||||
private function answersByDays(Collection $answers, Carbon $start, Carbon $end): Collection
|
||||
{
|
||||
// Totalize by date all answers
|
||||
// * Totalize by date all answers.
|
||||
$answersByDays = CustomerAnswer::whereDate('created_at', '>=', $start)
|
||||
->whereDate('created_at', '<=', $end)
|
||||
->whereIn('answer_id', $answers->pluck('id'))
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
|||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
|
||||
/**
|
||||
* The application's global HTTP middleware stack.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ namespace App\Http\Middleware;
|
|||
use App\Enums\ApiError;
|
||||
use App\Exceptions\Api\V2\ApiErrorException;
|
||||
use App\Lib\Helper;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
/**
|
||||
* Check Http Header Timestamp with request timestamp
|
||||
|
|
@ -18,25 +18,24 @@ class ApiCheckTimeStamps
|
|||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @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)
|
||||
public function handle(Request $request, \Closure $next)
|
||||
{
|
||||
if (Helper::isRequestForApiV2($request)) {
|
||||
if (!$request->hasHeader('X-API-TIMESTAMP') or !is_numeric($request->header('X-API-TIMESTAMP'))) {
|
||||
if (!\is_string($request->timestamp) or !Carbon::hasFormat($request->timestamp, 'Y-m-d H:i:s')) {
|
||||
throw new ApiErrorException(ApiError::wrongTimeStamp());
|
||||
}
|
||||
if (!$request->timestamp or !is_numeric($request->timestamp)) {
|
||||
throw new ApiErrorException(ApiError::wrongTimeStamp());
|
||||
}
|
||||
$diff = intval($request->timestamp) - intval($request->header('X-API-TIMESTAMP'));
|
||||
$maxDiff = 120;
|
||||
if (abs($diff) > $maxDiff) {
|
||||
if (
|
||||
Carbon::parse($request->timestamp, 'utc')
|
||||
->isBefore(Carbon::now('UTC')->subSeconds(config('api.timestampTimeout', 10)))
|
||||
) {
|
||||
throw new ApiErrorException(ApiError::wrongTimeStamp());
|
||||
}
|
||||
}
|
||||
return $next($request);
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ use App\Enums\ApiStatus;
|
|||
use App\Http\Resources\Api\V2\ApiResponse as V2ApiResponse;
|
||||
use App\Lib\Helper;
|
||||
use App\Models\Service;
|
||||
use Closure;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
|
|
@ -16,60 +16,47 @@ class ApiResponse
|
|||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @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)
|
||||
public function handle(Request $request, \Closure $next)
|
||||
{
|
||||
/** @var \Illuminate\Http\JsonResponse $response */
|
||||
$response = $next($request);
|
||||
|
||||
// Set no cache header
|
||||
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');
|
||||
|
||||
$isApiV1Request = Helper::isRequestForApiV1($request);
|
||||
$isApiV2Request = Helper::isRequestForApiV2($request);
|
||||
|
||||
if ($isApiV1Request) {
|
||||
// Use patched v1 Auth mecanism (without bearer reimplementation)
|
||||
$bearerToken = Helper::getBearerTokenFromRequest($request) ?? $request->api_token;
|
||||
if (Helper::isRequestForApiV1($request)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
try {
|
||||
$service = Service::getFromBearerToken($bearerToken);
|
||||
$bearerToken = Helper::getBearerTokenFromRequest($request) ?? $request->api_token ?? '';
|
||||
$service = Service::getFromBearerToken($bearerToken);
|
||||
$service->incrementStat();
|
||||
} catch (ModelNotFoundException $e) {
|
||||
Log::error(sprintf('Could not increment stats %s', $e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
if ($isApiV2Request) {
|
||||
$service = null;
|
||||
$bearerToken = Helper::getBearerTokenFromRequest($request);
|
||||
try {
|
||||
if ($bearerToken) {
|
||||
$service = Service::getFromBearerToken($bearerToken);
|
||||
}
|
||||
} catch (ModelNotFoundException $e) {
|
||||
try {
|
||||
if (!$service and $request->api_token) {
|
||||
$service = Service::getFromApiToken($request->api_token);
|
||||
}
|
||||
} catch (ModelNotFoundException $e) {
|
||||
Log::error(sprintf('Could not increment stats %s', $e->getMessage()));
|
||||
}
|
||||
}
|
||||
if ($service) {
|
||||
$service->incrementStat();
|
||||
}
|
||||
}
|
||||
|
||||
if ($isApiV1Request) {
|
||||
return $response;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/** @var \App\Models\Service|null */
|
||||
if (\optional($service = app(Service::class))->id) {
|
||||
$service->incrementStat();
|
||||
}
|
||||
|
||||
/** @var object $data */
|
||||
$data = $response->getData();
|
||||
|
||||
// Is reponse is already a V2ApiResponse then just return it
|
||||
// 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
|
||||
|
|
@ -79,7 +66,7 @@ class ApiResponse
|
|||
return $response;
|
||||
}
|
||||
|
||||
/** @ var \App\Enums\ApiStatus */
|
||||
/** @var \App\Enums\ApiStatus $status */
|
||||
$status = intval($response->getStatusCode() / 100) == 2 ? ApiStatus::OK() : ApiStatus::KO();
|
||||
return $response->setData(new V2ApiResponse((object)$response->getData(), $status));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,24 +15,25 @@ class Authenticate extends Middleware
|
|||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string[] ...$guards
|
||||
* @param mixed $request
|
||||
* @param \Closure $next
|
||||
* @param mixed ...$guards
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Illuminate\Auth\AuthenticationException
|
||||
* @throws \Illuminate\Auth\AuthenticationException If authentification fails.
|
||||
*/
|
||||
public function handle($request, Closure $next, ...$guards)
|
||||
{
|
||||
// TODO: check this works
|
||||
if (Helper::isRequestForApiV1($request)) {
|
||||
// Use patched v1 Auth mecanism (without bearer reimplementation)
|
||||
// @codeCoverageIgnoreStart
|
||||
// Use patched v1 Auth mecanism (without bearer reimplementation).
|
||||
$bearerToken = Helper::getBearerTokenFromRequest($request) ?? $request->api_token ?? '';
|
||||
$brand = Service::getFromBearerToken($bearerToken)->brand;
|
||||
$request->request->add(['brand_id' => $brand->id]);
|
||||
$service = Service::getFromBearerToken($bearerToken);
|
||||
$request->request->add(['brand_id' => $service->brand->id]);
|
||||
$response = $next($request);
|
||||
// @codeCoverageIgnoreEnd
|
||||
} else {
|
||||
// Use Laravel auth check mecanism
|
||||
// Use Laravel auth check mecanism.
|
||||
$response = \call_user_func_array(
|
||||
[$this, 'parent::' . __FUNCTION__],
|
||||
array_merge([$request, $next], $guards)
|
||||
|
|
@ -44,8 +45,9 @@ class Authenticate extends Middleware
|
|||
/**
|
||||
* Get the path the user should be redirected to when they are not authenticated.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param mixed $request
|
||||
* @return string|null
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
protected function redirectTo($request)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,30 +8,33 @@ use Closure;
|
|||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class CheckBrand
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$apiToken = Helper::getBearerTokenFromRequest($request) ?? $request->api_token;
|
||||
$apiToken = Helper::getBearerTokenFromRequest($request) ?? $request->api_token ?? '';
|
||||
|
||||
if ($apiToken === null) {
|
||||
abort(401, 'Le token d\'authentification est incorrect dfd' . \json_encode($request->all()));
|
||||
abort(401, 'Le token d\'authentification est incorrect ' . \json_encode($request->all()));
|
||||
}
|
||||
|
||||
try {
|
||||
$brand = Service::getFromBearerToken($apiToken)->brand;
|
||||
$request->request->add(['brand_id' => $brand->id]);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
abort(401, 'Le token d\'authentification est incorrect ' . $e->getMessage());
|
||||
}
|
||||
|
||||
$request->request->add(['brand_id' => $brand->id]);
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,13 +6,10 @@ use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
|||
|
||||
class EncryptCookies extends Middleware
|
||||
{
|
||||
|
||||
/**
|
||||
* The names of the cookies that should not be encrypted.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
protected $except = [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Lib\Helper;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class JsonOnly
|
||||
|
|
@ -11,15 +10,20 @@ class JsonOnly
|
|||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
public function handle(Request $request, \Closure $next)
|
||||
{
|
||||
// TODO reve v1 check when dropped
|
||||
if (!Helper::isRequestForApiV1($request) and !$request->isJson()) {
|
||||
abort(400);
|
||||
// TODO remove condition v1 check when dropped.
|
||||
if (
|
||||
!Helper::isRequestForApiV1($request) and
|
||||
(!$request->isJson() or
|
||||
!$request->expectsJson() or
|
||||
is_null(json_decode($request->getContent())))
|
||||
) {
|
||||
abort(400, 'Invalid JSON');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
|
|
|
|||
|
|
@ -6,13 +6,10 @@ use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Mi
|
|||
|
||||
class PreventRequestsDuringMaintenance extends Middleware
|
||||
{
|
||||
|
||||
/**
|
||||
* The URIs that should be reachable while maintenance mode is enabled.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
protected $except = [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,14 +7,17 @@ use Closure;
|
|||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class RedirectIfAuthenticated
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string|null ...$guards
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string|null ...$guards
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, ...$guards)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
|
|||
|
||||
class TrimStrings extends Middleware
|
||||
{
|
||||
|
||||
/**
|
||||
* The names of the attributes that should not be trimmed.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ namespace App\Http\Middleware;
|
|||
|
||||
use Illuminate\Http\Middleware\TrustHosts as Middleware;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class TrustHosts extends Middleware
|
||||
{
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ use Illuminate\Http\Request;
|
|||
|
||||
class TrustProxies extends Middleware
|
||||
{
|
||||
|
||||
/**
|
||||
* The trusted proxies for this application.
|
||||
*
|
||||
|
|
@ -18,7 +17,7 @@ class TrustProxies extends Middleware
|
|||
/**
|
||||
* The headers that should be used to detect proxies.
|
||||
*
|
||||
* @var int
|
||||
* @var integer
|
||||
*/
|
||||
protected $headers = Request::HEADER_X_FORWARDED_FOR |
|
||||
Request::HEADER_X_FORWARDED_HOST |
|
||||
|
|
|
|||
|
|
@ -6,13 +6,10 @@ use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
|||
|
||||
class VerifyCsrfToken extends Middleware
|
||||
{
|
||||
|
||||
/**
|
||||
* The URIs that should be excluded from CSRF verification.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
protected $except = [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ class XssSanitizer
|
|||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param mixed $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ class AuthenticateRequest extends BaseApiRequest
|
|||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Http\Requests\Api\V2;
|
||||
|
||||
use App\Http\Sanitizers\RemoveSpaces;
|
||||
use ArondeParon\RequestSanitizer\Sanitizers\Capitalize;
|
||||
use ArondeParon\RequestSanitizer\Sanitizers\Lowercase;
|
||||
use ArondeParon\RequestSanitizer\Sanitizers\RemoveNonNumeric;
|
||||
|
|
@ -14,6 +15,10 @@ abstract class BaseApiRequest extends FormRequest
|
|||
{
|
||||
use SanitizesInputs;
|
||||
|
||||
/**
|
||||
* Using these sanitizers before proceeding the request
|
||||
* @var array
|
||||
*/
|
||||
protected $sanitizers = [
|
||||
'firstname' => [Trim::class, TrimDuplicateSpaces::class, Capitalize::class],
|
||||
'lastname' => [Trim::class, TrimDuplicateSpaces::class, Capitalize::class],
|
||||
|
|
@ -21,15 +26,15 @@ abstract class BaseApiRequest extends FormRequest
|
|||
'address2' => [Trim::class, TrimDuplicateSpaces::class],
|
||||
'city' => [Trim::class, TrimDuplicateSpaces::class],
|
||||
'password' => Trim::class,
|
||||
'phone' => Trim::class,
|
||||
'email' => [Lowercase::class, Trim::class],
|
||||
'phone' => [Trim::class, RemoveSpaces::class],
|
||||
'email' => [Lowercase::class, Trim::class, RemoveSpaces::class],
|
||||
'postcode' => [RemoveNonNumeric::class, Trim::class]
|
||||
];
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,17 +3,17 @@
|
|||
namespace App\Http\Requests\Api\V2;
|
||||
|
||||
use App\Http\Requests\Api\V2\BaseApiRequest;
|
||||
use Exception;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class GetBrandStatsRequest extends BaseApiRequest
|
||||
{
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
* @throws ValidationException If given dates are unparsable of period is bigger than 3 months.
|
||||
* @throws \Exception If carbon date processing failed.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
|
|
@ -23,7 +23,7 @@ class GetBrandStatsRequest extends BaseApiRequest
|
|||
'Vous devez sélectionner une période de maximum 3 mois'
|
||||
]);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
if (\get_class($e) !== ValidationException::class) {
|
||||
throw ValidationException::withMessages([
|
||||
'Les dates séléectionnées ne sembles pas être valides'
|
||||
|
|
|
|||
|
|
@ -9,11 +9,12 @@ use Illuminate\Validation\ValidationException;
|
|||
|
||||
class GetQuestionStatsRequest extends BaseApiRequest
|
||||
{
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
* @throws ValidationException If given dates are unparsable of period is bigger than 3 months.
|
||||
* @throws \Exception If carbon date processing failed.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
|
|
@ -32,8 +33,6 @@ class GetQuestionStatsRequest extends BaseApiRequest
|
|||
throw $e;
|
||||
}
|
||||
return [
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||
'question' => 'required|exists:questions,id',
|
||||
'start' => 'required|date|date_format:Y-m-d',
|
||||
'end' => 'required|date|date_format:Y-m-d|after_or_equal:start',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ class SendArchadiaDefaultMailRequest extends BaseApiRequest
|
|||
{
|
||||
use SanitizesInputs;
|
||||
|
||||
/**
|
||||
* Using these sanitizers before proceeding the request
|
||||
* @var array
|
||||
*/
|
||||
protected $sanitizers = [
|
||||
'label_type_support' => Trim::class,
|
||||
'operation' => Trim::class,
|
||||
|
|
@ -59,6 +63,11 @@ class SendArchadiaDefaultMailRequest extends BaseApiRequest
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Attributes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes()
|
||||
{
|
||||
return [
|
||||
|
|
@ -81,6 +90,11 @@ class SendArchadiaDefaultMailRequest extends BaseApiRequest
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Messages
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ class SendArchadiaGameMailRequest extends BaseApiRequest
|
|||
{
|
||||
use SanitizesInputs;
|
||||
|
||||
/**
|
||||
* Using these sanitizers before proceeding the request
|
||||
* @var array
|
||||
*/
|
||||
protected $sanitizers = [
|
||||
'code_on_pack' => Trim::class,
|
||||
'code_unique' => Trim::class,
|
||||
|
|
@ -53,6 +57,11 @@ class SendArchadiaGameMailRequest extends BaseApiRequest
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Attributes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes()
|
||||
{
|
||||
return [
|
||||
|
|
@ -74,6 +83,11 @@ class SendArchadiaGameMailRequest extends BaseApiRequest
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Messages
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ namespace App\Http\Requests\Api\V2;
|
|||
|
||||
class SingleOptinRequest extends BaseApiRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class StoreCustomerAnswers extends BaseApiRequest
|
|||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
// resolve Service
|
||||
// * Resolve Service.
|
||||
$service = app()->make(\App\Models\Service::class);
|
||||
$brandId = $service->brand->id;
|
||||
$question = Question::findOrFail($this->question);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class StoreCustomerRequest extends BaseApiRequest
|
|||
Country::belgium()->value,
|
||||
Country::luxembourg()->value,
|
||||
Country::france()->value,
|
||||
// DROM COM
|
||||
// * DROM COM.
|
||||
Country::guadeloupe()->value,
|
||||
Country::martinique()->value,
|
||||
Country::frenchGuiana()->value,
|
||||
|
|
@ -64,6 +64,11 @@ class StoreCustomerRequest extends BaseApiRequest
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Messages
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Api\V2;
|
||||
|
||||
class StoreSingleOptinRequest extends BaseApiRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'email' => 'required|email:rfc,dns,spoof,filter'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,6 @@ use Kwaadpepper\Enum\Rules\EnumIsValidRule;
|
|||
|
||||
class UpdateCustomerRequest extends BaseApiRequest
|
||||
{
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
|
|
@ -16,37 +15,35 @@ class UpdateCustomerRequest extends BaseApiRequest
|
|||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$fields = [
|
||||
$fields = [
|
||||
'firstname', 'lastname', 'email', 'password',
|
||||
'birth_date', 'address', 'phone', 'country',
|
||||
'city', 'postcode', 'opt_in'
|
||||
];
|
||||
//phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||
$atLeast = function ($attribute, $value, $fail) use ($fields) {
|
||||
if (!\in_array($attribute, $fields)) {
|
||||
$fail(trans('require_at_least_field', [
|
||||
'attributes' => rtrim(\implode(',', $fields), ',')
|
||||
]));
|
||||
}
|
||||
$atLeast = function ($attribute) use ($fields) {
|
||||
return 'required_without_all:' . collect($fields)
|
||||
->filter(function ($fieldName) use ($attribute) {
|
||||
return $fieldName !== $attribute;
|
||||
})->implode(',');
|
||||
};
|
||||
return [
|
||||
'firstname' => [$atLeast, 'string', 'min:1', 'max:255'],
|
||||
'lastname' => [$atLeast, 'string', 'min:1', 'max:255'],
|
||||
'email' => [$atLeast, 'email:rfc,dns,spoof,filter', 'max:255'],
|
||||
'firstname' => [$atLeast('firstname'), 'string', 'min:1', 'max:255'],
|
||||
'lastname' => [$atLeast('lastname'), 'string', 'min:1', 'max:255'],
|
||||
'email' => [$atLeast('email'), 'email:rfc,dns,spoof,filter', 'max:255'],
|
||||
'password' => [
|
||||
$atLeast, 'max:255', Password::min(8)
|
||||
$atLeast('password'), 'max:255', Password::min(8)
|
||||
->mixedCase()
|
||||
->letters()
|
||||
->numbers()
|
||||
->symbols()
|
||||
->uncompromised()
|
||||
],
|
||||
'birth_date' => [$atLeast, 'date_format:Y-m-d', 'max:255'],
|
||||
'address' => [$atLeast, 'required_with:address2', 'string', 'max:255'],
|
||||
'birth_date' => [$atLeast('birth_date'), 'date_format:Y-m-d', 'max:255'],
|
||||
'address' => [$atLeast('address'), 'required_with:address2', 'string', 'max:255'],
|
||||
'address2' => ['sometimes', 'string', 'max:255'],
|
||||
'phone' => [$atLeast, 'string', 'phone:FR,BE,LU', 'max:255'],
|
||||
'phone' => [$atLeast('phone'), 'string', 'phone:FR,BE,LU', 'max:255'],
|
||||
'country' => [
|
||||
$atLeast,
|
||||
$atLeast('country'),
|
||||
'numeric',
|
||||
new EnumIsValidRule(Country::class),
|
||||
sprintf(
|
||||
|
|
@ -55,7 +52,7 @@ class UpdateCustomerRequest extends BaseApiRequest
|
|||
Country::belgium()->value,
|
||||
Country::luxembourg()->value,
|
||||
Country::france()->value,
|
||||
// DROM COM
|
||||
// * DROM COM.
|
||||
Country::guadeloupe()->value,
|
||||
Country::martinique()->value,
|
||||
Country::frenchGuiana()->value,
|
||||
|
|
@ -70,12 +67,17 @@ class UpdateCustomerRequest extends BaseApiRequest
|
|||
])
|
||||
)
|
||||
],
|
||||
'city' => [$atLeast, 'string', 'min:2', 'max:255'],
|
||||
'postcode' => [$atLeast, 'string', 'postal_code:FR,BE,LU', 'max:255'],
|
||||
'opt_in' => [$atLeast, 'boolean'],
|
||||
'city' => [$atLeast('city'), 'string', 'min:2', 'max:255'],
|
||||
'postcode' => [$atLeast('postcode'), 'string', 'postal_code:FR,BE,LU', 'max:255'],
|
||||
'opt_in' => [$atLeast('opt_in'), 'boolean'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Messages
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Api\V2;
|
||||
|
||||
class UpdateSingleOptinRequest extends BaseApiRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'email' => 'required|email:rfc,dns,spoof,filter',
|
||||
'optin' => 'required|boolean'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -5,12 +5,15 @@ namespace App\Http\Requests;
|
|||
use App\Enums\CustomerExportType;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class CustomerExport extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,12 +4,15 @@ namespace App\Http\Requests;
|
|||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class GenerateApiToken extends FormRequest
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class GenerateServiceToken extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
|
|
@ -23,8 +26,6 @@ class GenerateApiToken extends FormRequest
|
|||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -6,10 +6,17 @@ use ArondeParon\RequestSanitizer\Sanitizers\FilterVars;
|
|||
use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class ManageBrandRequest extends FormRequest
|
||||
{
|
||||
use SanitizesInputs;
|
||||
|
||||
/**
|
||||
* Using these sanitizers before proceeding the request
|
||||
* @var array
|
||||
*/
|
||||
protected $sanitizers = [
|
||||
'name' => [
|
||||
FilterVars::class => [
|
||||
|
|
@ -21,7 +28,7 @@ class ManageBrandRequest extends FormRequest
|
|||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,10 +6,17 @@ use ArondeParon\RequestSanitizer\Sanitizers\FilterVars;
|
|||
use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class ManageServiceRequest extends FormRequest
|
||||
{
|
||||
use SanitizesInputs;
|
||||
|
||||
/**
|
||||
* Using these sanitizers before proceeding the request
|
||||
* @var array
|
||||
*/
|
||||
protected $sanitizers = [
|
||||
'name' => [
|
||||
FilterVars::class => [
|
||||
|
|
@ -21,7 +28,7 @@ class ManageServiceRequest extends FormRequest
|
|||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,12 +4,15 @@ namespace App\Http\Requests;
|
|||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class QuestionExport extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,12 +7,15 @@ use Illuminate\Foundation\Http\FormRequest;
|
|||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class StatsByBrandRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
|
|
@ -23,6 +26,8 @@ class StatsByBrandRequest extends FormRequest
|
|||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
* @throws ValidationException If given dates are unparsable of period is bigger than 1 year.
|
||||
* @throws \Exception If carbon date processing failed.
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
|
|
@ -46,7 +51,7 @@ class StatsByBrandRequest extends FormRequest
|
|||
'start' => 'required|date|date_format:Y-m-d',
|
||||
'end' => 'required|date|date_format:Y-m-d|after_or_equal:start',
|
||||
];
|
||||
}
|
||||
}//end if
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,12 +7,15 @@ use Illuminate\Foundation\Http\FormRequest;
|
|||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class StatsByQuestionRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
|
|
@ -23,6 +26,8 @@ class StatsByQuestionRequest extends FormRequest
|
|||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
* @throws ValidationException If given dates are unparsable of period is bigger than 1 year.
|
||||
* @throws \Exception If carbon date processing failed.
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
|
|
@ -46,7 +51,7 @@ class StatsByQuestionRequest extends FormRequest
|
|||
'start' => 'required|date|date_format:Y-m-d',
|
||||
'end' => 'required|date|date_format:Y-m-d|after_or_equal:start',
|
||||
];
|
||||
}
|
||||
}//end if
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ class AnswersResource extends JsonResourceCollection
|
|||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param mixed $request
|
||||
* @return array
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
|
||||
public function toArray($request)
|
||||
{
|
||||
return $this->collection->map(function (Answer $answer) {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ use stdClass;
|
|||
|
||||
class ApiResponse extends JsonResource
|
||||
{
|
||||
|
||||
/** @var stdClass */
|
||||
protected $data;
|
||||
|
||||
|
|
@ -16,7 +15,7 @@ class ApiResponse extends JsonResource
|
|||
protected $status;
|
||||
|
||||
/**
|
||||
* @param stdClass $data
|
||||
* @param stdClass $data
|
||||
* @param ApiStatus $status
|
||||
*/
|
||||
public function __construct(stdClass $data = null, ApiStatus $status)
|
||||
|
|
@ -28,11 +27,10 @@ class ApiResponse extends JsonResource
|
|||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param mixed $request
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
//phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
|
||||
public function toArray($request): array
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ class BrandStatsResource extends JsonResourceCollection
|
|||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param mixed $request
|
||||
* @return array
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ use Illuminate\Http\Resources\Json\JsonResource;
|
|||
|
||||
class CustomerResource extends JsonResource
|
||||
{
|
||||
|
||||
/** @var \App\Models\Customer */
|
||||
private $customer;
|
||||
|
||||
|
|
@ -18,6 +17,7 @@ class CustomerResource extends JsonResource
|
|||
/** @var \App\Models\CustomerLogin */
|
||||
private $login;
|
||||
|
||||
/** @var array */
|
||||
private static $skeleton = [
|
||||
'hashcode' => '',
|
||||
'firstname' => '',
|
||||
|
|
@ -35,6 +35,12 @@ class CustomerResource extends JsonResource
|
|||
'last_connection' => ''
|
||||
];
|
||||
|
||||
/**
|
||||
* CustomerResource
|
||||
*
|
||||
* @param Brand $brand
|
||||
* @param Customer $customer
|
||||
*/
|
||||
public function __construct(Brand $brand, Customer $customer)
|
||||
{
|
||||
$this->customer = $customer;
|
||||
|
|
@ -45,7 +51,9 @@ class CustomerResource extends JsonResource
|
|||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request|null $request
|
||||
* @return array
|
||||
* + email string
|
||||
* + firstname string
|
||||
* + lastname string
|
||||
* + birth_date datetime
|
||||
|
|
@ -58,11 +66,11 @@ class CustomerResource extends JsonResource
|
|||
* + opt_in boolean
|
||||
* + hashcode string
|
||||
* + status boolean
|
||||
* + last_connection date
|
||||
*/
|
||||
//phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
//phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
|
||||
public function toArray($request = null)
|
||||
{
|
||||
// patch country TODO remove this when all crm are migrated to v2
|
||||
$data = array_intersect_key(
|
||||
array_merge(
|
||||
[
|
||||
|
|
@ -75,11 +83,16 @@ class CustomerResource extends JsonResource
|
|||
$this->getSkeleton()
|
||||
);
|
||||
|
||||
$data['country'] = $this->info->newcountry->value;
|
||||
$data['country'] = $this->info->country->value;
|
||||
$data['address2'] = $this->info->address2 ?? '';
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resource skeletton
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getSkeleton(): array
|
||||
{
|
||||
return self::$skeleton;
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources\Api\V2;
|
||||
|
||||
use App\Models\Question;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class QuestionResource extends JsonResource
|
||||
{
|
||||
|
||||
/** @var Question */
|
||||
private $question;
|
||||
|
||||
public function __construct(Question $question)
|
||||
{
|
||||
$this->question = $question;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->question->id,
|
||||
'question' => $this->question->question,
|
||||
'answers' => new AnswersResource($this->question->answers)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -8,14 +8,14 @@ use Illuminate\Support\Collection;
|
|||
|
||||
class QuestionStatsResource extends JsonResourceCollection
|
||||
{
|
||||
|
||||
/** @var \App\Models\Question */
|
||||
protected $question = null;
|
||||
|
||||
/**
|
||||
* Create a new resource instance.
|
||||
*
|
||||
* @param mixed $resource
|
||||
* @param \Illuminate\Support\Collection $questionStats
|
||||
* @param \App\Models\Question $question
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Collection $questionStats = null, Question $question)
|
||||
|
|
@ -27,15 +27,14 @@ class QuestionStatsResource extends JsonResourceCollection
|
|||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param mixed $request
|
||||
* @return array
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'answers' => (new AnswersResource($this->question->answers))
|
||||
->toArray($request),
|
||||
'answers' => (new AnswersResource($this->question->answers))->toArray($request),
|
||||
'stats' => $this->collection->toArray()
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@ use Illuminate\Support\Collection;
|
|||
|
||||
class QuestionsResource extends JsonResourceCollection
|
||||
{
|
||||
|
||||
/** @var \Illuminate\Support\Collection */
|
||||
protected $userAnwsers = null;
|
||||
|
||||
/**
|
||||
* Create a new resource instance.
|
||||
*
|
||||
* @param mixed $resource
|
||||
* @param mixed $resource
|
||||
* @param \Illuminate\Support\Collection $userAnwsers
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($resource, Collection $userAnwsers = null)
|
||||
|
|
@ -28,10 +28,10 @@ class QuestionsResource extends JsonResourceCollection
|
|||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param mixed $request
|
||||
* @return array
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
|
||||
public function toArray($request)
|
||||
{
|
||||
return $this->collection->map(function (Question $question) {
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources\Api\V2;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class SingleOptinResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'email' => $request->email
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -4,11 +4,18 @@ namespace App\Http\Resources;
|
|||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class CustomerResource extends JsonResource
|
||||
{
|
||||
|
||||
/** @var integer */
|
||||
private $brandId;
|
||||
|
||||
/**
|
||||
* @param mixed $resource
|
||||
* @param mixed $brandId
|
||||
*/
|
||||
public function __construct($resource, $brandId)
|
||||
{
|
||||
parent::__construct($resource);
|
||||
|
|
@ -18,10 +25,10 @@ class CustomerResource extends JsonResource
|
|||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param mixed $request
|
||||
* @return array
|
||||
*/
|
||||
//phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
//phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
|
||||
public function toArray($request)
|
||||
{
|
||||
$infos = $this->infos()->where('brand_id', $this->brandId)->first();
|
||||
|
|
@ -37,7 +44,7 @@ class CustomerResource extends JsonResource
|
|||
'address' => $infos->address,
|
||||
'address2' => $infos->address2,
|
||||
'phone' => $infos->phone,
|
||||
'country' => $infos->country,
|
||||
'country' => $infos->country->label,
|
||||
'city' => $infos->city,
|
||||
'postcode' => $infos->postcode,
|
||||
'opt_in' => $infos->opt_in,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ namespace App\Http\Resources;
|
|||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class StatusResource extends JsonResource
|
||||
{
|
||||
}
|
||||
|
|
|
|||
17
app/Http/Sanitizers/RemoveSpaces.php
Normal file
17
app/Http/Sanitizers/RemoveSpaces.php
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Sanitizers;
|
||||
|
||||
use ArondeParon\RequestSanitizer\Contracts\Sanitizer;
|
||||
|
||||
class RemoveSpaces implements Sanitizer
|
||||
{
|
||||
/**
|
||||
* @param mixed $input
|
||||
* @return string
|
||||
*/
|
||||
public function sanitize($input): string
|
||||
{
|
||||
return preg_replace('~\s{1,}~', '', $input);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,9 @@ use Illuminate\Queue\InteractsWithQueue;
|
|||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class SetServiceStats implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
|
|
@ -26,6 +29,7 @@ class SetServiceStats implements ShouldQueue
|
|||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param Service $service
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Service $service)
|
||||
|
|
@ -53,11 +57,21 @@ class SetServiceStats implements ShouldQueue
|
|||
$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 [
|
||||
|
|
|
|||
|
|
@ -8,12 +8,20 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
|||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class StartStatJobs implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$brands = Brand::all();
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ use Illuminate\Queue\Middleware\WithoutOverlapping;
|
|||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class StatJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
|
|
@ -22,7 +25,11 @@ class StatJob implements ShouldQueue
|
|||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
// Set high max tries as using prevent overlap
|
||||
/**
|
||||
* Set high max tries as using prevent overlap
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $tries = 10;
|
||||
|
||||
/** Used to prevent overlaping */
|
||||
|
|
@ -34,6 +41,7 @@ class StatJob implements ShouldQueue
|
|||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param Brand $brand
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Brand $brand)
|
||||
|
|
@ -72,7 +80,7 @@ class StatJob implements ShouldQueue
|
|||
*/
|
||||
public function middleware()
|
||||
{
|
||||
// dispatch randomly overlaping jobs if overlapping
|
||||
// * Dispatch randomly overlaping jobs if overlapping.
|
||||
return [(new WithoutOverlapping(self::JOBKEY))->releaseAfter(rand(20, 90))];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ use Illuminate\Queue\InteractsWithQueue;
|
|||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class UpdateCustomerLastConnection implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
|
|
@ -30,6 +33,9 @@ class UpdateCustomerLastConnection implements ShouldQueue
|
|||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param Customer $customer
|
||||
* @param CustomerLogin $customerLogin
|
||||
* @param Carbon $date
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@
|
|||
|
||||
namespace App\Lib;
|
||||
|
||||
use App\Exceptions\Api\V2\CrmException;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
|
|
@ -11,7 +14,6 @@ use Illuminate\Support\Str;
|
|||
*/
|
||||
class Helper
|
||||
{
|
||||
|
||||
/**
|
||||
* Package infos
|
||||
*
|
||||
|
|
@ -27,7 +29,7 @@ class Helper
|
|||
*/
|
||||
public static function isRequestForApiV2(Request $request)
|
||||
{
|
||||
// Small hack to ensure to know if route is From APIv2
|
||||
// * Small hack to ensure to know if route is From APIv2.
|
||||
$routeName = static::getRouteName($request) ??
|
||||
(strpos($request->path(), 'api/v2') === 0 ? 'apiV2.' : null) ?? 'none';
|
||||
return strpos($routeName, 'apiV2.') === 0;
|
||||
|
|
@ -41,7 +43,7 @@ class Helper
|
|||
*/
|
||||
public static function isRequestForApiV1(Request $request): bool
|
||||
{
|
||||
// Small hack to ensure to know if route is From APIv1
|
||||
// * 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;
|
||||
|
|
@ -58,6 +60,27 @@ class Helper
|
|||
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
|
||||
*
|
||||
|
|
@ -95,23 +118,77 @@ class Helper
|
|||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
* Calculate the request signature
|
||||
*
|
||||
* @param Request $request
|
||||
* @return string|null
|
||||
* @param string $secret
|
||||
* @param string $uri
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
* @return string request signature
|
||||
* @throws CrmException If an error occurs.
|
||||
*/
|
||||
public static function getBearerTokenFromRequest(Request $request): ?string
|
||||
public static function signRequest(string $secret, string $uri, string $method, array $parameters): string
|
||||
{
|
||||
$header = $request->header('Authorization', '');
|
||||
try {
|
||||
$uri = collect(\parse_url($uri));
|
||||
$method = \strtolower($method);
|
||||
|
||||
if (Str::contains($header, 'Bearer ')) {
|
||||
$auths = \explode(',', $header);
|
||||
foreach ($auths as $auth) {
|
||||
if (Str::contains($auth, 'Bearer ')) {
|
||||
return Str::substr($auth, 7);
|
||||
// * 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 CrmException('Failed to calculate requests hash');
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
return $calculatedHash;
|
||||
} catch (DecryptException $e) {
|
||||
throw new CrmException('Failed to decrypt service credentials', 0, $e);
|
||||
} catch (\Exception $e) {
|
||||
throw new CrmException('An error occured when authenticating service', 0, $e);
|
||||
}//end try
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,12 +16,21 @@ use Illuminate\Support\Facades\Hash;
|
|||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class MigrateOldData extends LeGaulois2016Migration
|
||||
{
|
||||
|
||||
/** @var Brand */
|
||||
private $brand;
|
||||
|
||||
/**
|
||||
* runMigration
|
||||
*
|
||||
* @param Brand $brand
|
||||
* @param string $table
|
||||
* @return void
|
||||
*/
|
||||
public function runMigration(Brand $brand, string $table)
|
||||
{
|
||||
$this->table = $table;
|
||||
|
|
@ -30,6 +39,12 @@ class MigrateOldData extends LeGaulois2016Migration
|
|||
$this->up();
|
||||
}
|
||||
|
||||
/**
|
||||
* instruct
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
// phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Squiz.Commenting.FunctionComment.Missing
|
||||
public function instruct(): array
|
||||
{
|
||||
$this->initFile();
|
||||
|
|
@ -65,7 +80,7 @@ class MigrateOldData extends LeGaulois2016Migration
|
|||
$updated_at = $v[12];
|
||||
$last_cnx = $v[13];
|
||||
|
||||
// Just save Single optin
|
||||
// * Just save Single optin.
|
||||
if (empty($first_name) && empty($last_name)) {
|
||||
$sglOptin = SingleOptin::where('email', strtolower($email))
|
||||
->where('brand_id', $this->brand->id)->firstOrNew();
|
||||
|
|
@ -73,7 +88,7 @@ class MigrateOldData extends LeGaulois2016Migration
|
|||
$sglOptin->brand_id = $this->brand->id;
|
||||
$sglOptin->save();
|
||||
} else {
|
||||
// Validate Data
|
||||
// * Validate Data.
|
||||
$d = [
|
||||
'first_name' => $first_name,
|
||||
'last_name' => $last_name,
|
||||
|
|
@ -115,7 +130,7 @@ class MigrateOldData extends LeGaulois2016Migration
|
|||
$model->updated_at = $updated_at;
|
||||
$model->save();
|
||||
|
||||
// filtre birthdate puisque c'est du sale dans la vielle crm
|
||||
// * Filtre birthdate puisque c'est du sale dans la vielle crm.
|
||||
try {
|
||||
$expl = explode('-', $birth_date);
|
||||
$d = Carbon::parse($birth_date);
|
||||
|
|
@ -178,7 +193,7 @@ class MigrateOldData extends LeGaulois2016Migration
|
|||
'updated_at' => $updated_at
|
||||
]
|
||||
);
|
||||
}
|
||||
}//end if
|
||||
throw new AbortRowImportException();
|
||||
},
|
||||
'name' => [
|
||||
|
|
@ -203,11 +218,22 @@ class MigrateOldData extends LeGaulois2016Migration
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the migration file
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initFile()
|
||||
{
|
||||
File::put(storage_path('crm_migration.csv'), sprintf('new;old;hashcode;created_at;updated_at') . PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write date in the migration file
|
||||
*
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function write(array $data)
|
||||
{
|
||||
foreach ($data as &$d) {
|
||||
|
|
|
|||
|
|
@ -7,23 +7,32 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
|||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class CourrierArcadiaError extends Mailable implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
/** @var string */
|
||||
public $subject;
|
||||
|
||||
/** @var string */
|
||||
public $msg;
|
||||
|
||||
/** @var string */
|
||||
public $xml;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @param string $subject
|
||||
* @param string $msg
|
||||
* @param string $xml
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($subject, $msg, $xml)
|
||||
public function __construct(string $subject, string $msg, string $xml)
|
||||
{
|
||||
$this->subject = $subject;
|
||||
$this->msg = $msg;
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@ use App\Models\Service;
|
|||
use App\Models\User;
|
||||
use Illuminate\Mail\Mailable;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class ServiceCredentialRenewed extends Mailable
|
||||
{
|
||||
|
||||
/**
|
||||
* The concerned service
|
||||
*
|
||||
|
|
@ -26,6 +28,8 @@ class ServiceCredentialRenewed extends Mailable
|
|||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @param Service $service
|
||||
* @param User $user
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Service $service, User $user)
|
||||
|
|
|
|||
|
|
@ -14,14 +14,26 @@ class Answer extends Model
|
|||
use HasFactory;
|
||||
use IsCrudModel;
|
||||
|
||||
/** @var string */
|
||||
protected $crudLabelColumn = 'answer';
|
||||
|
||||
/** @var string */
|
||||
protected $crudValueColumn = 'id';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'answer'
|
||||
];
|
||||
|
||||
/**
|
||||
* Answer
|
||||
*
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
self::$editableProperties = [
|
||||
|
|
@ -43,6 +55,8 @@ class Answer extends Model
|
|||
parent::__construct($attributes);
|
||||
}
|
||||
|
||||
// * RELATIONS
|
||||
|
||||
/**
|
||||
* Question BelongsTo relation
|
||||
*
|
||||
|
|
|
|||
|
|
@ -16,16 +16,33 @@ class Brand extends Model
|
|||
use HasFactory;
|
||||
use QueryCacheable;
|
||||
|
||||
/** @var string */
|
||||
protected $crudLabelColumn = 'name';
|
||||
|
||||
/** @var string */
|
||||
protected $crudValueColumn = 'id';
|
||||
|
||||
/**
|
||||
* The timeout cache for this model
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $cacheFor = 0;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name'
|
||||
];
|
||||
|
||||
/**
|
||||
* Brand
|
||||
*
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
$this->cacheFor = config('api.servicesDBCache') * 60;
|
||||
|
|
@ -53,7 +70,7 @@ class Brand extends Model
|
|||
* Set the base cache tags that will be present
|
||||
* on all queries.
|
||||
*
|
||||
*! This is used to flush caching
|
||||
* ! This is used to flush caching
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
|
@ -64,22 +81,29 @@ class Brand extends Model
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function () {
|
||||
// flush Eloquent cache
|
||||
// * Flush Eloquent cache.
|
||||
self::flushQueryCache();
|
||||
});
|
||||
static::updated(function () {
|
||||
// flush Eloquent cache
|
||||
// * Flush Eloquent cache.
|
||||
self::flushQueryCache();
|
||||
});
|
||||
static::deleted(function () {
|
||||
// flush Eloquent cache
|
||||
// * Flush Eloquent cache.
|
||||
self::flushQueryCache();
|
||||
});
|
||||
}
|
||||
|
||||
// * RELATIONS
|
||||
|
||||
/**
|
||||
* Service hasMany relation
|
||||
*
|
||||
|
|
@ -130,12 +154,24 @@ class Brand extends Model
|
|||
return $this->hasMany(SingleOptin::class);
|
||||
}
|
||||
|
||||
public function getAccountsAttribute()
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Account attribute accessor
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getAccountsAttribute(): int
|
||||
{
|
||||
return $this->customerInfos()->count();
|
||||
}
|
||||
|
||||
public function getTotalOptinsAttribute()
|
||||
/**
|
||||
* Total optins attribute accessor
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getTotalOptinsAttribute(): int
|
||||
{
|
||||
return $this->customerInfos()->where('opt_in', 1)->count() + $this->singleOptins()->count();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,19 +7,38 @@ use Illuminate\Support\Carbon;
|
|||
|
||||
class BrandStat extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* Indicates if the model should be timestamped.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $casts = [
|
||||
'created_at' => 'date'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'active', 'inactive',
|
||||
'old_accounts', 'new_accounts',
|
||||
'total_accounts', 'total_opt_in'
|
||||
];
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function (BrandStat $stat) {
|
||||
|
|
@ -29,6 +48,13 @@ class BrandStat extends Model
|
|||
});
|
||||
}
|
||||
|
||||
// * RELATIONS
|
||||
|
||||
/**
|
||||
* Brand hasMany relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function brand()
|
||||
{
|
||||
return $this->belongsTo(Brand::class);
|
||||
|
|
|
|||
|
|
@ -16,10 +16,17 @@ class Customer extends Model
|
|||
public const FIRST_OR_NEW = 1;
|
||||
public const FIRST_OR_FAIL = 2;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'email'
|
||||
];
|
||||
|
||||
// * RELATIONS
|
||||
|
||||
/**
|
||||
* CustomerInfo hasMany relation
|
||||
*
|
||||
|
|
@ -30,39 +37,6 @@ class Customer extends Model
|
|||
return $this->hasMany(CustomerInfo::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CustomerInfo from a specific brand
|
||||
* or create a New one if $firstOrNewOrFail is 1
|
||||
* throw model not found exception if firstOrNewOrFail = 2
|
||||
*
|
||||
* @param Brand $brand
|
||||
* @param int $firstOrNewOrFail
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function info(Brand $brand, $firstOrNewOrFail = self::FIRST_OR_NULL): ?CustomerInfo
|
||||
{
|
||||
if (isset($this->relations['infos'])) {
|
||||
/** @var \App\Models\CustomerInfo $info */
|
||||
foreach ($this->relations['infos'] as $info) {
|
||||
if ($info->brand_id === $brand->id) {
|
||||
return $info;
|
||||
}
|
||||
}
|
||||
}
|
||||
$query = CustomerInfo::whereHas('brand', function (Builder $query) use ($brand) {
|
||||
$query->where('id', $brand->id);
|
||||
})->whereHas('customer', function (Builder $query) {
|
||||
$query->where('id', $this->id);
|
||||
});
|
||||
if ($firstOrNewOrFail === self::FIRST_OR_NEW) {
|
||||
return $query->firstOrNew();
|
||||
}
|
||||
if ($firstOrNewOrFail === self::FIRST_OR_FAIL) {
|
||||
return $query->firstOrFail();
|
||||
}
|
||||
return $query->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* CustomerLogin hasMany relation
|
||||
*
|
||||
|
|
@ -73,64 +47,6 @@ class Customer extends Model
|
|||
return $this->hasMany(CustomerLogin::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CustomerLogin from a specific brand
|
||||
* or create a New one if $firstOrNewOrFail is 1
|
||||
* throw model not found exception if firstOrNewOrFail = 2
|
||||
*
|
||||
* @param Brand $brand
|
||||
* @param int $firstOrNewOrFail
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function login(Brand $brand, $firstOrNewOrFail = self::FIRST_OR_NULL): ?CustomerLogin
|
||||
{
|
||||
if (isset($this->relations['logins'])) {
|
||||
/** @var \App\Models\CustomerLogin $login */
|
||||
foreach ($this->relations['logins'] as $login) {
|
||||
if ($login->brand_id === $brand->id) {
|
||||
return $login;
|
||||
}
|
||||
}
|
||||
}
|
||||
$query = CustomerLogin::whereHas('brand', function (Builder $query) use ($brand) {
|
||||
$query->where('id', $brand->id);
|
||||
})->whereHas('customer', function (Builder $query) use ($brand) {
|
||||
$query->where('id', $this->id);
|
||||
});
|
||||
if ($firstOrNewOrFail === self::FIRST_OR_NEW) {
|
||||
return $query->firstOrNew();
|
||||
}
|
||||
if ($firstOrNewOrFail === self::FIRST_OR_FAIL) {
|
||||
return $query->firstOrFail();
|
||||
}
|
||||
return $query->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load login and brand for this customer
|
||||
*
|
||||
* @param Brand $brand
|
||||
* @return self
|
||||
*/
|
||||
public function loadBrandData(Brand $brand, bool $orFail = false): self
|
||||
{
|
||||
$this->load([
|
||||
'logins' => function ($q) use ($brand) {
|
||||
$q->where('brand_id', $brand->id);
|
||||
},
|
||||
'infos' => function ($q) use ($brand) {
|
||||
$q->where('brand_id', $brand->id);
|
||||
}
|
||||
]);
|
||||
if ($orFail and !isset($this->relations['logins'][0])) {
|
||||
throw (new ModelNotFoundException())->setModel(CustomerLogin::class);
|
||||
}
|
||||
if ($orFail and !isset($this->relations['infos'][0])) {
|
||||
throw (new ModelNotFoundException())->setModel(CustomerInfo::class);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* CustomerOptInHistory hasMany relation
|
||||
*
|
||||
|
|
@ -161,13 +77,118 @@ class Customer extends Model
|
|||
return $this->belongsToMany(Answer::class, 'customer_answer');
|
||||
}
|
||||
|
||||
public function setOptIn(Brand $brand, bool $optIn)
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Get the CustomerInfo from a specific brand
|
||||
* or create a New one if $firstOrNewOrFail is 1
|
||||
* throw model not found exception if firstOrNewOrFail = 2
|
||||
*
|
||||
* @param Brand $brand
|
||||
* @param integer $firstOrNewOrFail
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function info(Brand $brand, int $firstOrNewOrFail = self::FIRST_OR_NULL): ?CustomerInfo
|
||||
{
|
||||
// Ajout dans la table d'historique des opt-in de la nouvelle valeur
|
||||
$optInHistory = new CustomerOptInHistory();
|
||||
$optInHistory->customer_id = $this->id;
|
||||
$optInHistory->brand_id = $brand->id;
|
||||
$optInHistory->new_value = $optIn;
|
||||
if (isset($this->relations['infos'])) {
|
||||
/** @var \App\Models\CustomerInfo $info */
|
||||
foreach ($this->relations['infos'] as $info) {
|
||||
if ($info->brand_id === $brand->id) {
|
||||
return $info;
|
||||
}
|
||||
}
|
||||
}
|
||||
$query = CustomerInfo::whereHas('brand', function (Builder $query) use ($brand) {
|
||||
$query->where('id', $brand->id);
|
||||
})->whereHas('customer', function (Builder $query) {
|
||||
$query->where('id', $this->id);
|
||||
});
|
||||
if ($firstOrNewOrFail === self::FIRST_OR_NEW) {
|
||||
return $query->firstOrNew();
|
||||
}
|
||||
if ($firstOrNewOrFail === self::FIRST_OR_FAIL) {
|
||||
return $query->firstOrFail();
|
||||
}
|
||||
return $query->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CustomerLogin from a specific brand
|
||||
* or create a New one if $firstOrNewOrFail is 1
|
||||
* throw model not found exception if firstOrNewOrFail = 2
|
||||
*
|
||||
* @param Brand $brand
|
||||
* @param integer $firstOrNewOrFail
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function login(Brand $brand, int $firstOrNewOrFail = self::FIRST_OR_NULL): ?CustomerLogin
|
||||
{
|
||||
if (isset($this->relations['logins'])) {
|
||||
/** @var \App\Models\CustomerLogin $login */
|
||||
foreach ($this->relations['logins'] as $login) {
|
||||
if ($login->brand_id === $brand->id) {
|
||||
return $login;
|
||||
}
|
||||
}
|
||||
}
|
||||
$query = CustomerLogin::whereHas('brand', function (Builder $query) use ($brand) {
|
||||
$query->where('id', $brand->id);
|
||||
})->whereHas('customer', function (Builder $query) use ($brand) {
|
||||
$query->where('id', $this->id);
|
||||
});
|
||||
if ($firstOrNewOrFail === self::FIRST_OR_NEW) {
|
||||
return $query->firstOrNew();
|
||||
}
|
||||
if ($firstOrNewOrFail === self::FIRST_OR_FAIL) {
|
||||
return $query->firstOrFail();
|
||||
}
|
||||
return $query->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load login and info for this customer and this specific brand.
|
||||
*
|
||||
* @param Brand $brand
|
||||
* @param boolean $orFail
|
||||
* @return self
|
||||
* @throws ModelNotFoundException If no login or info was found for the brand.
|
||||
*/
|
||||
public function loadBrandData(Brand $brand, bool $orFail = false): self
|
||||
{
|
||||
$this->load([
|
||||
'logins' => function ($q) use ($brand) {
|
||||
$q->where('brand_id', $brand->id);
|
||||
},
|
||||
'infos' => function ($q) use ($brand) {
|
||||
$q->where('brand_id', $brand->id);
|
||||
}
|
||||
]);
|
||||
if ($orFail and !isset($this->relations['logins'][0])) {
|
||||
throw (new ModelNotFoundException())->setModel(CustomerLogin::class);
|
||||
}
|
||||
if ($orFail and !isset($this->relations['infos'][0])) {
|
||||
throw (new ModelNotFoundException())->setModel(CustomerInfo::class);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this customer as optin
|
||||
*
|
||||
* @param Brand $brand
|
||||
* @param boolean $optIn
|
||||
* @return void
|
||||
* @throws \Throwable If customer history update failed.
|
||||
*/
|
||||
public function setOptIn(Brand $brand, bool $optIn): void
|
||||
{
|
||||
$this->opt_in = $optIn;
|
||||
|
||||
// * Ajout dans la table d'historique des opt-in de la nouvelle valeur.
|
||||
$optInHistory = new CustomerOptInHistory();
|
||||
$optInHistory->new_value = $optIn;
|
||||
$optInHistory->customer()->associate($this);
|
||||
$optInHistory->brand()->associate($brand);
|
||||
$optInHistory->saveOrFail();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,5 +6,36 @@ use Illuminate\Database\Eloquent\Relations\Pivot;
|
|||
|
||||
class CustomerAnswer extends Pivot
|
||||
{
|
||||
//
|
||||
/**
|
||||
* Inhibit update_at column as we only have
|
||||
* created_at
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getUpdatedAtColumn()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// * RELATIONS
|
||||
|
||||
/**
|
||||
* Customer belongsTo relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function customer()
|
||||
{
|
||||
return $this->belongsTo(Customer::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Customer belongsTo relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function answer()
|
||||
{
|
||||
return $this->belongsTo(Answer::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,14 +13,29 @@ class CustomerInfo extends Model
|
|||
use HasFactory;
|
||||
use CastsEnums;
|
||||
|
||||
/**
|
||||
* Map attribute names to enum classes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $enumCasts = [
|
||||
'newcountry' => Country::class
|
||||
'country' => Country::class
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'opt_in' => 'bool'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'firstname',
|
||||
'lastname',
|
||||
|
|
@ -28,7 +43,6 @@ class CustomerInfo extends Model
|
|||
'address',
|
||||
'address2',
|
||||
'phone',
|
||||
'newcountry',
|
||||
'country',
|
||||
'city',
|
||||
'postcode',
|
||||
|
|
@ -36,64 +50,26 @@ class CustomerInfo extends Model
|
|||
];
|
||||
|
||||
/**
|
||||
*! TODO Try to refactor this but beware this is difficult and breaks unit tests
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
// phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function ($customerInfo) {
|
||||
static::creating(function (CustomerInfo $customerInfo) {
|
||||
$customerInfo->hashcode = Str::uuid();
|
||||
if (ctype_digit($customerInfo->country)) {
|
||||
try {
|
||||
$customerInfo->newcountry = Country::make((int)$customerInfo->country);
|
||||
} catch (\BadMethodCallException $e) {
|
||||
// TODO change this when country column is updated
|
||||
// Set afghanistan as default country for now
|
||||
$customerInfo->newcountry = Country::afghanistan();
|
||||
}
|
||||
}
|
||||
if (\is_object($customerInfo->country) and \get_class($customerInfo->country) === Country::class) {
|
||||
$customerInfo->newcountry = $customerInfo->country;
|
||||
$customerInfo->country = $customerInfo->newcountry->label;
|
||||
}
|
||||
if (!$customerInfo->newcountry) {
|
||||
// TODO change this when country column is updated
|
||||
// Set afghanistan as default country for now
|
||||
$customerInfo->newcountry = Country::afghanistan();
|
||||
}
|
||||
|
||||
if (!$customerInfo->newcountry->equals(Country::afghanistan())) {
|
||||
$customerInfo->country = $customerInfo->newcountry->label;
|
||||
}
|
||||
});
|
||||
static::updating(function ($customerInfo) {
|
||||
if (ctype_digit($customerInfo->country)) {
|
||||
try {
|
||||
$customerInfo->newcountry = Country::make((int)$customerInfo->country);
|
||||
} catch (\BadMethodCallException $e) {
|
||||
// TODO change this when country column is updated
|
||||
// Set afghanistan as default country for now
|
||||
$customerInfo->newcountry = Country::afghanistan();
|
||||
}
|
||||
}
|
||||
if (\is_object($customerInfo->country) and \get_class($customerInfo->country) === Country::class) {
|
||||
$customerInfo->newcountry = $customerInfo->country;
|
||||
$customerInfo->country = $customerInfo->newcountry->label;
|
||||
}
|
||||
if (!$customerInfo->newcountry) {
|
||||
// TODO change this when country column is updated
|
||||
// Set afghanistan as default country for now
|
||||
$customerInfo->newcountry = Country::afghanistan();
|
||||
}
|
||||
|
||||
if (!$customerInfo->newcountry->equals(Country::afghanistan())) {
|
||||
$customerInfo->country = $customerInfo->newcountry->label;
|
||||
}
|
||||
static::deleting(function (CustomerInfo $customerInfo) {
|
||||
CustomerOptInHistory::whereHas('customer', function ($q) use ($customerInfo) {
|
||||
$q->whereId($customerInfo->customer->id);
|
||||
})->whereHas('brand', function ($q) use ($customerInfo) {
|
||||
$q->whereId($customerInfo->brand->id);
|
||||
})->delete();
|
||||
});
|
||||
}
|
||||
|
||||
// * RELATIONS
|
||||
|
||||
/**
|
||||
* Customer BelongsTo relation
|
||||
*
|
||||
|
|
@ -124,10 +100,12 @@ class CustomerInfo extends Model
|
|||
return $this->belongsTo(Brand::class);
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Get customer info from email
|
||||
*
|
||||
* @param Brand $brand
|
||||
* @param Brand $brand
|
||||
* @param string $email
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -10,18 +10,38 @@ class CustomerLogin extends Model
|
|||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The model's attributes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [
|
||||
'status' => false
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'password'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'status' => 'bool'
|
||||
];
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function ($customerLogin) {
|
||||
|
|
@ -36,10 +56,7 @@ class CustomerLogin extends Model
|
|||
});
|
||||
}
|
||||
|
||||
public function passwordMatch(string $password)
|
||||
{
|
||||
return Hash::check($password, $this->password);
|
||||
}
|
||||
// * RELATIONS
|
||||
|
||||
/**
|
||||
* Customer BelongsTo relation
|
||||
|
|
@ -60,4 +77,16 @@ class CustomerLogin extends Model
|
|||
{
|
||||
return $this->belongsTo(Brand::class);
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
/**
|
||||
* Check if the user password is correct
|
||||
*
|
||||
* @param string $password
|
||||
* @return boolean
|
||||
*/
|
||||
public function passwordMatch(string $password): bool
|
||||
{
|
||||
return Hash::check($password, $this->password);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,15 @@ class CustomerOptInHistory extends Model
|
|||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = "customer_opt_in_history";
|
||||
|
||||
// * RELATIONS
|
||||
|
||||
/**
|
||||
* Customer BelongsTo relation
|
||||
*
|
||||
|
|
|
|||
|
|
@ -17,19 +17,36 @@ class Question extends Model
|
|||
use HasFactory;
|
||||
use IsCrudModel;
|
||||
|
||||
/** @var string */
|
||||
protected $crudLabelColumn = 'question';
|
||||
|
||||
/** @var string */
|
||||
protected $crudValueColumn = 'id';
|
||||
|
||||
/**
|
||||
* Map attribute names to enum classes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $enumCasts = [
|
||||
'type' => QuestionType::class
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'question',
|
||||
'type'
|
||||
];
|
||||
|
||||
/**
|
||||
* Question
|
||||
*
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
self::$editableProperties = [
|
||||
|
|
@ -81,6 +98,8 @@ class Question extends Model
|
|||
parent::__construct($attributes);
|
||||
}
|
||||
|
||||
// * RELATIONS
|
||||
|
||||
/**
|
||||
* Brand BelongsTo relation
|
||||
*
|
||||
|
|
|
|||
|
|
@ -3,15 +3,19 @@
|
|||
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\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
|
||||
use Kwaadpepper\CrudPolicies\Enums\CrudType;
|
||||
use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
|
||||
use Rennokki\QueryCache\Traits\QueryCacheable;
|
||||
use RuntimeException;
|
||||
|
||||
class Service extends Model
|
||||
{
|
||||
|
|
@ -19,16 +23,33 @@ class Service extends Model
|
|||
use HasFactory;
|
||||
use QueryCacheable;
|
||||
|
||||
/** @var string */
|
||||
protected $crudLabelColumn = 'name';
|
||||
|
||||
/** @var string */
|
||||
protected $crudValueColumn = 'id';
|
||||
|
||||
/**
|
||||
* The timeout cache for this model
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $cacheFor = 0;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name', 'brand_id'
|
||||
];
|
||||
|
||||
/**
|
||||
* Service
|
||||
*
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
$this->cacheFor = config('api.servicesDBCache') * 60;
|
||||
|
|
@ -67,7 +88,7 @@ class Service extends Model
|
|||
* Set the base cache tags that will be present
|
||||
* on all queries.
|
||||
*
|
||||
*! This is used to flush caching
|
||||
* ! This is used to flush caching
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
|
@ -78,143 +99,32 @@ class Service extends Model
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function (self $service) {
|
||||
$service->generateBearerToken();
|
||||
if (Hash::needsRehash($service->bearer_token)) {
|
||||
$service->bearer_token = Hash::make($service->bearer_token);
|
||||
}
|
||||
if (Hash::needsRehash($service->bearer_pwd)) {
|
||||
$service->bearer_pwd = Hash::make($service->bearer_pwd);
|
||||
}
|
||||
if (Hash::needsRehash($service->api_token)) {
|
||||
$service->api_token = Hash::make($service->api_token);
|
||||
}
|
||||
});
|
||||
static::updating(function (self $service) {
|
||||
if (Hash::needsRehash($service->bearer_token)) {
|
||||
$service->bearer_token = Hash::make($service->bearer_token);
|
||||
}
|
||||
if (Hash::needsRehash($service->bearer_pwd)) {
|
||||
$service->bearer_pwd = Hash::make($service->bearer_pwd);
|
||||
}
|
||||
if (Hash::needsRehash($service->api_token)) {
|
||||
$service->api_token = Hash::make($service->api_token);
|
||||
}
|
||||
$service->generateBearerPassword();
|
||||
});
|
||||
static::created(function () {
|
||||
// flush Eloquent cache
|
||||
// * Flush Eloquent cache.
|
||||
self::flushQueryCache();
|
||||
});
|
||||
static::updated(function () {
|
||||
// flush Eloquent cache
|
||||
// * Flush Eloquent cache.
|
||||
self::flushQueryCache();
|
||||
});
|
||||
static::deleted(function () {
|
||||
// flush Eloquent cache
|
||||
// * Flush Eloquent cache.
|
||||
self::flushQueryCache();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new Api Token
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateApiToken(): string
|
||||
{
|
||||
$this->api_token = hash('sha256', Str::uuid());
|
||||
$this->api_token_date = now();
|
||||
return $this->api_token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates new bearer credentials
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateBearerToken(): string
|
||||
{
|
||||
$this->bearer_token = hash('sha256', Str::uuid());
|
||||
$this->bearer_token_date = now();
|
||||
$this->bearer_pwd = hash('ripemd160', Str::uuid());
|
||||
return $this->bearer_token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment API CRM Stat
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function incrementStat(): void
|
||||
{
|
||||
SetServiceStats::dispatch($this)->afterResponse()
|
||||
->onConnection('database')->onQueue('stats');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Service from apiToken with its brand
|
||||
*
|
||||
* @param string $apiToken
|
||||
* @return \App\Models\Service
|
||||
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
||||
*/
|
||||
public static function getFromApiToken(string $apiToken): self
|
||||
{
|
||||
$services = static::with([
|
||||
'brand' => function ($query) {
|
||||
return $query->cacheTags(["service:author"]);
|
||||
},
|
||||
])->get();
|
||||
/** @var \App\Models\Service $service */
|
||||
foreach ($services as $service) {
|
||||
if ($service->api_token and Hash::check($apiToken, $service->api_token)) {
|
||||
return $service->load([
|
||||
'brand' => function ($query) use ($service) {
|
||||
return $query->cacheTags(["service:{$service->id}:brand"]);
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
throw new ModelNotFoundException('Could not find service from Api Token');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Service from bearerToken with its brand
|
||||
*
|
||||
* @param string $bearerToken
|
||||
* @return \App\Models\Service
|
||||
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
||||
*/
|
||||
public static function getFromBearerToken(string $bearerToken): self
|
||||
{
|
||||
$services = static::all();
|
||||
/** @var \App\Models\Service $service */
|
||||
foreach ($services as $service) {
|
||||
if ($service->bearer_token and Hash::check($bearerToken, $service->bearer_token)) {
|
||||
return $service->load([
|
||||
'brand' => function ($query) use ($service) {
|
||||
return $query->cacheTags(["service:{$service->id}:brand"]);
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
throw new ModelNotFoundException('Could not find service from Service Token');
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate from the api token string
|
||||
*
|
||||
* @param array $guards
|
||||
* @param string $apiToken
|
||||
* @return bool
|
||||
*/
|
||||
public function authenticateApi(string $apiToken): bool
|
||||
{
|
||||
return $this->api_token and Hash::check($apiToken, $this->api_token) and
|
||||
((abs(strtotime($this->api_token_date) - time()) * 60) < (config('api.tokenTimeout') * 60));
|
||||
}
|
||||
// * RELATIONS
|
||||
|
||||
/**
|
||||
* Brand BelongsTo relation
|
||||
|
|
@ -235,4 +145,122 @@ class Service extends Model
|
|||
{
|
||||
return $this->hasMany(ServiceStat::class);
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment API CRM Stat
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function incrementStat(): void
|
||||
{
|
||||
SetServiceStats::dispatch($this)->afterResponse()
|
||||
->onConnection('database')->onQueue('stats');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Service from bearerToken with its brand
|
||||
*
|
||||
* TODO remove this method when dropping V1
|
||||
*
|
||||
* @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) {
|
||||
// * Keep compatibility with V3.
|
||||
$tokenEquals = false;
|
||||
try {
|
||||
$tokenEquals = Hash::check($bearerToken, $service->bearer_token);
|
||||
} catch (RuntimeException $e) {
|
||||
$tokenEquals = $bearerToken === $service->bearer_token;
|
||||
}
|
||||
if ($service->bearer_token and $tokenEquals) {
|
||||
return $service->load([
|
||||
'brand' => function ($query) use ($service) {
|
||||
return $query->cacheTags(["service:{$service->id}:brand"]);
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
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
|
||||
{
|
||||
$calculatedHash = Helper::signRequest(
|
||||
$this->bearer_pwd,
|
||||
$request->getUri(),
|
||||
$request->getMethod(),
|
||||
$request->all()
|
||||
);
|
||||
|
||||
// ! Très important hash_equals https://fr.wikipedia.org/wiki/Attaque_temporelle
|
||||
return \hash_equals($calculatedHash, $request->signature);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue